diff options
152 files changed, 3984 insertions, 3568 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 9e308435c9bc..70cda8b8dd7a 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -71,6 +71,8 @@ aconfig_srcjars = [ ":android.appwidget.flags-aconfig-java{.generated_srcjars}", ":android.webkit.flags-aconfig-java{.generated_srcjars}", ":android.provider.flags-aconfig-java{.generated_srcjars}", + ":android.chre.flags-aconfig-java{.generated_srcjars}", + ":android.speech.flags-aconfig-java{.generated_srcjars}", ] filegroup { @@ -905,3 +907,23 @@ java_aconfig_library { aconfig_declarations: "android.provider.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// ContextHub +java_aconfig_library { + name: "android.chre.flags-aconfig-java", + aconfig_declarations: "chre_flags", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +// Speech +aconfig_declarations { + name: "android.speech.flags-aconfig", + package: "android.speech.flags", + srcs: ["core/java/android/speech/flags/*.aconfig"], +} + +java_aconfig_library { + name: "android.speech.flags-aconfig-java", + aconfig_declarations: "android.speech.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/Android.bp b/Android.bp index f3b2ebb4fc17..c84de511719a 100644 --- a/Android.bp +++ b/Android.bp @@ -175,9 +175,6 @@ java_library { // and remove this line. "//frameworks/base/tools/hoststubgen:__subpackages__", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // AIDL files under these paths are mixture of public and private ones. @@ -270,9 +267,6 @@ java_library { ], sdk_version: "core_platform", installable: false, - lint: { - baseline_filename: "lint-baseline.xml", - }, } // NOTE: This filegroup is exposed for vendor libraries to depend on and is referenced in @@ -441,9 +435,6 @@ java_library { ], sdk_version: "core_platform", installable: false, - lint: { - baseline_filename: "lint-baseline.xml", - }, } // Separated so framework-minus-apex-defaults can be used without the libs dependency @@ -487,9 +478,6 @@ java_library { ], compile_dex: false, headers_only: true, - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -534,7 +522,7 @@ java_library { }, lint: { enabled: false, - baseline_filename: "lint-baseline.xml", + }, } @@ -559,9 +547,6 @@ java_library { ], sdk_version: "core_platform", apex_available: ["//apex_available:platform"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -577,9 +562,6 @@ java_library { "calendar-provider-compat-config", "contacts-provider-platform-compat-config", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } platform_compat_config { @@ -634,9 +616,6 @@ java_library { "rappor", ], dxflags: ["--core-library"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // utility classes statically linked into framework-wifi and dynamically linked diff --git a/core/api/current.txt b/core/api/current.txt index 7c34812464c0..15fcf663d3ab 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4371,7 +4371,7 @@ package android.app { method public final android.media.session.MediaController getMediaController(); method @NonNull public android.view.MenuInflater getMenuInflater(); method @NonNull public android.window.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); - method @Deprecated public final android.app.Activity getParent(); + method public final android.app.Activity getParent(); method @Nullable public android.content.Intent getParentActivityIntent(); method public android.content.SharedPreferences getPreferences(int); method @Nullable public android.net.Uri getReferrer(); @@ -4389,7 +4389,7 @@ package android.app { method public void invalidateOptionsMenu(); method public boolean isActivityTransitionRunning(); method public boolean isChangingConfigurations(); - method @Deprecated public final boolean isChild(); + method public final boolean isChild(); method public boolean isDestroyed(); method public boolean isFinishing(); method public boolean isImmersive(); @@ -41552,6 +41552,8 @@ package android.speech { field public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL"; field public static final String EXTRA_LANGUAGE_PREFERENCE = "android.speech.extra.LANGUAGE_PREFERENCE"; field public static final String EXTRA_LANGUAGE_SWITCH_ALLOWED_LANGUAGES = "android.speech.extra.LANGUAGE_SWITCH_ALLOWED_LANGUAGES"; + field @FlaggedApi("android.speech.flags.multilang_extra_launch") public static final String EXTRA_LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS = "android.speech.extra.LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS"; + field @FlaggedApi("android.speech.flags.multilang_extra_launch") public static final String EXTRA_LANGUAGE_SWITCH_MAX_SWITCHES = "android.speech.extra.LANGUAGE_SWITCH_MAX_SWITCHES"; field public static final String EXTRA_MASK_OFFENSIVE_WORDS = "android.speech.extra.MASK_OFFENSIVE_WORDS"; field public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS"; field public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE = "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE"; @@ -45343,7 +45345,7 @@ package android.telephony { method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid); method public boolean canManageSubscription(android.telephony.SubscriptionInfo); - method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles(); + method @FlaggedApi("com.android.internal.telephony.flags.enforce_subscription_user_filter") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>); method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context); method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c9e12c0b5948..a532cdbe4d7a 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -862,10 +862,6 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR; } - @FlaggedApi("android.app.bic_client") public final class BackgroundInstallControlManager { - method @FlaggedApi("android.app.bic_client") @NonNull @RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES) public java.util.List<android.content.pm.PackageInfo> getBackgroundInstalledPackages(long); - } - public class BroadcastOptions { method public void clearRequireCompatChange(); method public int getPendingIntentBackgroundActivityStartMode(); diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 1e1f1554d3a2..5840f02a8650 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -25,8 +25,6 @@ import android.util.Log; import android.util.Property; import android.view.animation.AccelerateDecelerateInterpolator; -import java.lang.ref.WeakReference; - /** * This subclass of {@link ValueAnimator} provides support for animating properties on target objects. * The constructors of this class take parameters to define the target object that will be animated @@ -73,11 +71,7 @@ public final class ObjectAnimator extends ValueAnimator { private static final boolean DBG = false; - /** - * A weak reference to the target object on which the property exists, set - * in the constructor. We'll cancel the animation if this goes away. - */ - private WeakReference<Object> mTarget; + private Object mTarget; private String mPropertyName; @@ -919,7 +913,7 @@ public final class ObjectAnimator extends ValueAnimator { */ @Nullable public Object getTarget() { - return mTarget == null ? null : mTarget.get(); + return mTarget; } @Override @@ -929,7 +923,7 @@ public final class ObjectAnimator extends ValueAnimator { if (isStarted()) { cancel(); } - mTarget = target == null ? null : new WeakReference<Object>(target); + mTarget = target; // New target should cause re-initialization prior to starting mInitialized = false; } @@ -977,13 +971,6 @@ public final class ObjectAnimator extends ValueAnimator { @Override void animateValue(float fraction) { final Object target = getTarget(); - if (mTarget != null && target == null) { - // We lost the target reference, cancel and clean up. Note: we allow null target if the - /// target has never been set. - cancel(); - return; - } - super.animateValue(fraction); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index be2582f6175d..2103055afe50 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1177,23 +1177,12 @@ public class Activity extends ContextThemeWrapper return mApplication; } - /** - * Whether this is a child {@link Activity} of an {@link ActivityGroup}. - * - * @deprecated {@link ActivityGroup} is deprecated. - */ - @Deprecated + /** Is this activity embedded inside of another activity? */ public final boolean isChild() { return mParent != null; } - /** - * Returns the parent {@link Activity} if this is a child {@link Activity} of an - * {@link ActivityGroup}. - * - * @deprecated {@link ActivityGroup} is deprecated. - */ - @Deprecated + /** Return the parent activity if this view is an embedded child. */ public final Activity getParent() { return mParent; } diff --git a/core/java/android/app/BackgroundInstallControlManager.java b/core/java/android/app/BackgroundInstallControlManager.java deleted file mode 100644 index f5b68788f0ea..000000000000 --- a/core/java/android/app/BackgroundInstallControlManager.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app; - -import static android.Manifest.permission.QUERY_ALL_PACKAGES; -import static android.annotation.SystemApi.Client.PRIVILEGED_APPS; - -import android.annotation.FlaggedApi; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.annotation.SystemService; -import android.content.Context; -import android.content.pm.IBackgroundInstallControlService; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.ServiceManager; - -import java.util.List; - -/** - * BackgroundInstallControlManager client allows apps to query apps installed in background. - * - * <p>Any applications that was installed without an accompanying installer UI activity paired - * with recorded user interaction event is considered background installed. This is determined by - * analysis of user-activity logs. - * - * <p>Warning: BackgroundInstallControl should not be considered a reliable or accurate - * determination of background install application. Consumers can use this as a supplementary - * signal, but must perform additional due diligence to confirm the install nature of the package. - * - * @hide - */ -@FlaggedApi(Flags.FLAG_BIC_CLIENT) -@SystemApi(client = PRIVILEGED_APPS) -@SystemService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE) -public final class BackgroundInstallControlManager { - - private static final String TAG = "BackgroundInstallControlManager"; - private static IBackgroundInstallControlService sService; - private final Context mContext; - - BackgroundInstallControlManager(Context context) { - mContext = context; - } - - private static IBackgroundInstallControlService getService() { - if (sService == null) { - sService = - IBackgroundInstallControlService.Stub.asInterface( - ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); - } - return sService; - } - - /** - * Returns a full list of {@link PackageInfo} of apps currently installed that are considered - * installed in the background. - * - * <p>Refer to top level doc {@link BackgroundInstallControlManager} for more details on - * background-installed applications. - * <p> - * - * @param flags - Flags will be used to call - * {@link PackageManager#getInstalledPackages(PackageInfoFlags)} to retrieve installed packages. - * @return A list of packages retrieved from {@link PackageManager} with non-background - * installed app filter applied. - * - * @hide - */ - @FlaggedApi(Flags.FLAG_BIC_CLIENT) - @SystemApi - @RequiresPermission(QUERY_ALL_PACKAGES) - public @NonNull List<PackageInfo> getBackgroundInstalledPackages( - @PackageManager.PackageInfoFlagsBits long flags) { - try { - return getService() - .getBackgroundInstalledPackages(flags, mContext.getUserId()) - .getList(); - } catch (SecurityException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 390fa2212298..9cf732abb86a 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1603,20 +1603,6 @@ public final class SystemServiceRegistry { } }); - // DO NOT do a flag check like this unless the flag is read-only. - // (because this code is executed during preload in zygote.) - // If the flag is mutable, the check should be inside CachedServiceFetcher. - if (Flags.bicClient()) { - registerService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, - BackgroundInstallControlManager.class, - new CachedServiceFetcher<BackgroundInstallControlManager>() { - @Override - public BackgroundInstallControlManager createService(ContextImpl ctx) { - return new BackgroundInstallControlManager(ctx); - } - }); - } - sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/core/java/android/app/background_install_control_manager.aconfig b/core/java/android/app/background_install_control_manager.aconfig deleted file mode 100644 index 029b93ab4534..000000000000 --- a/core/java/android/app/background_install_control_manager.aconfig +++ /dev/null @@ -1,9 +0,0 @@ -package: "android.app" - -flag { - namespace: "background_install_control" - name: "bic_client" - description: "System API for background install control." - is_fixed_read_only: true - bug: "287507984" -} diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS index 073e2d79850b..497eaf0e40f1 100644 --- a/core/java/android/app/wearable/OWNERS +++ b/core/java/android/app/wearable/OWNERS @@ -1,3 +1,5 @@ charliewang@google.com +hackz@google.com oni@google.com +tomchan@google.com volnov@google.com
\ No newline at end of file diff --git a/core/java/android/content/pm/IBackgroundInstallControlService.aidl b/core/java/android/content/pm/IBackgroundInstallControlService.aidl index 4bc8fe16b249..c8e7caebc821 100644 --- a/core/java/android/content/pm/IBackgroundInstallControlService.aidl +++ b/core/java/android/content/pm/IBackgroundInstallControlService.aidl @@ -16,20 +16,11 @@ package android.content.pm; - import android.content.pm.ParceledListSlice; -import android.os.IRemoteCallback; /** * {@hide} */ interface IBackgroundInstallControlService { - @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest.permission.QUERY_ALL_PACKAGES)") ParceledListSlice getBackgroundInstalledPackages(long flags, int userId); - - @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})") - void registerBackgroundInstallCallback(IRemoteCallback callback); - - @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.QUERY_ALL_PACKAGES, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})") - void unregisterBackgroundInstallCallback(IRemoteCallback callback); -}
\ No newline at end of file +} diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index c727a6034006..561db9c8a8ce 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -102,6 +102,24 @@ public class VcnManager { public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY = "vcn_network_selection_wifi_exit_rssi_threshold"; + /** + * Key for the interval to poll IpSecTransformState for packet loss monitoring + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY = + "vcn_network_selection_poll_ipsec_state_interval_seconds"; + + /** + * Key for the threshold of IPSec packet loss rate + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY = + "vcn_network_selection_ipsec_packet_loss_percent_threshold"; + // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz /** @@ -148,6 +166,8 @@ public class VcnManager { new String[] { VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, + VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index 67a1906d48ed..7afd72195fcb 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -12,4 +12,11 @@ flag { namespace: "vcn" description: "Feature flag for adjustable safe mode timeout" bug: "317406085" +} + +flag{ + name: "network_metric_monitor" + namespace: "vcn" + description: "Feature flag for enabling network metric monitor" + bug: "282996138" }
\ No newline at end of file diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 7d9c0a37a13f..2d657c2813a5 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -445,6 +445,19 @@ public class DreamService extends Service implements Window.Callback { } /** + * Retrieves the current {@link android.app.Activity} associated with the dream. + * This method behaves similarly to calling {@link android.app.Activity#getActivity()}. + * + * @return The current activity, or null if the dream is not associated with an activity + * or not started. + * + * @hide + */ + public Activity getActivity() { + return mActivity; + } + + /** * Inflates a layout resource and set it to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(int)}. * diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 92c516c38dcc..7658af53a7f8 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -42,6 +42,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.os.BadParcelableException; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -1056,7 +1057,7 @@ public abstract class NotificationListenerService extends Service { ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface() .getActiveNotificationsFromListener(mWrapper, keys, trim); return cleanUpNotificationList(parceledList); - } catch (android.os.RemoteException ex) { + } catch (android.os.RemoteException | BadParcelableException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } return null; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 45a0c205a09b..54248be74e04 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -64,6 +64,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.time.Instant; import java.util.Arrays; import java.util.Calendar; import java.util.Date; @@ -233,6 +234,7 @@ public class ZenModeConfig implements Parcelable { private static final String MANUAL_TAG = "manual"; private static final String AUTOMATIC_TAG = "automatic"; + private static final String AUTOMATIC_DELETED_TAG = "deleted"; private static final String RULE_ATT_ID = "ruleId"; private static final String RULE_ATT_ENABLED = "enabled"; @@ -251,6 +253,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_USER_MODIFIED_FIELDS = "userModifiedFields"; private static final String RULE_ATT_ICON = "rule_icon"; private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc"; + private static final String RULE_ATT_DELETION_INSTANT = "deletionInstant"; private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale"; private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY = @@ -292,6 +295,10 @@ public class ZenModeConfig implements Parcelable { @UnsupportedAppUsage public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); + // Note: Map is *pkg|conditionId* (see deletedRuleKey()) -> ZenRule, + // unlike automaticRules (which is id -> rule). + public final ArrayMap<String, ZenRule> deletedRules = new ArrayMap<>(); + @UnsupportedAppUsage public ZenModeConfig() { } @@ -306,15 +313,9 @@ public class ZenModeConfig implements Parcelable { allowMessagesFrom = source.readInt(); user = source.readInt(); manualRule = source.readParcelable(null, ZenRule.class); - final int len = source.readInt(); - if (len > 0) { - final String[] ids = new String[len]; - final ZenRule[] rules = new ZenRule[len]; - source.readStringArray(ids); - source.readTypedArray(rules, ZenRule.CREATOR); - for (int i = 0; i < len; i++) { - automaticRules.put(ids[i], rules[i]); - } + readRulesFromParcel(automaticRules, source); + if (Flags.modesApi()) { + readRulesFromParcel(deletedRules, source); } allowAlarms = source.readInt() == 1; allowMedia = source.readInt() == 1; @@ -328,6 +329,19 @@ public class ZenModeConfig implements Parcelable { } } + private static void readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source) { + final int len = source.readInt(); + if (len > 0) { + final String[] ids = new String[len]; + final ZenRule[] rules = new ZenRule[len]; + source.readStringArray(ids); + source.readTypedArray(rules, ZenRule.CREATOR); + for (int i = 0; i < len; i++) { + ruleMap.put(ids[i], rules[i]); + } + } + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(allowCalls ? 1 : 0); @@ -339,19 +353,9 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(allowMessagesFrom); dest.writeInt(user); dest.writeParcelable(manualRule, 0); - if (!automaticRules.isEmpty()) { - final int len = automaticRules.size(); - final String[] ids = new String[len]; - final ZenRule[] rules = new ZenRule[len]; - for (int i = 0; i < len; i++) { - ids[i] = automaticRules.keyAt(i); - rules[i] = automaticRules.valueAt(i); - } - dest.writeInt(len); - dest.writeStringArray(ids); - dest.writeTypedArray(rules, 0); - } else { - dest.writeInt(0); + writeRulesToParcel(automaticRules, dest); + if (Flags.modesApi()) { + writeRulesToParcel(deletedRules, dest); } dest.writeInt(allowAlarms ? 1 : 0); dest.writeInt(allowMedia ? 1 : 0); @@ -365,6 +369,23 @@ public class ZenModeConfig implements Parcelable { } } + private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest) { + if (!ruleMap.isEmpty()) { + final int len = ruleMap.size(); + final String[] ids = new String[len]; + final ZenRule[] rules = new ZenRule[len]; + for (int i = 0; i < len; i++) { + ids[i] = ruleMap.keyAt(i); + rules[i] = ruleMap.valueAt(i); + } + dest.writeInt(len); + dest.writeStringArray(ids); + dest.writeTypedArray(rules, 0); + } else { + dest.writeInt(0); + } + } + @Override public String toString() { StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[') @@ -389,23 +410,26 @@ public class ZenModeConfig implements Parcelable { } else { sb.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd); } - return sb.append(",\nautomaticRules=").append(rulesToString()) - .append(",\nmanualRule=").append(manualRule) - .append(']').toString(); + sb.append(",\nautomaticRules=").append(rulesToString(automaticRules)) + .append(",\nmanualRule=").append(manualRule); + if (Flags.modesApi()) { + sb.append(",\ndeletedRules=").append(rulesToString(deletedRules)); + } + return sb.append(']').toString(); } - private String rulesToString() { - if (automaticRules.isEmpty()) { + private static String rulesToString(ArrayMap<String, ZenRule> ruleList) { + if (ruleList.isEmpty()) { return "{}"; } - StringBuilder buffer = new StringBuilder(automaticRules.size() * 28); + StringBuilder buffer = new StringBuilder(ruleList.size() * 28); buffer.append("{\n"); - for (int i = 0; i < automaticRules.size(); i++) { + for (int i = 0; i < ruleList.size(); i++) { if (i > 0) { buffer.append(",\n"); } - Object value = automaticRules.valueAt(i); + Object value = ruleList.valueAt(i); buffer.append(value); } buffer.append('}'); @@ -487,7 +511,9 @@ public class ZenModeConfig implements Parcelable { && other.allowConversations == allowConversations && other.allowConversationsFrom == allowConversationsFrom; if (Flags.modesApi()) { - return eq && other.allowPriorityChannels == allowPriorityChannels; + return eq + && Objects.equals(other.deletedRules, deletedRules) + && other.allowPriorityChannels == allowPriorityChannels; } return eq; } @@ -644,12 +670,20 @@ public class ZenModeConfig implements Parcelable { DEFAULT_SUPPRESSED_VISUAL_EFFECTS); } else if (MANUAL_TAG.equals(tag)) { rt.manualRule = readRuleXml(parser); - } else if (AUTOMATIC_TAG.equals(tag)) { + } else if (AUTOMATIC_TAG.equals(tag) + || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) { final String id = parser.getAttributeValue(null, RULE_ATT_ID); final ZenRule automaticRule = readRuleXml(parser); if (id != null && automaticRule != null) { automaticRule.id = id; - rt.automaticRules.put(id, automaticRule); + if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) { + String deletedRuleKey = deletedRuleKey(automaticRule); + if (deletedRuleKey != null) { + rt.deletedRules.put(deletedRuleKey, automaticRule); + } + } else if (AUTOMATIC_TAG.equals(tag)) { + rt.automaticRules.put(id, automaticRule); + } } } else if (STATE_TAG.equals(tag)) { rt.areChannelsBypassingDnd = safeBoolean(parser, @@ -660,13 +694,24 @@ public class ZenModeConfig implements Parcelable { throw new IllegalStateException("Failed to reach END_DOCUMENT"); } + /** Generates the map key used for a {@link ZenRule} in {@link #deletedRules}. */ + @Nullable + public static String deletedRuleKey(ZenRule rule) { + if (rule.pkg != null && rule.conditionId != null) { + return rule.pkg + "|" + rule.conditionId.toString(); + } else { + return null; + } + } + /** * Writes XML of current ZenModeConfig * @param out serializer * @param version uses XML_VERSION if version is null * @throws IOException */ - public void writeXml(TypedXmlSerializer out, Integer version) throws IOException { + public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup) + throws IOException { out.startTag(null, ZEN_TAG); out.attribute(null, ZEN_ATT_VERSION, version == null ? Integer.toString(XML_VERSION) : Integer.toString(version)); @@ -707,6 +752,15 @@ public class ZenModeConfig implements Parcelable { writeRuleXml(automaticRule, out); out.endTag(null, AUTOMATIC_TAG); } + if (Flags.modesApi() && !forBackup) { + for (int i = 0; i < deletedRules.size(); i++) { + final ZenRule deletedRule = deletedRules.valueAt(i); + out.startTag(null, AUTOMATIC_DELETED_TAG); + out.attribute(null, RULE_ATT_ID, deletedRule.id); + writeRuleXml(deletedRule, out); + out.endTag(null, AUTOMATIC_DELETED_TAG); + } + } out.startTag(null, STATE_TAG); out.attributeBoolean(null, STATE_ATT_CHANNELS_BYPASSING_DND, areChannelsBypassingDnd); @@ -752,6 +806,11 @@ public class ZenModeConfig implements Parcelable { rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC); rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN); rt.userModifiedFields = safeInt(parser, RULE_ATT_USER_MODIFIED_FIELDS, 0); + Long deletionInstant = tryParseLong( + parser.getAttributeValue(null, RULE_ATT_DELETION_INSTANT), null); + if (deletionInstant != null) { + rt.deletionInstant = Instant.ofEpochMilli(deletionInstant); + } } return rt; } @@ -799,6 +858,10 @@ public class ZenModeConfig implements Parcelable { } out.attributeInt(null, RULE_ATT_TYPE, rule.type); out.attributeInt(null, RULE_ATT_USER_MODIFIED_FIELDS, rule.userModifiedFields); + if (rule.deletionInstant != null) { + out.attributeLong(null, RULE_ATT_DELETION_INSTANT, + rule.deletionInstant.toEpochMilli()); + } } } @@ -1998,6 +2061,7 @@ public class ZenModeConfig implements Parcelable { public String iconResName; public boolean allowManualInvocation; public int userModifiedFields; + @Nullable public Instant deletionInstant; // Only set on deleted rules. public ZenRule() { } @@ -2031,6 +2095,9 @@ public class ZenModeConfig implements Parcelable { triggerDescription = source.readString(); type = source.readInt(); userModifiedFields = source.readInt(); + if (source.readInt() == 1) { + deletionInstant = Instant.ofEpochMilli(source.readLong()); + } } } @@ -2091,6 +2158,12 @@ public class ZenModeConfig implements Parcelable { dest.writeString(triggerDescription); dest.writeInt(type); dest.writeInt(userModifiedFields); + if (deletionInstant != null) { + dest.writeInt(1); + dest.writeLong(deletionInstant.toEpochMilli()); + } else { + dest.writeInt(0); + } } } @@ -2121,6 +2194,9 @@ public class ZenModeConfig implements Parcelable { .append(",triggerDescription=").append(triggerDescription) .append(",type=").append(type) .append(",userModifiedFields=").append(userModifiedFields); + if (deletionInstant != null) { + sb.append(",deletionInstant=").append(deletionInstant); + } } return sb.append(']').toString(); @@ -2180,7 +2256,8 @@ public class ZenModeConfig implements Parcelable { && Objects.equals(other.iconResName, iconResName) && Objects.equals(other.triggerDescription, triggerDescription) && other.type == type - && other.userModifiedFields == userModifiedFields; + && other.userModifiedFields == userModifiedFields + && Objects.equals(other.deletionInstant, deletionInstant); } return finalEquals; @@ -2192,7 +2269,7 @@ public class ZenModeConfig implements Parcelable { return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, component, configurationActivity, pkg, id, enabler, zenPolicy, zenDeviceEffects, modified, allowManualInvocation, iconResName, - triggerDescription, type, userModifiedFields); + triggerDescription, type, userModifiedFields, deletionInstant); } 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 8902368072bf..91ef11cf1d2d 100644 --- a/core/java/android/service/notification/ZenModeDiff.java +++ b/core/java/android/service/notification/ZenModeDiff.java @@ -30,6 +30,11 @@ import java.util.Set; /** * ZenModeDiff is a utility class meant to encapsulate the diff between ZenModeConfigs and their * subcomponents (automatic and manual ZenRules). + * + * <p>Note that this class is intended to detect <em>meaningful</em> differences, so objects that + * are not identical (as per their {@code equals()} implementation) can still produce an empty diff + * if only "metadata" fields are updated. + * * @hide */ public class ZenModeDiff { @@ -467,7 +472,6 @@ public class ZenModeDiff { public static final String FIELD_ICON_RES = "iconResName"; public static final String FIELD_TRIGGER_DESCRIPTION = "triggerDescription"; public static final String FIELD_TYPE = "type"; - public static final String FIELD_USER_MODIFIED_FIELDS = "userModifiedFields"; // NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule // Special field to track whether this rule became active or inactive @@ -563,10 +567,6 @@ public class ZenModeDiff { if (!Objects.equals(from.iconResName, to.iconResName)) { addField(FIELD_ICON_RES, new FieldDiff<>(from.iconResName, to.iconResName)); } - if (from.userModifiedFields != to.userModifiedFields) { - addField(FIELD_USER_MODIFIED_FIELDS, - new FieldDiff<>(from.userModifiedFields, to.userModifiedFields)); - } } } diff --git a/core/java/android/service/wearable/OWNERS b/core/java/android/service/wearable/OWNERS index 073e2d79850b..eca48b742cef 100644 --- a/core/java/android/service/wearable/OWNERS +++ b/core/java/android/service/wearable/OWNERS @@ -1,3 +1 @@ -charliewang@google.com -oni@google.com -volnov@google.com
\ No newline at end of file +include /core/java/android/app/wearable/OWNERS
\ No newline at end of file diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index 118d0284fb9e..1ca7ac77158c 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -16,6 +16,9 @@ package android.speech; +import static android.speech.flags.Flags.FLAG_MULTILANG_EXTRA_LAUNCH; + +import android.annotation.FlaggedApi; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -653,4 +656,30 @@ public class RecognizerIntent { */ public static final String EXTRA_LANGUAGE_SWITCH_ALLOWED_LANGUAGES = "android.speech.extra.LANGUAGE_SWITCH_ALLOWED_LANGUAGES"; + + /** + * Optional integer to use for {@link #EXTRA_ENABLE_LANGUAGE_SWITCH}. If set, the language + * switch will be deactivated when LANGUAGE_SWITCH_MAX_SWITCHES reached. + * + * <p> Depending on the recognizer implementation, this flag may have no effect. + * + * @see #EXTRA_ENABLE_LANGUAGE_SWITCH + */ + @FlaggedApi(FLAG_MULTILANG_EXTRA_LAUNCH) + public static final String EXTRA_LANGUAGE_SWITCH_MAX_SWITCHES = + "android.speech.extra.LANGUAGE_SWITCH_MAX_SWITCHES"; + + /** + * Optional integer to use for {@link #EXTRA_ENABLE_LANGUAGE_SWITCH}. If set, the language + * switch will only be activated for this value of ms of audio since the START_OF_SPEECH. This + * could provide a more stable recognition result when the language switch is only required in + * the beginning of the session. + * + * <p> Depending on the recognizer implementation, this flag may have no effect. + * + * @see #EXTRA_ENABLE_LANGUAGE_SWITCH + */ + @FlaggedApi(FLAG_MULTILANG_EXTRA_LAUNCH) + public static final String EXTRA_LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS = + "android.speech.extra.LANGUAGE_SWITCH_INITIAL_ACTIVE_DURATION_TIME_MILLIS"; } diff --git a/core/java/android/speech/flags/speech_flags.aconfig b/core/java/android/speech/flags/speech_flags.aconfig new file mode 100644 index 000000000000..fd8012746a27 --- /dev/null +++ b/core/java/android/speech/flags/speech_flags.aconfig @@ -0,0 +1,8 @@ +package: "android.speech.flags" + +flag { + name: "multilang_extra_launch" + namespace: "machine_learning" + description: "Feature flag for adding new extra for multi-lang feature" + bug: "312489931" +} diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig index a74b06a491e8..9f9b7b4b68a9 100644 --- a/core/java/android/view/flags/view_flags.aconfig +++ b/core/java/android/view/flags/view_flags.aconfig @@ -1,6 +1,13 @@ package: "android.view.flags" flag { + name: "enable_surface_native_alloc_registration" + namespace: "toolkit" + description: "Feature flag for registering surfaces with the VM for faster cleanup" + bug: "306193257" +} + +flag { name: "enable_use_measure_cache_during_force_layout" namespace: "toolkit" description: "Enables using the measure cache during a view force layout from the second " diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index bdaad2b68fc2..473b814fc4a7 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -47,6 +47,7 @@ import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -337,7 +338,14 @@ public final class SplashScreenView extends FrameLayout { "SplashScreenView"); ImageView imageView = new ImageView(viewContext); imageView.setBackground(mIconDrawable); - viewHost.setView(imageView, mIconSize, mIconSize); + final int windowFlag = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + final WindowManager.LayoutParams lp = + new WindowManager.LayoutParams(mIconSize, mIconSize, + WindowManager.LayoutParams.TYPE_APPLICATION, windowFlag, + PixelFormat.TRANSPARENT); + viewHost.setView(imageView, lp); SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage(); surfaceView.setChildSurfacePackage(surfacePackage); view.mSurfacePackage = surfacePackage; diff --git a/core/proto/OWNERS b/core/proto/OWNERS index db391f7a8c35..a854e3626e78 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -18,6 +18,7 @@ per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/a per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS per-file android/hardware/sensorprivacy.proto = ntmyren@google.com,evanseverson@google.com per-file background_install_control.proto = wenhaowang@google.com,georgechan@google.com,billylau@google.com +per-file android/content/intent.proto = file:/PACKAGE_MANAGER_OWNERS # Biometrics jaggies@google.com @@ -31,5 +32,3 @@ jreck@google.com # Accessibility pweaver@google.com -hongmingjin@google.com -cbrower@google.com diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml index af305329da1a..31acd9af164c 100644 --- a/core/res/res/values-watch/config.xml +++ b/core/res/res/values-watch/config.xml @@ -90,4 +90,7 @@ {@link MotionEvent#AXIS_SCROLL} generated by {@link InputDevice#SOURCE_ROTARY_ENCODER} devices. --> <bool name="config_viewRotaryEncoderHapticScrollFedbackEnabled">true</bool> + + <!-- If this is true, allow wake from theater mode from motion. --> + <bool name="config_allowTheaterModeWakeFromMotion">true</bool> </resources> diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 5ad144d50b87..45540e0fbbb8 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -175,3 +175,74 @@ android_library { plugins: ["dagger2-compiler"], use_resource_processor: true, } + +android_app { + name: "WindowManagerShellRobolectric", + platform_apis: true, + static_libs: [ + "WindowManager-Shell", + ], + manifest: "multivalentTests/AndroidManifestRobolectric.xml", + use_resource_processor: true, +} + +android_robolectric_test { + name: "WMShellRobolectricTests", + instrumentation_for: "WindowManagerShellRobolectric", + upstream: true, + java_resource_dirs: [ + "multivalentTests/robolectric/config", + ], + srcs: [ + "multivalentTests/src/**/*.kt", + ], + static_libs: [ + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-robolectric-prebuilt", + "mockito-kotlin2", + "truth", + ], +} + +android_test { + name: "WMShellMultivalentTestsOnDevice", + srcs: [ + "multivalentTests/src/**/*.kt", + ], + static_libs: [ + "WindowManager-Shell", + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "frameworks-base-testutils", + "mockito-kotlin2", + "mockito-target-extended-minus-junit4", + "truth", + "platform-test-annotations", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + kotlincflags: ["-Xjvm-default=all"], + optimize: { + enabled: false, + }, + test_suites: ["device-tests"], + platform_apis: true, + certificate: "platform", + aaptflags: [ + "--extra-packages", + "com.android.wm.shell", + ], + manifest: "multivalentTests/AndroidManifest.xml", +} diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml new file mode 100644 index 000000000000..f8f8338e5f04 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml @@ -0,0 +1,13 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.multivalenttests"> + + <application android:debuggable="true" android:supportsRtl="true" > + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:label="Multivalent tests for WindowManager-Shell" + android:targetPackage="com.android.wm.shell.multivalenttests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml new file mode 100644 index 000000000000..ffcd7d46fbae --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml @@ -0,0 +1,3 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.multivalenttests"> +</manifest> diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml new file mode 100644 index 000000000000..36fe8ec3370d --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Tests for WindowManagerShellLib"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="WMShellMultivalentTestsOnDevice.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="WMShellMultivalentTestsOnDevice" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.wm.shell.multivalenttests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties new file mode 100644 index 000000000000..7a0527ccaafb --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties @@ -0,0 +1,2 @@ +sdk=NEWEST_SDK + diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt new file mode 100644 index 000000000000..ea7c6edd4b56 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.bubbles + +import android.content.Context +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.graphics.Insets +import android.graphics.PointF +import android.graphics.Rect +import android.os.UserHandle +import android.view.WindowManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.wm.shell.R +import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests operations and the resulting state managed by [BubblePositioner]. */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class BubblePositionerTest { + + private lateinit var positioner: BubblePositioner + private val context = ApplicationProvider.getApplicationContext<Context>() + private val defaultDeviceConfig = + DeviceConfig( + windowBounds = Rect(0, 0, 1000, 2000), + isLargeScreen = false, + isSmallTablet = false, + isLandscape = false, + isRtl = false, + insets = Insets.of(0, 0, 0, 0) + ) + + @Before + fun setUp() { + val windowManager = context.getSystemService(WindowManager::class.java) + positioner = BubblePositioner(context, windowManager) + } + + @Test + fun testUpdate() { + val insets = Insets.of(10, 20, 5, 15) + val screenBounds = Rect(0, 0, 1000, 1200) + val availableRect = Rect(screenBounds) + availableRect.inset(insets) + positioner.update(defaultDeviceConfig.copy(insets = insets, windowBounds = screenBounds)) + assertThat(positioner.availableRect).isEqualTo(availableRect) + assertThat(positioner.isLandscape).isFalse() + assertThat(positioner.isLargeScreen).isFalse() + assertThat(positioner.insets).isEqualTo(insets) + } + + @Test + fun testShowBubblesVertically_phonePortrait() { + positioner.update(defaultDeviceConfig) + assertThat(positioner.showBubblesVertically()).isFalse() + } + + @Test + fun testShowBubblesVertically_phoneLandscape() { + positioner.update(defaultDeviceConfig.copy(isLandscape = true)) + assertThat(positioner.isLandscape).isTrue() + assertThat(positioner.showBubblesVertically()).isTrue() + } + + @Test + fun testShowBubblesVertically_tablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + assertThat(positioner.showBubblesVertically()).isTrue() + } + + /** If a resting position hasn't been set, calling it will return the default position. */ + @Test + fun testGetRestingPosition_returnsDefaultPosition() { + positioner.update(defaultDeviceConfig) + val restingPosition = positioner.getRestingPosition() + val defaultPosition = positioner.defaultStartPosition + assertThat(restingPosition).isEqualTo(defaultPosition) + } + + /** If a resting position has been set, it'll return that instead of the default position. */ + @Test + fun testGetRestingPosition_returnsRestingPosition() { + positioner.update(defaultDeviceConfig) + val restingPosition = PointF(100f, 100f) + positioner.restingPosition = restingPosition + assertThat(positioner.getRestingPosition()).isEqualTo(restingPosition) + } + + /** Test that the default resting position on phone is in upper left. */ + @Test + fun testGetRestingPosition_bubble_onPhone() { + positioner.update(defaultDeviceConfig) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_bubble_onPhone_RTL() { + positioner.update(defaultDeviceConfig.copy(isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + /** Test that the default resting position on tablet is middle left. */ + @Test + fun testGetRestingPosition_chatBubble_onTablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_chatBubble_onTablet_RTL() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + /** Test that the default resting position on tablet is middle right. */ + @Test + fun testGetDefaultPosition_appBubble_onTablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */) + assertThat(startPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(startPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_appBubble_onTablet_RTL() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */) + assertThat(startPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(startPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testHasUserModifiedDefaultPosition_false() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + positioner.restingPosition = positioner.defaultStartPosition + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + } + + @Test + fun testHasUserModifiedDefaultPosition_true() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + positioner.restingPosition = PointF(0f, 100f) + assertThat(positioner.hasUserModifiedDefaultPosition()).isTrue() + } + + @Test + fun testGetExpandedViewHeight_max() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT) + } + + @Test + fun testGetExpandedViewHeight_customHeight_valid() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + val minHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height) + val bubble = + Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + minHeight + 100 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor()) {} + + // Ensure the height is the same as the desired value + assertThat(positioner.getExpandedViewHeight(bubble)) + .isEqualTo(bubble.getDesiredHeight(context)) + } + + @Test + fun testGetExpandedViewHeight_customHeight_tooSmall() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val bubble = + Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + 10 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor()) {} + + // Ensure the height is the same as the desired value + val minHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height) + assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight) + } + + @Test + fun testGetMaxExpandedViewHeight_onLargeTablet() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val manageButtonHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height) + val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width) + val expandedViewPadding = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding) + val expectedHeight = + 1800 - 2 * 20 - manageButtonHeight - pointerWidth - expandedViewPadding * 2 + assertThat(positioner.getMaxExpandedViewHeight(false /* isOverflow */)) + .isEqualTo(expectedHeight) + } + + @Test + fun testAreBubblesBottomAligned_largeScreen_true() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isTrue() + } + + @Test + fun testAreBubblesBottomAligned_largeScreen_landscape_false() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testAreBubblesBottomAligned_smallTablet_false() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isSmallTablet = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testAreBubblesBottomAligned_phone_false() { + val deviceConfig = + defaultDeviceConfig.copy( + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testExpandedViewY_phoneLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height so it'll always be top aligned + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_phonePortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // Always top aligned in phone portrait + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_smallTabletLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isSmallTablet = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on small tablets + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_smallTabletPortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + isSmallTablet = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on small tablets + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_largeScreenLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on landscape, large tablet + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_largeScreenPortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + val manageButtonHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height) + val manageButtonPlusMargin = + manageButtonHeight + + 2 * context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_margin) + val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width) + + val expectedExpandedViewY = + positioner.availableRect.bottom - + manageButtonPlusMargin - + positioner.getExpandedViewHeightForLargeScreen() - + pointerWidth + + // Bubbles are bottom aligned on portrait, large tablet + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(expectedExpandedViewY) + } + + private val defaultYPosition: Float + /** + * Calculates the Y position bubbles should be placed based on the config. Based on the + * calculations in [BubblePositioner.getDefaultStartPosition] and + * [BubbleStackView.RelativeStackPosition]. + */ + get() { + val isTablet = positioner.isLargeScreen + + // On tablet the position is centered, on phone it is an offset from the top. + val desiredY = + if (isTablet) { + positioner.screenRect.height() / 2f - positioner.bubbleSize / 2f + } else { + context.resources + .getDimensionPixelOffset(R.dimen.bubble_stack_starting_offset_y) + .toFloat() + } + // Since we're visually centering the bubbles on tablet, use total screen height rather + // than the available height. + val height = + if (isTablet) { + positioner.screenRect.height() + } else { + positioner.availableRect.height() + } + val offsetPercent = (desiredY / height).coerceIn(0f, 1f) + val allowableStackRegion = + positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent + } +} diff --git a/libs/WindowManager/Shell/multivalentTestsForDevice b/libs/WindowManager/Shell/multivalentTestsForDevice new file mode 120000 index 000000000000..20ee34ada103 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTestsForDevice @@ -0,0 +1 @@ +multivalentTests
\ No newline at end of file diff --git a/libs/WindowManager/Shell/multivalentTestsForDeviceless b/libs/WindowManager/Shell/multivalentTestsForDeviceless new file mode 120000 index 000000000000..20ee34ada103 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTestsForDeviceless @@ -0,0 +1 @@ +multivalentTests
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 81d963877e4c..bb433dbbd2ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -176,6 +176,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private StatusBarCustomizer mCustomizer; private boolean mTrackingLatency; + // Keep previous navigation type before remove mBackNavigationInfo. + @BackNavigationInfo.BackTargetType + private int mPreviousNavigationType; + public BackAnimationController( @NonNull ShellInit shellInit, @NonNull ShellController shellController, @@ -871,6 +875,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellBackAnimationRegistry.resetDefaultCrossActivity(); cancelLatencyTracking(); if (mBackNavigationInfo != null) { + mPreviousNavigationType = mBackNavigationInfo.getType(); mBackNavigationInfo.onBackNavigationFinished(triggerBack); mBackNavigationInfo = null; } @@ -983,7 +988,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellExecutor.execute( () -> { if (!mShellBackAnimationRegistry.cancel( - mBackNavigationInfo.getType())) { + mBackNavigationInfo != null + ? mBackNavigationInfo.getType() + : mPreviousNavigationType)) { return; } if (!mBackGestureStarted) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 8c861c63a70d..bf783e6af36f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -197,60 +197,61 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { - return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, - finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler); - } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - return animateEnterPipFromActivityEmbedding( - info, startTransaction, finishTransaction, finishCallback); - } else if (mType == TYPE_DISPLAY_AND_SPLIT_CHANGE) { - return false; - } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - final boolean handledToPip = animateOpenIntentWithRemoteAndPip( - info, startTransaction, finishTransaction, finishCallback); - // Consume the transition on remote handler if the leftover handler already handle - // this transition. And if it cannot, the transition will be handled by remote - // handler, so don't consume here. - // Need to check leftOverHandler as it may change in - // #animateOpenIntentWithRemoteAndPip - if (handledToPip && mHasRequestToRemote - && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { - mPlayer.getRemoteTransitionHandler().onTransitionConsumed( - transition, false, null); - } - return handledToPip; - } else if (mType == TYPE_RECENTS_DURING_SPLIT) { - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - // Pip auto-entering info might be appended to recent transition like pressing - // home-key in 3-button navigation. This offers split handler the opportunity to - // handle split to pip animation. - if (mPipHandler.isEnteringPip(change, info.getType()) - && mSplitHandler.getSplitItemPosition(change.getLastParent()) - != SPLIT_POSITION_UNDEFINED) { - return animateEnterPipFromSplit( - this, info, startTransaction, finishTransaction, finishCallback, - mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + switch (mType) { + case TYPE_ENTER_PIP_FROM_SPLIT: + return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, + finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: + return animateEnterPipFromActivityEmbedding( + info, startTransaction, finishTransaction, finishCallback); + case TYPE_DISPLAY_AND_SPLIT_CHANGE: + return false; + case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + final boolean handledToPip = animateOpenIntentWithRemoteAndPip( + info, startTransaction, finishTransaction, finishCallback); + // Consume the transition on remote handler if the leftover handler already + // handle this transition. And if it cannot, the transition will be handled by + // remote handler, so don't consume here. + // Need to check leftOverHandler as it may change in + // #animateOpenIntentWithRemoteAndPip + if (handledToPip && mHasRequestToRemote + && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { + mPlayer.getRemoteTransitionHandler().onTransitionConsumed( + transition, false, null); + } + return handledToPip; + case TYPE_RECENTS_DURING_SPLIT: + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + // Pip auto-entering info might be appended to recent transition like + // pressing home-key in 3-button navigation. This offers split handler the + // opportunity to handle split to pip animation. + if (mPipHandler.isEnteringPip(change, info.getType()) + && mSplitHandler.getSplitItemPosition(change.getLastParent()) + != SPLIT_POSITION_UNDEFINED) { + return animateEnterPipFromSplit( + this, info, startTransaction, finishTransaction, finishCallback, + mPlayer, mMixedHandler, mPipHandler, mSplitHandler); + } } - } - return animateRecentsDuringSplit( - info, startTransaction, finishTransaction, finishCallback); - } else if (mType == TYPE_KEYGUARD) { - return animateKeyguard(this, info, startTransaction, finishTransaction, - finishCallback, mKeyguardHandler, mPipHandler); - } else if (mType == TYPE_RECENTS_DURING_KEYGUARD) { - return animateRecentsDuringKeyguard( - info, startTransaction, finishTransaction, finishCallback); - } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { - return animateRecentsDuringDesktop( - info, startTransaction, finishTransaction, finishCallback); - } else if (mType == TYPE_UNFOLD) { - return animateUnfold( - info, startTransaction, finishTransaction, finishCallback); - } else { - throw new IllegalStateException( - "Starting mixed animation without a known mixed type? " + mType); + return animateRecentsDuringSplit( + info, startTransaction, finishTransaction, finishCallback); + case TYPE_KEYGUARD: + return animateKeyguard(this, info, startTransaction, finishTransaction, + finishCallback, mKeyguardHandler, mPipHandler); + case TYPE_RECENTS_DURING_KEYGUARD: + return animateRecentsDuringKeyguard( + info, startTransaction, finishTransaction, finishCallback); + case TYPE_RECENTS_DURING_DESKTOP: + return animateRecentsDuringDesktop( + info, startTransaction, finishTransaction, finishCallback); + case TYPE_UNFOLD: + return animateUnfold( + info, startTransaction, finishTransaction, finishCallback); + default: + throw new IllegalStateException( + "Starting mixed animation without a known mixed type? " + mType); } } @@ -457,79 +458,100 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (mType == TYPE_DISPLAY_AND_SPLIT_CHANGE) { - // queue since no actual animation. - } else if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { - if (mAnimType == ANIM_TYPE_GOING_HOME) { - boolean ended = mSplitHandler.end(); - // If split couldn't end (because it is remote), then don't end everything else - // since we have to play out the animation anyways. - if (!ended) return; - mPipHandler.end(); - if (mLeftoversHandler != null) { - mLeftoversHandler.mergeAnimation( - transition, info, t, mergeTarget, finishCallback); + switch (mType) { + case TYPE_DISPLAY_AND_SPLIT_CHANGE: + // queue since no actual animation. + break; + case TYPE_ENTER_PIP_FROM_SPLIT: + if (mAnimType == ANIM_TYPE_GOING_HOME) { + boolean ended = mSplitHandler.end(); + // If split couldn't end (because it is remote), then don't end everything + // else since we have to play out the animation anyways. + if (!ended) return; + mPipHandler.end(); + if (mLeftoversHandler != null) { + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + } + } else { + mPipHandler.end(); } - } else { + break; + case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: mPipHandler.end(); - } - } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - mPipHandler.end(); - mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, - finishCallback); - } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - mPipHandler.end(); - if (mLeftoversHandler != null) { - mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, + mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } - } else if (mType == TYPE_RECENTS_DURING_SPLIT) { - if (mSplitHandler.isPendingEnter(transition)) { - // Recents -> enter-split means that we are switching from one pair to - // another pair. - mAnimType = ANIM_TYPE_PAIR_TO_PAIR; - } - mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mType == TYPE_KEYGUARD) { - mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mType == TYPE_RECENTS_DURING_KEYGUARD) { - if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { - DefaultMixedHandler.handoverTransitionLeashes(mInfo, info, t, mFinishT); - if (animateKeyguard( - this, info, t, mFinishT, mFinishCB, mKeyguardHandler, mPipHandler)) { - finishCallback.onTransitionFinished(null); + break; + case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + mPipHandler.end(); + if (mLeftoversHandler != null) { + mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); } - } - mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { - mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else if (mType == TYPE_UNFOLD) { - mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); - } else { - throw new IllegalStateException( - "Playing a mixed transition with unknown type? " + mType); + break; + case TYPE_RECENTS_DURING_SPLIT: + if (mSplitHandler.isPendingEnter(transition)) { + // Recents -> enter-split means that we are switching from one pair to + // another pair. + mAnimType = ANIM_TYPE_PAIR_TO_PAIR; + } + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + break; + case TYPE_KEYGUARD: + mKeyguardHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + break; + case TYPE_RECENTS_DURING_KEYGUARD: + if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { + DefaultMixedHandler.handoverTransitionLeashes(mInfo, info, t, mFinishT); + if (animateKeyguard(this, info, t, mFinishT, mFinishCB, mKeyguardHandler, + mPipHandler)) { + finishCallback.onTransitionFinished(null); + } + } + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + break; + case TYPE_RECENTS_DURING_DESKTOP: + mLeftoversHandler.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + break; + case TYPE_UNFOLD: + mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + break; + default: + throw new IllegalStateException( + "Playing a mixed transition with unknown type? " + mType); } } void onTransitionConsumed( @NonNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishT) { - if (mType == TYPE_ENTER_PIP_FROM_SPLIT) { - mPipHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) { - mPipHandler.onTransitionConsumed(transition, aborted, finishT); - mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_RECENTS_DURING_SPLIT) { - mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { - mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_KEYGUARD) { - mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_RECENTS_DURING_DESKTOP) { - mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); - } else if (mType == TYPE_UNFOLD) { - mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); + switch (mType) { + case TYPE_ENTER_PIP_FROM_SPLIT: + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + break; + case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: + mPipHandler.onTransitionConsumed(transition, aborted, finishT); + mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT); + break; + case TYPE_RECENTS_DURING_SPLIT: + case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + case TYPE_RECENTS_DURING_DESKTOP: + mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); + break; + case TYPE_KEYGUARD: + mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); + break; + case TYPE_UNFOLD: + mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); + break; + default: + break; } + if (mHasRequestToRemote) { mPlayer.getRemoteTransitionHandler().onTransitionConsumed( transition, aborted, finishT); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java deleted file mode 100644 index 6ebee730756e..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java +++ /dev/null @@ -1,602 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.bubbles; - -import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; - -import static org.mockito.Mockito.mock; - -import android.content.Intent; -import android.content.pm.ShortcutInfo; -import android.graphics.Insets; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.os.UserHandle; -import android.testing.AndroidTestingRunner; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests operations and the resulting state managed by {@link BubblePositioner}. - */ -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class BubblePositionerTest extends ShellTestCase { - - private BubblePositioner mPositioner; - - @Before - public void setUp() { - WindowManager windowManager = mContext.getSystemService(WindowManager.class); - mPositioner = new BubblePositioner(mContext, windowManager); - } - - @Test - public void testUpdate() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1000, 1200); - Rect availableRect = new Rect(screenBounds); - availableRect.inset(insets); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.getAvailableRect()).isEqualTo(availableRect); - assertThat(mPositioner.isLandscape()).isFalse(); - assertThat(mPositioner.isLargeScreen()).isFalse(); - assertThat(mPositioner.getInsets()).isEqualTo(insets); - } - - @Test - public void testShowBubblesVertically_phonePortrait() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.showBubblesVertically()).isFalse(); - } - - @Test - public void testShowBubblesVertically_phoneLandscape() { - DeviceConfig deviceConfig = new ConfigBuilder().setLandscape().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.isLandscape()).isTrue(); - assertThat(mPositioner.showBubblesVertically()).isTrue(); - } - - @Test - public void testShowBubblesVertically_tablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.showBubblesVertically()).isTrue(); - } - - /** If a resting position hasn't been set, calling it will return the default position. */ - @Test - public void testGetRestingPosition_returnsDefaultPosition() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - PointF restingPosition = mPositioner.getRestingPosition(); - PointF defaultPosition = mPositioner.getDefaultStartPosition(); - - assertThat(restingPosition).isEqualTo(defaultPosition); - } - - /** If a resting position has been set, it'll return that instead of the default position. */ - @Test - public void testGetRestingPosition_returnsRestingPosition() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - PointF restingPosition = new PointF(100, 100); - mPositioner.setRestingPosition(restingPosition); - - assertThat(mPositioner.getRestingPosition()).isEqualTo(restingPosition); - } - - /** Test that the default resting position on phone is in upper left. */ - @Test - public void testGetRestingPosition_bubble_onPhone() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_bubble_onPhone_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - /** Test that the default resting position on tablet is middle left. */ - @Test - public void testGetRestingPosition_chatBubble_onTablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_chatBubble_onTablet_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - /** Test that the default resting position on tablet is middle right. */ - @Test - public void testGetDefaultPosition_appBubble_onTablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */); - - assertThat(startPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(startPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_appBubble_onTablet_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */); - - assertThat(startPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(startPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testHasUserModifiedDefaultPosition_false() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - - mPositioner.setRestingPosition(mPositioner.getDefaultStartPosition()); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - } - - @Test - public void testHasUserModifiedDefaultPosition_true() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - - mPositioner.setRestingPosition(new PointF(0, 100)); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue(); - } - - @Test - public void testGetExpandedViewHeight_max() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT); - } - - @Test - public void testGetExpandedViewHeight_customHeight_valid() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - final int minHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_expanded_default_height); - Bubble bubble = new Bubble("key", - mock(ShortcutInfo.class), - minHeight + 100 /* desiredHeight */, - 0 /* desiredHeightResId */, - "title", - 0 /* taskId */, - null /* locus */, - true /* isDismissable */, - directExecutor(), - mock(Bubbles.BubbleMetadataFlagListener.class)); - - // Ensure the height is the same as the desired value - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo( - bubble.getDesiredHeight(mContext)); - } - - - @Test - public void testGetExpandedViewHeight_customHeight_tooSmall() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Bubble bubble = new Bubble("key", - mock(ShortcutInfo.class), - 10 /* desiredHeight */, - 0 /* desiredHeightResId */, - "title", - 0 /* taskId */, - null /* locus */, - true /* isDismissable */, - directExecutor(), - mock(Bubbles.BubbleMetadataFlagListener.class)); - - // Ensure the height is the same as the minimum value - final int minHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_expanded_default_height); - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight); - } - - @Test - public void testGetMaxExpandedViewHeight_onLargeTablet() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - int manageButtonHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); - int pointerWidth = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_pointer_width); - int expandedViewPadding = mContext.getResources().getDimensionPixelSize(R - .dimen.bubble_expanded_view_padding); - float expectedHeight = 1800 - 2 * 20 - manageButtonHeight - pointerWidth - - expandedViewPadding * 2; - assertThat(((float) mPositioner.getMaxExpandedViewHeight(false /* isOverflow */))) - .isWithin(0.1f).of(expectedHeight); - } - - @Test - public void testAreBubblesBottomAligned_largeScreen_true() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isTrue(); - } - - @Test - public void testAreBubblesBottomAligned_largeScreen_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testAreBubblesBottomAligned_smallTablet_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setSmallTablet() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testAreBubblesBottomAligned_phone_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testExpandedViewY_phoneLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height so it'll always be top aligned - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_phonePortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // Always top aligned in phone portrait - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_smallTabletLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setSmallTablet() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on small tablets - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_smallTabletPortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setSmallTablet() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on small tablets - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_largeScreenLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on landscape, large tablet - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_largeScreenPortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - int manageButtonHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); - int manageButtonPlusMargin = manageButtonHeight + 2 - * mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_manage_button_margin); - int pointerWidth = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_pointer_width); - - final float expectedExpandedViewY = mPositioner.getAvailableRect().bottom - - manageButtonPlusMargin - - mPositioner.getExpandedViewHeightForLargeScreen() - - pointerWidth; - - // Bubbles are bottom aligned on portrait, large tablet - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(expectedExpandedViewY); - } - - /** - * Calculates the Y position bubbles should be placed based on the config. Based on - * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and - * {@link BubbleStackView.RelativeStackPosition}. - */ - private float getDefaultYPosition() { - final boolean isTablet = mPositioner.isLargeScreen(); - - // On tablet the position is centered, on phone it is an offset from the top. - final float desiredY = isTablet - ? mPositioner.getScreenRect().height() / 2f - (mPositioner.getBubbleSize() / 2f) - : mContext.getResources().getDimensionPixelOffset( - R.dimen.bubble_stack_starting_offset_y); - // Since we're visually centering the bubbles on tablet, use total screen height rather - // than the available height. - final float height = isTablet - ? mPositioner.getScreenRect().height() - : mPositioner.getAvailableRect().height(); - float offsetPercent = desiredY / height; - offsetPercent = Math.max(0f, Math.min(1f, offsetPercent)); - final RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent; - } - - /** - * Sets up window manager to return config values based on what you need for the test. - * By default it sets up a portrait phone without any insets. - */ - private static class ConfigBuilder { - private Rect mScreenBounds = new Rect(0, 0, 1000, 2000); - private boolean mIsLargeScreen = false; - private boolean mIsSmallTablet = false; - private boolean mIsLandscape = false; - private boolean mIsRtl = false; - private Insets mInsets = Insets.of(0, 0, 0, 0); - - public ConfigBuilder setScreenBounds(Rect screenBounds) { - mScreenBounds = screenBounds; - return this; - } - - public ConfigBuilder setLargeScreen() { - mIsLargeScreen = true; - return this; - } - - public ConfigBuilder setSmallTablet() { - mIsSmallTablet = true; - return this; - } - - public ConfigBuilder setLandscape() { - mIsLandscape = true; - return this; - } - - public ConfigBuilder setRtl() { - mIsRtl = true; - return this; - } - - public ConfigBuilder setInsets(Insets insets) { - mInsets = insets; - return this; - } - - private DeviceConfig build() { - return new DeviceConfig(mIsLargeScreen, mIsSmallTablet, mIsLandscape, mIsRtl, - mScreenBounds, mInsets); - } - } -} diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 6c3172a26751..d58c872dbc56 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -56,6 +56,7 @@ std::optional<std::int32_t> render_ahead() { bool Properties::debugLayersUpdates = false; bool Properties::debugOverdraw = false; +bool Properties::debugTraceGpuResourceCategories = false; bool Properties::showDirtyRegions = false; bool Properties::skipEmptyFrames = true; bool Properties::useBufferAge = true; @@ -151,10 +152,12 @@ bool Properties::load() { skpCaptureEnabled = debuggingEnabled && base::GetBoolProperty(PROPERTY_CAPTURE_SKP_ENABLED, false); - SkAndroidFrameworkTraceUtil::setEnableTracing( - base::GetBoolProperty(PROPERTY_SKIA_TRACING_ENABLED, false)); + bool skiaBroadTracing = base::GetBoolProperty(PROPERTY_SKIA_TRACING_ENABLED, false); + SkAndroidFrameworkTraceUtil::setEnableTracing(skiaBroadTracing); SkAndroidFrameworkTraceUtil::setUsePerfettoTrackEvents( base::GetBoolProperty(PROPERTY_SKIA_USE_PERFETTO_TRACK_EVENTS, false)); + debugTraceGpuResourceCategories = + base::GetBoolProperty(PROPERTY_TRACE_GPU_RESOURCES, skiaBroadTracing); runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index bca57e9e4678..b956facf6f90 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -143,6 +143,15 @@ enum DebugLevel { #define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled" /** + * Might split Skia's GPU resource utilization into separate tracing tracks (slow). + * + * Aggregate total and purgeable numbers will still be reported under a "misc" track when this is + * disabled, they just won't be split into distinct categories. Results may vary depending on GPU + * backend/API, and the category mappings defined in ATraceMemoryDump's hardcoded sResourceMap. + */ +#define PROPERTY_TRACE_GPU_RESOURCES "debug.hwui.trace_gpu_resources" + +/** * Allows broad recording of Skia drawing commands. * * If disabled, a very minimal set of trace events *may* be recorded. @@ -254,6 +263,7 @@ public: static bool debugLayersUpdates; static bool debugOverdraw; + static bool debugTraceGpuResourceCategories; static bool showDirtyRegions; // TODO: Remove after stabilization period static bool skipEmptyFrames; diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp index 234f42d79cb7..756b937f7de3 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -20,6 +20,8 @@ #include <cstring> +#include "GrDirectContext.h" + namespace android { namespace uirenderer { namespace skiapipeline { @@ -114,8 +116,16 @@ void ATraceMemoryDump::startFrame() { /** * logTraces reads from mCurrentValues and logs the counters with ATRACE. + * + * gpuMemoryIsAlreadyInDump must be true if GrDirectContext::dumpMemoryStatistics(...) was called + * with this tracer, false otherwise. Leaving this false allows this function to quickly query total + * and purgable GPU memory without the caller having to spend time in + * GrDirectContext::dumpMemoryStatistics(...) first, which iterates over every resource in the GPU + * cache. This can save significant time, but buckets all GPU memory into a single "misc" track, + * which may be a loss of granularity depending on the GPU backend and the categories defined in + * sResourceMap. */ -void ATraceMemoryDump::logTraces() { +void ATraceMemoryDump::logTraces(bool gpuMemoryIsAlreadyInDump, GrDirectContext* grContext) { // Accumulate data from last dumpName recordAndResetCountersIfNeeded(""); uint64_t hwui_all_frame_memory = 0; @@ -126,6 +136,20 @@ void ATraceMemoryDump::logTraces() { ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory); } } + + if (!gpuMemoryIsAlreadyInDump && grContext) { + // Total GPU memory + int gpuResourceCount; + size_t gpuResourceBytes; + grContext->getResourceCacheUsage(&gpuResourceCount, &gpuResourceBytes); + hwui_all_frame_memory += (uint64_t)gpuResourceBytes; + ATRACE_INT64("HWUI Misc Memory", gpuResourceBytes); + + // Purgable subset of GPU memory + size_t purgeableGpuResourceBytes = grContext->getResourceCachePurgeableBytes(); + ATRACE_INT64("Purgeable HWUI Misc Memory", purgeableGpuResourceBytes); + } + ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory); } diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h index 4592711dd5b5..777d1a2ddb5b 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -16,6 +16,7 @@ #pragma once +#include <GrDirectContext.h> #include <SkString.h> #include <SkTraceMemoryDump.h> @@ -50,7 +51,7 @@ public: void startFrame(); - void logTraces(); + void logTraces(bool gpuMemoryIsAlreadyInDump, GrDirectContext* grContext); private: std::string mLastDumpName; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 30d461271c89..eb4d4948dc53 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -269,13 +269,14 @@ void CacheManager::onFrameCompleted() { cancelDestroyContext(); mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC); if (ATRACE_ENABLED()) { + ATRACE_NAME("dumpingMemoryStatistics"); static skiapipeline::ATraceMemoryDump tracer; tracer.startFrame(); SkGraphics::DumpMemoryStatistics(&tracer); - if (mGrContext) { + if (mGrContext && Properties::debugTraceGpuResourceCategories) { mGrContext->dumpMemoryStatistics(&tracer); } - tracer.logTraces(); + tracer.logTraces(Properties::debugTraceGpuResourceCategories, mGrContext.get()); } } diff --git a/lint-baseline.xml b/lint-baseline.xml index 79b21551a76e..660884a18010 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -433,7 +433,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;" + errorLine1=" boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -444,7 +444,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;" + errorLine1=" boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -455,7 +455,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;" + errorLine1=" boolean period = System.getInt(resolver, System.TEXT_AUTO_PUNCTUATE, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -466,7 +466,7 @@ <issue id="NonUserGetterCalled" message="`android.provider.Settings.System#getInt()` called from system process. Please call `android.provider.Settings.System#getIntForUser()` instead. " - errorLine1=" boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;" + errorLine1=" boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;" errorLine2=" ~~~~~~"> <location file="frameworks/base/core/java/android/text/method/TextKeyListener.java" @@ -562,4 +562,4 @@ column="74"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 89792c7638cb..9616b5d44540 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -48,6 +48,7 @@ import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -141,7 +142,9 @@ public final class MediaRouter2 { * dispatch. This is only used to determine what callback a route should be assigned to (added, * removed, changed) in {@link #dispatchFilteredRoutesUpdatedOnHandler(List)}. */ - private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>(); + private volatile ArrayMap<String, MediaRoute2Info> mPreviousFilteredRoutes = new ArrayMap<>(); + + private final Map<String, MediaRoute2Info> mPreviousUnfilteredRoutes = new ArrayMap<>(); /** * Stores the latest copy of exposed routes after filtering, sorting, and deduplication. Can be @@ -282,6 +285,8 @@ public final class MediaRouter2 { MediaRouter2 instance = sAppToProxyRouterMap.get(key); if (instance == null) { instance = new MediaRouter2(context, looper, clientPackageName, user); + // Register proxy router after instantiation to avoid race condition. + ((ProxyMediaRouter2Impl) instance.mImpl).registerProxyRouter(); sAppToProxyRouterMap.put(key, instance); } return instance; @@ -368,6 +373,7 @@ public final class MediaRouter2 { new SystemRoutingController( ProxyMediaRouter2Impl.getSystemSessionInfoImpl( mMediaRouterService, clientPackageName)); + mImpl = new ProxyMediaRouter2Impl(context, clientPackageName, user); } @@ -713,7 +719,7 @@ public final class MediaRouter2 { mImpl.transfer( controller.getRoutingSessionInfo(), route, - android.os.Process.myUserHandle(), + Process.myUserHandle(), mContext.getPackageName()); } @@ -883,7 +889,7 @@ public final class MediaRouter2 { newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet()); for (MediaRoute2Info route : newRoutes) { - MediaRoute2Info prevRoute = mPreviousRoutes.get(route.getId()); + MediaRoute2Info prevRoute = mPreviousFilteredRoutes.get(route.getId()); if (prevRoute == null) { addedRoutes.add(route); } else if (!prevRoute.equals(route)) { @@ -891,21 +897,21 @@ public final class MediaRouter2 { } } - for (int i = 0; i < mPreviousRoutes.size(); i++) { - if (!newRouteIds.contains(mPreviousRoutes.keyAt(i))) { - removedRoutes.add(mPreviousRoutes.valueAt(i)); + for (int i = 0; i < mPreviousFilteredRoutes.size(); i++) { + if (!newRouteIds.contains(mPreviousFilteredRoutes.keyAt(i))) { + removedRoutes.add(mPreviousFilteredRoutes.valueAt(i)); } } // update previous routes for (MediaRoute2Info route : removedRoutes) { - mPreviousRoutes.remove(route.getId()); + mPreviousFilteredRoutes.remove(route.getId()); } for (MediaRoute2Info route : addedRoutes) { - mPreviousRoutes.put(route.getId(), route); + mPreviousFilteredRoutes.put(route.getId(), route); } for (MediaRoute2Info route : changedRoutes) { - mPreviousRoutes.put(route.getId(), route); + mPreviousFilteredRoutes.put(route.getId(), route); } if (!addedRoutes.isEmpty()) { @@ -924,6 +930,27 @@ public final class MediaRouter2 { } } + void dispatchControllerUpdatedIfNeededOnHandler(Map<String, MediaRoute2Info> routesMap) { + List<RoutingController> controllers = getControllers(); + for (RoutingController controller : controllers) { + + for (String selectedRoute : controller.getRoutingSessionInfo().getSelectedRoutes()) { + if (routesMap.containsKey(selectedRoute) + && mPreviousUnfilteredRoutes.containsKey(selectedRoute)) { + MediaRoute2Info currentRoute = routesMap.get(selectedRoute); + MediaRoute2Info oldRoute = mPreviousUnfilteredRoutes.get(selectedRoute); + if (!currentRoute.equals(oldRoute)) { + notifyControllerUpdated(controller); + break; + } + } + } + } + + mPreviousUnfilteredRoutes.clear(); + mPreviousUnfilteredRoutes.putAll(routesMap); + } + void updateRoutesOnHandler(List<MediaRoute2Info> newRoutes) { synchronized (mLock) { mRoutes.clear(); @@ -945,6 +972,11 @@ public final class MediaRouter2 { MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler, this, mFilteredRoutes)); + mHandler.sendMessage( + obtainMessage( + MediaRouter2::dispatchControllerUpdatedIfNeededOnHandler, + this, + new HashMap<>(mRoutes))); } /** @@ -1528,7 +1560,7 @@ public final class MediaRouter2 { UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle(); String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName(); - return Objects.equals(android.os.Process.myUserHandle(), transferInitiatorUserHandle) + return Objects.equals(Process.myUserHandle(), transferInitiatorUserHandle) && Objects.equals(mContext.getPackageName(), transferInitiatorPackageName); } @@ -2153,18 +2185,19 @@ public final class MediaRouter2 { mClientUser = user; mClientPackageName = clientPackageName; mClient = new Client(); + mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; + } + public void registerProxyRouter() { try { mMediaRouterService.registerProxyRouter( mClient, - context.getApplicationContext().getPackageName(), - clientPackageName, - user); + mContext.getApplicationContext().getPackageName(), + mClientPackageName, + mClientUser); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } - - mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; } @Override @@ -2294,11 +2327,7 @@ public final class MediaRouter2 { List<RoutingSessionInfo> sessionInfos = getRoutingSessions(); RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1); - transfer( - targetSession, - route, - android.os.Process.myUserHandle(), - mContext.getPackageName()); + transfer(targetSession, route, Process.myUserHandle(), mContext.getPackageName()); } @Override @@ -3165,8 +3194,12 @@ public final class MediaRouter2 { return; } - requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE, - android.os.Process.myUserHandle(), mContext.getPackageName()); + requestCreateController( + controller, + route, + MANAGER_REQUEST_ID_NONE, + Process.myUserHandle(), + mContext.getPackageName()); } @Override diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java index 262f5f198c22..096e8adf2f68 100644 --- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java +++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java @@ -50,8 +50,16 @@ public final class BluetoothMidiDevice { private static final String TAG = "BluetoothMidiDevice"; private static final boolean DEBUG = false; - private static final int DEFAULT_PACKET_SIZE = 20; - private static final int MAX_PACKET_SIZE = 512; + // Bluetooth services should subtract 5 bytes from the MTU for headers. + private static final int HEADER_SIZE = 5; + // Min MTU size for BLE + private static final int MIN_L2CAP_MTU = 23; + // 23 (min L2CAP MTU) - 5 (header size) + private static final int DEFAULT_PACKET_SIZE = MIN_L2CAP_MTU - HEADER_SIZE; + // Max MTU size on Android + private static final int MAX_ANDROID_MTU = 517; + // 517 (max Android MTU) - 5 (header size) + private static final int MAX_PACKET_SIZE = MAX_ANDROID_MTU - HEADER_SIZE; // Bluetooth MIDI Gatt service UUID private static final UUID MIDI_SERVICE = UUID.fromString( @@ -135,8 +143,8 @@ public final class BluetoothMidiDevice { // switch to receiving notifications mBluetoothGatt.readCharacteristic(characteristic); - // Request higher MTU size - if (!gatt.requestMtu(MAX_PACKET_SIZE)) { + // Request max MTU size + if (!gatt.requestMtu(MAX_ANDROID_MTU)) { Log.e(TAG, "request mtu failed"); mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); @@ -204,8 +212,15 @@ public final class BluetoothMidiDevice { public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { Log.d(TAG, "onMtuChanged callback received. mtu: " + mtu + ", status: " + status); if (status == BluetoothGatt.GATT_SUCCESS) { - mPacketEncoder.setMaxPacketSize(Math.min(mtu, MAX_PACKET_SIZE)); - mPacketDecoder.setMaxPacketSize(Math.min(mtu, MAX_PACKET_SIZE)); + int packetSize = Math.min(mtu - HEADER_SIZE, MAX_PACKET_SIZE); + if (packetSize <= 0) { + Log.e(TAG, "onMtuChanged non-positive packet size: " + packetSize); + packetSize = DEFAULT_PACKET_SIZE; + } else if (packetSize < DEFAULT_PACKET_SIZE) { + Log.w(TAG, "onMtuChanged small packet size: " + packetSize); + } + mPacketEncoder.setMaxPacketSize(packetSize); + mPacketDecoder.setMaxPacketSize(packetSize); } else { mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE); diff --git a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml deleted file mode 100644 index 9d16f32db932..000000000000 --- a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<!-- Copied from //frameworks/base/core/res/res/drawable/item_background_material.xml --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@color/autofill_light_colorControlHighlight"> - <item android:id="@android:id/mask"> - <color android:color="@android:color/white"/> - </item> -</ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml index 2f0c83b6556a..5becc86927d2 100644 --- a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml +++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2023 The Android Open Source Project + ~ Copyright (C) 2024 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ android:shape="rectangle" android:top="1dp"> <shape> - <corners android:radius="28dp" /> - <solid android:color="@android:color/system_surface_container_high_light" /> + <corners android:radius="16dp" /> + <solid android:color="@color/dropdown_container" /> </shape> </item> </ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml deleted file mode 100644 index e4e9f7ac85a9..000000000000 --- a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml +++ /dev/null @@ -1,37 +0,0 @@ -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@android:id/content" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/autofill.Dataset"> - <ImageView - android:id="@android:id/icon1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_alignParentStart="true" - android:background="@null"/> - <TextView - android:id="@android:id/text1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_toEndOf="@android:id/icon1" - style="@style/autofill.TextAppearance"/> - -</RelativeLayout> diff --git a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml new file mode 100644 index 000000000000..cb6c6b473244 --- /dev/null +++ b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml @@ -0,0 +1,45 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.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. + --> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxWidth="@dimen/autofill_dropdown_layout_width" + android:elevation="3dp"> + + <ImageView + android:id="@android:id/icon1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_alignParentStart="true" + android:background="@null"/> + <TextView + android:id="@android:id/text1" + android:layout_width="@dimen/autofill_dropdown_text_width" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_toEndOf="@android:id/icon1" + style="@style/autofill.TextTitle"/> + <TextView + android:id="@android:id/text2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@android:id/text1" + android:layout_toEndOf="@android:id/icon1" + style="@style/autofill.TextSubtitle"/> + +</RelativeLayout> diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml index 63b9f24d9033..dcb7ef9c3ed8 100644 --- a/packages/CredentialManager/res/values/colors.xml +++ b/packages/CredentialManager/res/values/colors.xml @@ -16,23 +16,8 @@ <!-- Color palette --> <resources> - <color name="autofill_light_colorPrimary">@color/primary_material_light</color> - <color name="autofill_light_colorAccent">@color/accent_material_light</color> - <color name="autofill_light_colorControlHighlight">@color/ripple_material_light</color> - <color name="autofill_light_colorButtonNormal">@color/button_material_light</color> - - <!-- Text colors --> - <color name="autofill_light_textColorPrimary">@color/abc_primary_text_material_light</color> - <color name="autofill_light_textColorSecondary">@color/abc_secondary_text_material_light</color> - <color name="autofill_light_textColorHint">@color/abc_hint_foreground_material_light</color> - <color name="autofill_light_textColorHintInverse">@color/abc_hint_foreground_material_dark - </color> - <color name="autofill_light_textColorHighlight">@color/highlighted_text_material_light</color> - <color name="autofill_light_textColorLink">@color/autofill_light_colorAccent</color> - <!-- These colors are used for Remote Views. --> - <color name="background_dark_mode">#0E0C0B</color> - <color name="background">#F1F3F4</color> - <color name="text_primary_dark_mode">#DFDEDB</color> - <color name="text_primary">#202124</color> + <color name="text_primary">#1A1B20</color> + <color name="text_secondary">#44474F</color> + <color name="dropdown_container">#F3F3FA</color> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml index 67003a330974..2a4719d027e2 100644 --- a/packages/CredentialManager/res/values/dimens.xml +++ b/packages/CredentialManager/res/values/dimens.xml @@ -17,6 +17,12 @@ --> <resources> - <dimen name="autofill_view_padding">16dp</dimen> - <dimen name="autofill_icon_size">16dp</dimen> + <dimen name="autofill_view_top_padding">12dp</dimen> + <dimen name="autofill_view_right_padding">24dp</dimen> + <dimen name="autofill_view_bottom_padding">12dp</dimen> + <dimen name="autofill_view_left_padding">16dp</dimen> + <dimen name="autofill_view_icon_to_text_padding">10dp</dimen> + <dimen name="autofill_icon_size">24dp</dimen> + <dimen name="autofill_dropdown_layout_width">296dp</dimen> + <dimen name="autofill_dropdown_text_width">240dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/styles.xml b/packages/CredentialManager/res/values/styles.xml index 4a5761acdd83..7de941ebd4a5 100644 --- a/packages/CredentialManager/res/values/styles.xml +++ b/packages/CredentialManager/res/values/styles.xml @@ -15,24 +15,13 @@ --> <resources> - <style name="autofill.TextAppearance.Small" parent="@style/autofill.TextAppearance"> - <item name="android:textSize">12sp</item> - </style> - - - <style name="autofill.Dataset" parent=""> - <item name="android:background">@drawable/autofill_light_selectable_item_background</item> - </style> - - <style name="autofill.TextAppearance" parent=""> - <item name="android:textColor">@color/autofill_light_textColorPrimary</item> - <item name="android:textColorHint">@color/autofill_light_textColorHint</item> - <item name="android:textColorHighlight">@color/autofill_light_textColorHighlight</item> - <item name="android:textColorLink">@color/autofill_light_textColorLink</item> + <style name="autofill.TextTitle" parent=""> + <item name="android:fontFamily">google-sans-medium</item> <item name="android:textSize">14sp</item> </style> - <style name="autofill.TextAppearance.Primary"> - <item name="android:textColor">@color/autofill_light_textColorPrimary</item> + <style name="autofill.TextSubtitle" parent=""> + <item name="android:fontFamily">google-sans-text</item> + <item name="android:textSize">12sp</item> </style> </resources>
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index b2c23a401117..58467afe43a4 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -57,6 +57,7 @@ import com.android.credentialmanager.common.ui.RemoteViewsFactory import com.android.credentialmanager.getflow.ProviderDisplayInfo import com.android.credentialmanager.getflow.toProviderDisplayInfo import com.android.credentialmanager.ktx.credentialEntry +import com.android.credentialmanager.model.CredentialType import com.android.credentialmanager.model.get.CredentialEntryInfo import com.android.credentialmanager.model.get.ProviderInfo import org.json.JSONException @@ -313,12 +314,14 @@ class CredentialAutofillService : AutofillService() { var i = 0 var datasetAdded = false - val duplicateDisplayNames: MutableMap<String, Boolean> = mutableMapOf() + val duplicateDisplayNamesForPasskeys: MutableMap<String, Boolean> = mutableMapOf() providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach { val credentialEntry = it.sortedCredentialEntryList.first() - credentialEntry.displayName?.let {displayName -> - val duplicateEntry = duplicateDisplayNames.contains(displayName) - duplicateDisplayNames[displayName] = duplicateEntry + if (credentialEntry.credentialType == CredentialType.PASSKEY) { + credentialEntry.displayName?.let { displayName -> + val duplicateEntry = duplicateDisplayNamesForPasskeys.contains(displayName) + duplicateDisplayNamesForPasskeys[displayName] = duplicateEntry + } } } providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{ @@ -355,12 +358,19 @@ class CredentialAutofillService : AutofillService() { } else { spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1] } - val displayName : String = primaryEntry.displayName ?: primaryEntry.userName + val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY + && primaryEntry.displayName != null) { + primaryEntry.displayName!! + } else { + primaryEntry.userName + } val sliceBuilder = InlineSuggestionUi .newContentBuilder(pendingIntent) .setTitle(displayName) sliceBuilder.setStartIcon(icon) - if (duplicateDisplayNames[displayName] == true) { + if (primaryEntry.credentialType == + CredentialType.PASSKEY && duplicateDisplayNamesForPasskeys[displayName] + == true) { sliceBuilder.setSubtitle(primaryEntry.userName) } inlinePresentation = InlinePresentation( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt index 4dc7f00c1550..e039dead043e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt @@ -21,6 +21,7 @@ import android.content.res.Configuration import android.widget.RemoteViews import androidx.core.content.ContextCompat import com.android.credentialmanager.model.get.CredentialEntryInfo +import com.android.credentialmanager.model.CredentialType import android.graphics.drawable.Icon class RemoteViewsFactory { @@ -29,48 +30,87 @@ class RemoteViewsFactory { private const val setAdjustViewBoundsMethodName = "setAdjustViewBounds" private const val setMaxHeightMethodName = "setMaxHeight" private const val setBackgroundResourceMethodName = "setBackgroundResource" + private const val bulletPoint = "\u2022" + private const val passwordCharacterLength = 15 fun createDropdownPresentation( context: Context, icon: Icon, credentialEntryInfo: CredentialEntryInfo ): RemoteViews { - val padding = context.resources.getDimensionPixelSize(com.android - .credentialmanager.R.dimen.autofill_view_padding) var layoutId: Int = com.android.credentialmanager.R.layout - .autofill_dataset_left_with_item_tag_hint + .credman_dropdown_presentation_layout val remoteViews = RemoteViews(context.packageName, layoutId) - setRemoteViewsPaddings(remoteViews, padding) - val textColorPrimary = getTextColorPrimary(isDarkMode(context), context); - remoteViews.setTextColor(android.R.id.text1, textColorPrimary); - remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName) - + if (credentialEntryInfo.credentialType == CredentialType.UNKNOWN) { + return remoteViews + } + setRemoteViewsPaddings(remoteViews, context) + if (credentialEntryInfo.credentialType == CredentialType.PASSKEY) { + val displayName = credentialEntryInfo.displayName ?: credentialEntryInfo.userName + remoteViews.setTextViewText(android.R.id.text1, displayName) + val secondaryText = if (credentialEntryInfo.displayName != null) + (credentialEntryInfo.userName + " " + bulletPoint + " " + + credentialEntryInfo.credentialTypeDisplayName + + " " + bulletPoint + " " + credentialEntryInfo.providerDisplayName) + else (credentialEntryInfo.credentialTypeDisplayName + " " + bulletPoint + " " + + credentialEntryInfo.providerDisplayName) + remoteViews.setTextViewText(android.R.id.text2, secondaryText) + } else { + remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName) + remoteViews.setTextViewText(android.R.id.text2, + bulletPoint.repeat(passwordCharacterLength)) + } + val textColorPrimary = ContextCompat.getColor(context, + com.android.credentialmanager.R.color.text_primary) + remoteViews.setTextColor(android.R.id.text1, textColorPrimary) + val textColorSecondary = ContextCompat.getColor(context, com.android + .credentialmanager.R.color.text_secondary) + remoteViews.setTextColor(android.R.id.text2, textColorSecondary) remoteViews.setImageViewIcon(android.R.id.icon1, icon); remoteViews.setBoolean( android.R.id.icon1, setAdjustViewBoundsMethodName, true); remoteViews.setInt( android.R.id.icon1, - setMaxHeightMethodName, + setMaxHeightMethodName, context.resources.getDimensionPixelSize( com.android.credentialmanager.R.dimen.autofill_icon_size)); - val drawableId = if (isDarkMode(context)) - com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one_dark - else com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one + val drawableId = + com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one remoteViews.setInt( android.R.id.content, setBackgroundResourceMethodName, drawableId); return remoteViews } private fun setRemoteViewsPaddings( - remoteViews: RemoteViews, - padding: Int) { - val halfPadding = padding / 2 + remoteViews: RemoteViews, context: Context) { + val leftPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_left_padding) + val iconToTextPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_icon_to_text_padding) + val rightPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_right_padding) + val topPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_top_padding) + val bottomPadding = context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_view_bottom_padding) + remoteViews.setViewPadding( + android.R.id.icon1, + leftPadding, + /* top=*/0, + /* right=*/0, + /* bottom=*/0) remoteViews.setViewPadding( android.R.id.text1, - halfPadding, - halfPadding, - halfPadding, - halfPadding) + iconToTextPadding, + /* top=*/topPadding, + /* right=*/rightPadding, + /* bottom=*/0) + remoteViews.setViewPadding( + android.R.id.text2, + iconToTextPadding, + /* top=*/0, + /* right=*/rightPadding, + /* bottom=*/bottomPadding) } private fun isDarkMode(context: Context): Boolean { @@ -78,11 +118,5 @@ class RemoteViewsFactory { Configuration.UI_MODE_NIGHT_MASK return currentNightMode == Configuration.UI_MODE_NIGHT_YES } - - private fun getTextColorPrimary(darkMode: Boolean, context: Context): Int { - return if (darkMode) ContextCompat.getColor( - context, com.android.credentialmanager.R.color.text_primary_dark_mode) - else ContextCompat.getColor(context, com.android.credentialmanager.R.color.text_primary) - } } } diff --git a/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml b/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml index 7f1651721d84..8127e1a6d5a2 100644 --- a/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml +++ b/packages/SettingsLib/AdaptiveIcon/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" @@ -8,7 +8,7 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java" - line="78" + line="79" column="34"/> </issue> @@ -19,7 +19,7 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java" - line="90" + line="91" column="36"/> </issue> @@ -30,7 +30,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="43" + line="45" column="46"/> </issue> @@ -41,7 +41,7 @@ errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="65" + line="67" column="9"/> </issue> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="72" + line="74" column="9"/> </issue> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="80" + line="82" column="9"/> </issue> @@ -74,8 +74,8 @@ errorLine2=" ~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java" - line="105" + line="107" column="26"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 8b16d64bcfe7..5da4b9518a31 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -61,7 +61,6 @@ android_library { "src/**/*.kt", ], lint: { - baseline_filename: "lint-baseline.xml", extra_check_modules: ["SettingsLibLintChecker"], }, } diff --git a/packages/SettingsLib/EmergencyNumber/lint-baseline.xml b/packages/SettingsLib/EmergencyNumber/lint-baseline.xml index e9c687fd3227..13bf5f54ead3 100644 --- a/packages/SettingsLib/EmergencyNumber/lint-baseline.xml +++ b/packages/SettingsLib/EmergencyNumber/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" @@ -8,7 +8,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="77" + line="81" column="41"/> </issue> @@ -19,7 +19,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="78" + line="82" column="45"/> </issue> @@ -30,18 +30,18 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="78" + line="82" column="62"/> </issue> <issue id="NewApi" message="Call requires API level 29 (current min is 21): `android.telephony.TelephonyManager#getEmergencyNumberList`" - errorLine1=" Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(" + errorLine1=" Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(" errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="173" + line="177" column="74"/> </issue> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="196" + line="200" column="41"/> </issue> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="219" + line="223" column="69"/> </issue> @@ -74,7 +74,7 @@ errorLine2=" ~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="234" + line="238" column="41"/> </issue> @@ -85,8 +85,8 @@ errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java" - line="251" + line="255" column="52"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp index d9f74dadf281..010a6ce9d4d9 100644 --- a/packages/SettingsLib/MainSwitchPreference/Android.bp +++ b/packages/SettingsLib/MainSwitchPreference/Android.bp @@ -28,7 +28,4 @@ android_library { "com.android.extservices", "com.android.healthfitness", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml b/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml deleted file mode 100644 index cfa64a487407..000000000000 --- a/packages/SettingsLib/MainSwitchPreference/lint-baseline.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> - - <issue - id="NewApi" - message="`@android:dimen/config_restrictedIconSize` requires API level 29 (current min is 28)" - errorLine1=' <dimen name="settingslib_restricted_icon_size">@android:dimen/config_restrictedIconSize</dimen>' - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml" - line="21" - column="52"/> - </issue> - -</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml index 26d05a621c22..45a07fe9eee3 100644 --- a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml +++ b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml @@ -1,26 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" - errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" - errorLine2=" ~~"> + message="Call requires API level 28 (current min is 23): `android.content.Context#createPackageContextAsUser`" + errorLine1=" userContext = context.createPackageContextAsUser(context.getPackageName(), 0, user);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="97" - column="56"/> + line="64" + column="35"/> </issue> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" - errorLine1=" return um.getUserProfiles().contains(UserHandle.of(userId));" - errorLine2=" ~~"> + message="Call requires API level 29 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerUser`" + errorLine1=" if (Objects.equals(dpm.getDeviceOwnerUser(), user)) {" + errorLine2=" ~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="140" - column="57"/> + line="74" + column="32"/> </issue> <issue @@ -36,35 +36,35 @@ <issue id="NewApi" - message="Call requires API level 28 (current min is 23): `android.content.Context#createPackageContextAsUser`" - errorLine1=" userContext = context.createPackageContextAsUser(context.getPackageName(), 0, user);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" + errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="64" - column="35"/> + line="97" + column="56"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerUser`" - errorLine1=" if (Objects.equals(dpm.getDeviceOwnerUser(), user)) {" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 23): `android.content.Context#startActivityAsUser`" + errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="74" - column="32"/> + line="97" + column="17"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 23): `android.content.Context#startActivityAsUser`" - errorLine1=" context.startActivityAsUser(intent, UserHandle.of(targetUserId));" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserHandle#of`" + errorLine1=" return um.getUserProfiles().contains(UserHandle.of(userId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java" - line="97" - column="17"/> + line="120" + column="57"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/SchedulesProvider/lint-baseline.xml b/packages/SettingsLib/SchedulesProvider/lint-baseline.xml index 0744710e5224..db6a88210cd4 100644 --- a/packages/SettingsLib/SchedulesProvider/lint-baseline.xml +++ b/packages/SettingsLib/SchedulesProvider/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" diff --git a/packages/SettingsLib/SearchProvider/lint-baseline.xml b/packages/SettingsLib/SearchProvider/lint-baseline.xml index 53346e030be2..3cfca1d2cdcb 100644 --- a/packages/SettingsLib/SearchProvider/lint-baseline.xml +++ b/packages/SettingsLib/SearchProvider/lint-baseline.xml @@ -1,36 +1,47 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableResource`" - errorLine1=" super(" - errorLine2=" ~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexablesProvider`" + errorLine1="public abstract class SettingsXmlIndexProvider extends SearchIndexablesProvider {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="107" - column="13"/> + line="34" + column="56"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableResource`" - errorLine1=" public static final class SearchIndexableIntentResource extends SearchIndexableResource {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexablesContract#INDEXABLES_XML_RES_COLUMNS`" + errorLine1=" final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="97" - column="69"/> + line="45" + column="54"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexablesProvider`" - errorLine1="public abstract class SettingsXmlIndexProvider extends SearchIndexablesProvider {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#rank`" + errorLine1=" .add(XmlResource.COLUMN_RANK, indexableResource.rank)" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="34" + line="50" + column="51"/> + </issue> + + <issue + id="NewApi" + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableResource#xmlResId`" + errorLine1=" .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" + line="51" column="56"/> </issue> @@ -58,79 +69,68 @@ <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" - errorLine1=' this.intentAction = "android.intent.action.MAIN";' - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="113" - column="17"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" - errorLine1=" this.intentAction = intentAction;" - errorLine2=" ~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" + errorLine1=" indexableResource.intentTargetClass);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="115" - column="17"/> + line="56" + column="29"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" - errorLine1=" indexableResource.intentTargetClass);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableResource`" + errorLine1=" public static final class SearchIndexableIntentResource extends SearchIndexableResource {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="56" - column="29"/> + line="97" + column="69"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" - errorLine1=" this.intentTargetClass = className;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableResource`" + errorLine1=" super(" + errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="117" + line="107" column="13"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#rank`" - errorLine1=" .add(XmlResource.COLUMN_RANK, indexableResource.rank)" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" + errorLine1=' this.intentAction = "android.intent.action.MAIN";' + errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="50" - column="51"/> + line="113" + column="17"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableResource#xmlResId`" - errorLine1=" .add(XmlResource.COLUMN_XML_RESID, indexableResource.xmlResId)" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentAction`" + errorLine1=" this.intentAction = intentAction;" + errorLine2=" ~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="51" - column="56"/> + line="115" + column="17"/> </issue> <issue id="NewApi" - message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexablesContract#INDEXABLES_XML_RES_COLUMNS`" - errorLine1=" final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`" + errorLine1=" this.intentTargetClass = className;" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java" - line="45" - column="54"/> + line="117" + column="13"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt index a9974dc7d389..514ad66919ee 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt @@ -39,6 +39,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import com.android.settingslib.spa.framework.theme.SettingsDimension import com.android.settingslib.spa.framework.theme.SettingsTheme @@ -68,6 +71,7 @@ fun Spinner(options: List<SpinnerOption>, selectedId: Int?, setId: (id: Int) -> ) { val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd) Button( + modifier = Modifier.semantics { role = Role.DropdownList }, onClick = { expanded = true }, colors = ButtonDefaults.buttonColors( containerColor = SettingsTheme.colorScheme.spinnerHeaderContainer, diff --git a/packages/SettingsLib/Tile/lint-baseline.xml b/packages/SettingsLib/Tile/lint-baseline.xml index 326ec0dbaa72..56b1bcafbae2 100644 --- a/packages/SettingsLib/Tile/lint-baseline.xml +++ b/packages/SettingsLib/Tile/lint-baseline.xml @@ -1,55 +1,59 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `java.lang.Iterable#forEach`" - errorLine1=" controllers.forEach(controller -> {" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java" - line="79" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`"> + message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`" + errorLine1=" dest.writeBoolean(this instanceof ProviderTile);" + errorLine2=" ~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="312"/> + line="114" + column="14"/> </issue> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`"> + message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`" + errorLine1=" final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);" + errorLine2=" ~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="318"/> + line="326" + column="36"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`"> + message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`" + errorLine1=" icon.setTint(tintColor);" + errorLine2=" ~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="373"/> + line="332" + column="22"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`"> + message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`" + errorLine1=" final boolean isProviderTile = source.readBoolean();" + errorLine2=" ~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java" - line="108"/> + line="387" + column="51"/> </issue> <issue id="NewApi" - message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`"> + message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`" + errorLine1=" return provider.call(context.getAttributionSource()," + errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java" - line="565"/> + line="601" + column="42"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/Utils/lint-baseline.xml b/packages/SettingsLib/Utils/lint-baseline.xml index 3fcd56c557e8..2f6cc3ae8719 100644 --- a/packages/SettingsLib/Utils/lint-baseline.xml +++ b/packages/SettingsLib/Utils/lint-baseline.xml @@ -1,26 +1,15 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" - errorLine1=" mContext.getPackageName(), 0, UserHandle.of(managedUserId)" - errorLine2=" ~~"> - <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="119" - column="70"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" - errorLine1=" intent, 0, UserHandle.of(managedUserId));" - errorLine2=" ~~"> + message="Call requires API level 24 (current min is 23): `android.os.UserManager#isManagedProfile`" + errorLine1=" return context.getSystemService(UserManager.class).isManagedProfile(userId)" + errorLine2=" ~~~~~~~~~~~~~~~~"> <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="150" - column="47"/> + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java" + line="62" + column="60"/> </issue> <issue @@ -36,68 +25,68 @@ <issue id="NewApi" - message="Call requires API level 24 (current min is 21): `android.os.UserManager#isManagedProfile`" - errorLine1=" if (mUserManager.isManagedProfile(id)) {" - errorLine2=" ~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 21): `android.content.Context#startActivityAsUser`" + errorLine1=" activityContext.startActivityAsUser(intent, UserHandle.of(userId));" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="173" - column="30"/> + line="80" + column="29"/> </issue> <issue id="NewApi" - message="Call requires API level 24 (current min is 23): `android.os.UserManager#isManagedProfile`" - errorLine1=" return context.getSystemService(UserManager.class).isManagedProfile(userId)" - errorLine2=" ~~~~~~~~~~~~~~~~"> + message="Call requires API level 28 (current min is 21): `android.content.Context#createPackageContextAsUser`" + errorLine1=" mContext.createPackageContextAsUser(" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location - file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java" - line="62" - column="60"/> + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" + line="118" + column="30"/> </issue> <issue id="NewApi" - message="Call requires API level 26 (current min is 21): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`" - errorLine1=" return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" + errorLine1=" mContext.getPackageName(), 0, UserHandle.of(managedUserId)" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="163" - column="37"/> + line="119" + column="70"/> </issue> <issue id="NewApi" - message="Call requires API level 28 (current min is 21): `android.content.Context#createPackageContextAsUser`" - errorLine1=" mContext.createPackageContextAsUser(" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 29 (current min is 21): `android.content.pm.PackageManager#queryIntentActivitiesAsUser`" + errorLine1=" mPackageManager.queryIntentActivitiesAsUser(" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="118" - column="30"/> + line="149" + column="33"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.content.Context#startActivityAsUser`" - errorLine1=" activityContext.startActivityAsUser(intent, UserHandle.of(userId));" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 24 (current min is 21): `android.os.UserHandle#of`" + errorLine1=" intent, 0, UserHandle.of(managedUserId));" + errorLine2=" ~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="80" - column="29"/> + line="150" + column="47"/> </issue> <issue id="NewApi" - message="Call requires API level 29 (current min is 21): `android.content.pm.PackageManager#queryIntentActivitiesAsUser`" - errorLine1=" mPackageManager.queryIntentActivitiesAsUser(" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 26 (current min is 21): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`" + errorLine1=" return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" - line="149" - column="33"/> + line="163" + column="37"/> </issue> <issue @@ -111,4 +100,15 @@ column="53"/> </issue> + <issue + id="NewApi" + message="Call requires API level 24 (current min is 21): `android.os.UserManager#isManagedProfile`" + errorLine1=" if (mUserManager.isManagedProfile(id)) {" + errorLine2=" ~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java" + line="173" + column="30"/> + </issue> + </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/lint-baseline.xml b/packages/SettingsLib/lint-baseline.xml deleted file mode 100644 index d6a23fd827d9..000000000000 --- a/packages/SettingsLib/lint-baseline.xml +++ /dev/null @@ -1,204 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.bluetooth.BluetoothDevice#setAlias`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java" - line="584"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiInfo#getSubscriptionId`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java" - line="248"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiInfo#getSubscriptionId`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java" - line="278"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiManager#registerSubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="201"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.net.wifi.WifiManager#unregisterSubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="208"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.os.UserManager#isUserForeground`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java" - line="78"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/Utils.java" - line="498"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java" - line="225"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="215"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="86"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#unregisterTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="222"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#unregisterTelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="88"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java" - line="66"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java" - line="49"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 34 (current min is 30): `android.os.UserManager#isAdminUser`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java" - line="33"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.net.wifi.WifiManager.SubsystemRestartTrackingCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="64"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="125"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.CarrierNetworkListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="124"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DataActivityListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="123"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DataConnectionStateListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="122"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.DisplayInfoListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="126"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ServiceStateListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="120"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.SignalStrengthsListener`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="121"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java" - line="79"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"> - <location - file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java" - line="119"/> - </issue> - -</issues>
\ No newline at end of file diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp index 3b14712cc87e..390c9d2e98de 100644 --- a/packages/SettingsLib/search/Android.bp +++ b/packages/SettingsLib/search/Android.bp @@ -12,9 +12,6 @@ java_library { visibility: ["//visibility:private"], srcs: ["interface-src/**/*.java"], host_supported: true, - lint: { - baseline_filename: "lint-baseline.xml", - }, } android_library { diff --git a/packages/SettingsLib/search/lint-baseline.xml b/packages/SettingsLib/search/lint-baseline.xml index 7ec512b617d7..61cdb051feeb 100644 --- a/packages/SettingsLib/search/lint-baseline.xml +++ b/packages/SettingsLib/search/lint-baseline.xml @@ -1,26 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NewApi" - message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableData`" - errorLine1=" super(context);" - errorLine2=" ~~~~~"> + message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableData`" + errorLine1="public class SearchIndexableRaw extends SearchIndexableData {" + errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location file="frameworks/base/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java" - line="62" - column="9"/> + line="29" + column="41"/> </issue> <issue id="NewApi" - message="Class requires API level 23 (current min is 21): `android.provider.SearchIndexableData`" - errorLine1="public class SearchIndexableRaw extends SearchIndexableData {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> + message="Call requires API level 23 (current min is 21): `new android.provider.SearchIndexableData`" + errorLine1=" super(context);" + errorLine2=" ~~~~~"> <location file="frameworks/base/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java" - line="29" - column="41"/> + line="62" + column="9"/> </issue> </issues>
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java index 5bc271954b25..943e3fc27ebb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java @@ -70,10 +70,8 @@ public abstract class AbstractWifiMacAddressPreferenceController @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - if (isAvailable()) { - mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS); - updateConnectivity(); - } + mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS); + updateConnectivity(); } @Override @@ -84,16 +82,16 @@ public abstract class AbstractWifiMacAddressPreferenceController @SuppressLint("HardwareIds") @Override protected void updateConnectivity() { + if (mWifiManager == null || mWifiMacAddress == null) { + return; + } + final String[] macAddresses = mWifiManager.getFactoryMacAddresses(); String macAddress = null; if (macAddresses != null && macAddresses.length > 0) { macAddress = macAddresses[0]; } - if (mWifiMacAddress == null) { - return; - } - if (TextUtils.isEmpty(macAddress) || macAddress.equals(WifiInfo.DEFAULT_MAC_ADDRESS)) { mWifiMacAddress.setSummary(R.string.status_unavailable); } else { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index d47527a0a191..2fea6a56daaf 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close @@ -21,10 +20,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.ElementKey @@ -41,7 +38,6 @@ import com.android.compose.animation.scene.updateSceneTransitionLayoutState import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.transform @@ -66,7 +62,6 @@ val sceneTransitions = transitions { * This is a temporary container to allow the communal UI to use [SceneTransitionLayout] for gesture * handling and transitions before the full Flexiglass layout is ready. */ -@OptIn(ExperimentalComposeUiApi::class, ExperimentalCoroutinesApi::class) @Composable fun CommunalContainer( modifier: Modifier = Modifier, @@ -83,8 +78,8 @@ fun CommunalContainer( transitions = sceneTransitions, ) - // Don't show hub mode UI if keyguard is present. This is important since we're in the shade, - // which can be opened from many locations. + // Don't show hub mode UI if keyguard is not present. This is important since we're in the + // shade, which can be opened from many locations. val isKeyguardShowing by viewModel.isKeyguardVisible.collectAsState(initial = false) // Failsafe to hide the whole SceneTransitionLayout in case of bugginess. @@ -102,56 +97,30 @@ fun CommunalContainer( onDispose { viewModel.setTransitionState(null) } } - Box(modifier = modifier.fillMaxSize()) { - SceneTransitionLayout( - state = sceneTransitionLayoutState, - modifier = Modifier.fillMaxSize(), - edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize), + SceneTransitionLayout( + state = sceneTransitionLayoutState, + modifier = modifier.fillMaxSize(), + edgeDetector = FixedSizeEdgeDetector(ContainerDimensions.EdgeSwipeSize), + ) { + scene( + TransitionSceneKey.Blank, + userActions = + mapOf( + Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to TransitionSceneKey.Communal + ) ) { - scene( - TransitionSceneKey.Blank, - userActions = - mapOf( - Swipe(SwipeDirection.Left, fromEdge = Edge.Right) to - TransitionSceneKey.Communal - ) - ) { - BlankScene { showSceneTransitionLayout = false } - } - - scene( - TransitionSceneKey.Communal, - userActions = - mapOf( - Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to - TransitionSceneKey.Blank - ), - ) { - CommunalScene(viewModel, modifier = modifier) - } + BlankScene { showSceneTransitionLayout = false } } - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't - // block touches anymore. - Box( - modifier = - Modifier.fillMaxSize() - // Offsetting to the left so that edge swipe to open the hub still works. This - // does mean that the very right edge of the hub won't refresh the screen - // timeout, but should be good enough for a temporary solution. - .offset(x = -ContainerDimensions.EdgeSwipeSize) - .pointerInteropFilter { - viewModel.onUserActivity() - if ( - sceneTransitionLayoutState.transitionState.currentScene == - TransitionSceneKey.Blank - ) { - viewModel.onOuterTouch(it) - return@pointerInteropFilter true - } - false - } - ) + scene( + TransitionSceneKey.Communal, + userActions = + mapOf( + Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to TransitionSceneKey.Blank + ), + ) { + CommunalScene(viewModel, modifier = modifier) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index c8461d2c5415..02d30c5ea20a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -197,7 +197,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(true) featureFlags = FakeFeatureFlags() - featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false) featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false) featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index ff6fd43745df..54510a82201a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -21,7 +21,6 @@ import android.app.Activity.RESULT_OK import android.app.smartspace.SmartspaceTarget import android.appwidget.AppWidgetHost import android.content.ComponentName -import android.os.PowerManager import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -41,13 +40,11 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -64,8 +61,6 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class CommunalEditModeViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost - @Mock private lateinit var shadeViewController: ShadeViewController - @Mock private lateinit var powerManager: PowerManager @Mock private lateinit var appWidgetHost: AppWidgetHost @Mock private lateinit var uiEventLogger: UiEventLogger @@ -97,8 +92,6 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalEditModeViewModel( withDeps.communalInteractor, appWidgetHost, - Provider { shadeViewController }, - powerManager, mediaHost, uiEventLogger, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 8e3f66464de6..a7760621e97c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget -import android.os.PowerManager import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -36,12 +35,10 @@ import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import javax.inject.Provider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy @@ -58,8 +55,6 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) class CommunalViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost - @Mock private lateinit var shadeViewController: ShadeViewController - @Mock private lateinit var powerManager: PowerManager private lateinit var testScope: TestScope @@ -92,8 +87,6 @@ class CommunalViewModelTest : SysuiTestCase() { withDeps.communalInteractor, WidgetInteractionHandler(mock()), withDeps.tutorialInteractor, - Provider { shadeViewController }, - powerManager, mediaHost, ) } diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 05106c904d3d..326c7ef52fce 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -34,9 +34,6 @@ java_library { srcs: [ ":statslog-SystemUI-java-gen", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } android_library { @@ -74,9 +71,6 @@ android_library { min_sdk_version: "current", plugins: ["dagger2-compiler"], kotlincflags: ["-Xjvm-default=all"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -88,9 +82,6 @@ java_library { static_kotlin_stdlib: false, java_version: "1.8", min_sdk_version: "current", - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library { @@ -110,7 +101,4 @@ java_library { }, java_version: "1.8", min_sdk_version: "current", - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SystemUI/shared/lint-baseline.xml b/packages/SystemUI/shared/lint-baseline.xml deleted file mode 100644 index 4bd6729227e8..000000000000 --- a/packages/SystemUI/shared/lint-baseline.xml +++ /dev/null @@ -1,708 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev"> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.os.RemoteException#rethrowFromSystemServer`" - errorLine1=" throw e.rethrowFromSystemServer();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java" - line="90" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.Bitmap#getHardwareBuffer`" - errorLine1=" mBuffer != null ? mBuffer.getHardwareBuffer() : null, mRect);" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecCompat.java" - line="39" - column="43"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `new android.graphics.ParcelableColorSpace`" - errorLine1=" ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="57" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `new android.graphics.ParcelableColorSpace`" - errorLine1=" : new ParcelableColorSpace(bitmap.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="58" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.Bitmap#getHardwareBuffer`" - errorLine1=" bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer());" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="61" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Cast from `ParcelableColorSpace` to `Parcelable` requires API level 31 (current min is 26)" - errorLine1=" bundle.putParcelable(KEY_COLOR_SPACE, colorSpace);" - errorLine2=" ~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="62" - column="47"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`" - errorLine1=" return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer)," - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="84" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.graphics.ParcelableColorSpace#getColorSpace`" - errorLine1=" colorSpace.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java" - line="85" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java" - line="122" - column="47"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.util.ArraySet`" - errorLine1=" mPluginActions = new ArraySet<>(mSharedPrefs.getStringSet(PLUGIN_ACTIONS, null));" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java" - line="41" - column="26"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.util.ArraySet`" - errorLine1=" return new ArraySet<>(mPluginActions);" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java" - line="45" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 28 (current min is 26): `android.graphics.Bitmap#createBitmap`" - errorLine1=" return Bitmap.createBitmap(picture);" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java" - line="113" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Builder`" - errorLine1=" mSurface = new SurfaceControl.Builder()" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="116" - column="24"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#setName`" - errorLine1=" .setName("Transition Unrotate")" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="117" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#setParent`" - errorLine1=" .setParent(parent)" - errorLine2=" ~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="119" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Builder#build`" - errorLine1=" .build();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="120" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#reparent`" - errorLine1=" t.reparent(child, mSurface);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="137" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" SurfaceControl.Transaction t = new SurfaceControl.Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="143" - column="44"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#reparent`" - errorLine1=" t.reparent(mRotateChildren.get(i), rootLeash);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="145" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="148" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterLauncher.mSurface, launcherLayer);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="200" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="206" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(leash, info.getChanges().size() * 3 - i);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="216" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="223" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(counterWallpaper.mSurface, -1);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="233" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java" - line="238" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#taskId`" - errorLine1=" taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="101" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="192" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#isRunning`" - errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="116" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#isRunning`" - errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="210" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`" - errorLine1=" leash.mSurfaceControl.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="159" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`" - errorLine1=" mStartLeash.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java" - line="161" - column="25"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(change.getLeash(), info.getChanges().size() * 3 - i);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="97" - column="27"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="105" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java" - line="107" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#isValid`" - errorLine1=" return mSurfaceControl != null && mSurfaceControl.isValid();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java" - line="41" - column="59"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#release`" - errorLine1=" mSurfaceControlViewHost.release();" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java" - line="61" - column="37"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 26): `android.view.SurfaceView#getHostToken`" - errorLine1=" bundle.putBinder(KEY_HOST_TOKEN, surfaceView.getHostToken());" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="34" - column="54"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceView#getSurfaceControl`" - errorLine1=" bundle.putParcelable(KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl());" - errorLine2=" ~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="35" - column="63"/> - </issue> - - <issue - id="NewApi" - message="Cast from `SurfaceControl` to `Parcelable` requires API level 29 (current min is 26)" - errorLine1=" bundle.putParcelable(KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl());" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestUtils.java" - line="35" - column="51"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#isValid`" - errorLine1=" if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {" - errorLine2=" ~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="107" - column="79"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" Transaction t = new Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="113" - column="33"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="122" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" t.setAlpha(surface, alpha);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="361" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" t.setLayer(surface, layer);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java" - line="364" - column="19"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#origActivity`" - errorLine1=" ComponentName sourceComponent = t.origActivity != null" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="73" - column="45"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#origActivity`" - errorLine1=" ? t.origActivity" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="75" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" this.id = t.taskId;" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="78" - column="23"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#baseIntent`" - errorLine1=" this.baseIntent = t.baseIntent;" - errorLine2=" ~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="80" - column="31"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskDescription`" - errorLine1=" ActivityManager.TaskDescription td = taskInfo.taskDescription;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="242" - column="46"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#topActivity`" - errorLine1=" taskInfo.supportsSplitScreenMultiWindow, isLocked, td, taskInfo.topActivity);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java" - line="246" - column="72"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#topActivity`" - errorLine1=" return info.topActivity;" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java" - line="49" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskDescription`" - errorLine1=" return info.taskDescription;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java" - line="53" - column="16"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#taskId`" - errorLine1=" onTaskMovedToFront(taskInfo.taskId);" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java" - line="70" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`" - errorLine1=" onTaskMovedToFront(taskInfo.taskId);" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java" - line="70" - column="28"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`" - errorLine1=" thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java" - line="69" - column="36"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 26): `android.app.WallpaperColors#getColorHints`" - errorLine1=" (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TonalCompat.java" - line="42" - column="29"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `new android.view.SurfaceControl.Transaction`" - errorLine1=" mTransaction = new Transaction();" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="31" - column="24"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" mTransaction.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="35" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setBufferSize`" - errorLine1=" mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="54" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setLayer`" - errorLine1=" mTransaction.setLayer(surfaceControl.mSurfaceControl, z);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="59" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#setAlpha`" - errorLine1=" mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha);" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java" - line="64" - column="22"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl.Transaction#apply`" - errorLine1=" t.apply();" - errorLine2=" ~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java" - line="64" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`" - errorLine1=" .getFloat(Resources.getSystem().getIdentifier(" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java" - line="46" - column="18"/> - </issue> - -</issues> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index e457ca18d071..8e5d0dac7bef 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -1111,7 +1111,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } private boolean canDisplayUserSwitcher() { - return mFeatureFlags.isEnabled(Flags.BOUNCER_USER_SWITCHER); + return getContext().getResources().getBoolean(R.bool.config_enableBouncerUserSwitcher); } private void configureMode() { diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 28f48ce1e647..84708a49f469 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -17,17 +17,12 @@ package com.android.systemui.communal.ui.viewmodel import android.content.ComponentName -import android.os.PowerManager -import android.os.SystemClock -import android.view.MotionEvent import android.widget.RemoteViews import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.media.controls.ui.MediaHost -import com.android.systemui.shade.ShadeViewController -import javax.inject.Provider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOf @@ -35,8 +30,6 @@ import kotlinx.coroutines.flow.flowOf /** The base view model for the communal hub. */ abstract class BaseCommunalViewModel( private val communalInteractor: CommunalInteractor, - private val shadeViewController: Provider<ShadeViewController>, - private val powerManager: PowerManager, val mediaHost: MediaHost, ) { val isKeyguardVisible: Flow<Boolean> = communalInteractor.isKeyguardVisible @@ -71,26 +64,6 @@ abstract class BaseCommunalViewModel( return true } - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block - // touches anymore. - /** Called when a touch is received outside the edge swipe area when hub mode is closed. */ - fun onOuterTouch(motionEvent: MotionEvent) { - // Forward the touch to the shade so that basic gestures like swipe up/down for - // shade/bouncer work. - shadeViewController.get().handleExternalTouch(motionEvent) - } - - // TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block - // touches anymore. - /** Called to refresh the screen timeout when a user touch is received. */ - fun onUserActivity() { - powerManager.userActivity( - SystemClock.uptimeMillis(), - PowerManager.USER_ACTIVITY_EVENT_TOUCH, - 0 - ) - } - /** A list of all the communal content to be displayed in the communal hub. */ abstract val communalContent: Flow<List<CommunalContentModel>> diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index 0cbf3f1312e2..7faf653cc177 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -23,7 +23,6 @@ import android.app.ActivityOptions import android.appwidget.AppWidgetHost import android.content.ActivityNotFoundException import android.content.ComponentName -import android.os.PowerManager import android.widget.RemoteViews import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.domain.interactor.CommunalInteractor @@ -32,11 +31,9 @@ import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.dagger.MediaModule -import com.android.systemui.shade.ShadeViewController import com.android.systemui.util.nullableAtomicReference import javax.inject.Inject import javax.inject.Named -import javax.inject.Provider import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -49,11 +46,9 @@ class CommunalEditModeViewModel constructor( private val communalInteractor: CommunalInteractor, private val appWidgetHost: AppWidgetHost, - shadeViewController: Provider<ShadeViewController>, - powerManager: PowerManager, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, private val uiEventLogger: UiEventLogger, -) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) { +) : BaseCommunalViewModel(communalInteractor, mediaHost) { private companion object { private const val KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle" diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 1c696851bb70..7a96fabd2433 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.communal.ui.viewmodel -import android.os.PowerManager import android.widget.RemoteViews import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor @@ -26,10 +25,8 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.controls.ui.MediaHost import com.android.systemui.media.dagger.MediaModule -import com.android.systemui.shade.ShadeViewController import javax.inject.Inject import javax.inject.Named -import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job @@ -51,10 +48,8 @@ constructor( private val communalInteractor: CommunalInteractor, private val interactionHandler: WidgetInteractionHandler, tutorialInteractor: CommunalTutorialInteractor, - shadeViewController: Provider<ShadeViewController>, - powerManager: PowerManager, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, -) : BaseCommunalViewModel(communalInteractor, shadeViewController, powerManager, mediaHost) { +) : BaseCommunalViewModel(communalInteractor, mediaHost) { @OptIn(ExperimentalCoroutinesApi::class) override val communalContent: Flow<List<CommunalContentModel>> = tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode -> diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 699532cbfca3..89a983ba5d90 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -122,11 +122,6 @@ object Flags { val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag("new_unlock_swipe_animation") val CHARGING_RIPPLE = resourceBooleanFlag(R.bool.flag_charging_ripple, "charging_ripple") - // TODO(b/254512281): Tracking Bug - @JvmField - val BOUNCER_USER_SWITCHER = - resourceBooleanFlag(R.bool.config_enableBouncerUserSwitcher, "bouncer_user_switcher") - // TODO(b/254512676): Tracking Bug @JvmField val LOCKSCREEN_CUSTOM_CLOCKS = @@ -377,9 +372,6 @@ object Flags { // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging") - // TODO(b/254512758): Tracking Bug - @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag("rounded_box_ripple") - // TODO(b/273509374): Tracking Bug @JvmField val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 52f2759fe63d..d7a2aa041ffc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -18,7 +18,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.sample import com.android.wm.shell.animation.Interpolators import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine @@ -39,13 +41,18 @@ class FromAlternateBouncerTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val powerInteractor: PowerInteractor, ) : TransitionInteractor( fromState = KeyguardState.ALTERNATE_BOUNCER, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -65,7 +72,7 @@ constructor( .sample( combine( keyguardInteractor.primaryBouncerShowing, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, powerInteractor.isAwake, keyguardInteractor.isAodAvailable, ::toQuad @@ -102,20 +109,19 @@ constructor( private fun listenForAlternateBouncerToGone() { scope.launch { - keyguardInteractor.isKeyguardGoingAway - .sample(transitionInteractor.finishedKeyguardState, ::Pair) - .collect { (isKeyguardGoingAway, keyguardState) -> - if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) { - startTransitionTo(KeyguardState.GONE) - } + keyguardInteractor.isKeyguardGoingAway.sample(finishedKeyguardState, ::Pair).collect { + (isKeyguardGoingAway, keyguardState) -> + if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) { + startTransitionTo(KeyguardState.GONE) } + } } } private fun listenForAlternateBouncerToPrimaryBouncer() { scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isPrimaryBouncerShowing, startedKeyguardState) -> if ( isPrimaryBouncerShowing && @@ -139,6 +145,7 @@ constructor( } companion object { + const val TAG = "FromAlternateBouncerTransitionInteractor" val TRANSITION_DURATION_MS = 300.milliseconds val TO_GONE_DURATION = 500.milliseconds val TO_AOD_DURATION = TRANSITION_DURATION_MS diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 858440185568..fedd63be1454 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock import com.android.systemui.keyguard.shared.model.DozeStateModel @@ -29,6 +30,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -38,12 +40,17 @@ class FromAodTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, ) : TransitionInteractor( fromState = KeyguardState.AOD, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -58,7 +65,7 @@ constructor( .dozeTransitionTo(DozeStateModel.FINISH) .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isKeyguardOccluded, ::Pair ), @@ -77,7 +84,6 @@ constructor( } else { TransitionModeOnCanceled.LAST_VALUE } - startTransitionTo( toState = toState, modeOnCanceled = modeOnCanceled, @@ -89,16 +95,13 @@ constructor( private fun listenForAodToGone() { scope.launch { - keyguardInteractor.biometricUnlockState - .sample(transitionInteractor.finishedKeyguardState, ::Pair) - .collect { pair -> - val (biometricUnlockState, keyguardState) = pair - if ( - keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState) - ) { - startTransitionTo(KeyguardState.GONE) - } + keyguardInteractor.biometricUnlockState.sample(finishedKeyguardState, ::Pair).collect { + pair -> + val (biometricUnlockState, keyguardState) = pair + if (keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)) { + startTransitionTo(KeyguardState.GONE) } + } } } override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { @@ -113,6 +116,7 @@ constructor( } companion object { + const val TAG = "FromAodTransitionInteractor" private val DEFAULT_DURATION = 500.milliseconds val TO_LOCKSCREEN_DURATION = 500.milliseconds val TO_GONE_DURATION = DEFAULT_DURATION diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index eca7088c079a..fcb7698c9bf5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock import com.android.systemui.keyguard.shared.model.KeyguardState @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -37,13 +39,18 @@ class FromDozingTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val powerInteractor: PowerInteractor, ) : TransitionInteractor( fromState = KeyguardState.DOZING, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -57,7 +64,7 @@ constructor( powerInteractor.isAwake .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isKeyguardOccluded, ::Pair ), @@ -76,7 +83,7 @@ constructor( private fun listenForDozingToGone() { scope.launch { keyguardInteractor.biometricUnlockState - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (biometricUnlockState, lastStartedTransition) -> if ( lastStartedTransition.to == KeyguardState.DOZING && @@ -96,6 +103,7 @@ constructor( } companion object { + const val TAG = "FromDozingTransitionInteractor" private val DEFAULT_DURATION = 500.milliseconds val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt index dac6ef5e6082..a6cdaa8c6761 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.DozeStateModel @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine @@ -39,12 +41,17 @@ class FromDreamingLockscreenHostedTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, ) : TransitionInteractor( fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -64,7 +71,7 @@ constructor( .sample( combine( keyguardInteractor.dozeTransitionModel, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair ), ::toTriple @@ -88,7 +95,7 @@ constructor( .sample( combine( keyguardInteractor.isKeyguardOccluded, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair, ), ::toTriple @@ -108,7 +115,7 @@ constructor( private fun listenForDreamingLockscreenHostedToPrimaryBouncer() { scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isBouncerShowing, lastStartedTransitionStep) -> if ( isBouncerShowing && @@ -123,7 +130,7 @@ constructor( private fun listenForDreamingLockscreenHostedToGone() { scope.launch { keyguardInteractor.biometricUnlockState - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (biometricUnlockState, lastStartedTransitionStep) -> if ( lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED && @@ -137,11 +144,7 @@ constructor( private fun listenForDreamingLockscreenHostedToDozing() { scope.launch { - combine( - keyguardInteractor.dozeTransitionModel, - transitionInteractor.startedKeyguardTransitionStep, - ::Pair - ) + combine(keyguardInteractor.dozeTransitionModel, startedKeyguardTransitionStep, ::Pair) .collect { (dozeTransitionModel, lastStartedTransitionStep) -> if ( dozeTransitionModel.to == DozeStateModel.DOZE && diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 7fdcf2f09bc1..13ffd6396cda 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.DozeStateModel @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -37,12 +39,17 @@ class FromDreamingTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, ) : TransitionInteractor( fromState = KeyguardState.DREAMING, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -66,7 +73,7 @@ constructor( private fun listenForDreamingToOccluded() { scope.launch { combine(keyguardInteractor.isKeyguardOccluded, keyguardInteractor.isDreaming, ::Pair) - .sample(transitionInteractor.startedKeyguardTransitionStep, ::toTriple) + .sample(startedKeyguardTransitionStep, ::toTriple) .collect { (isOccluded, isDreaming, lastStartedTransition) -> if ( isOccluded && @@ -82,7 +89,7 @@ constructor( private fun listenForDreamingToGone() { scope.launch { keyguardInteractor.biometricUnlockState - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (biometricUnlockState, lastStartedTransitionStep) -> if ( lastStartedTransitionStep.to == KeyguardState.DREAMING && @@ -96,11 +103,7 @@ constructor( private fun listenForDreamingToAodOrDozing() { scope.launch { - combine( - keyguardInteractor.dozeTransitionModel, - transitionInteractor.finishedKeyguardState, - ::Pair - ) + combine(keyguardInteractor.dozeTransitionModel, finishedKeyguardState, ::Pair) .collect { (dozeTransitionModel, keyguardState) -> if (keyguardState == KeyguardState.DREAMING) { if (dozeTransitionModel.to == DozeStateModel.DOZE) { @@ -123,6 +126,7 @@ constructor( } companion object { + const val TAG = "FromDreamingTransitionInteractor" private val DEFAULT_DURATION = 500.milliseconds val TO_LOCKSCREEN_DURATION = 1167.milliseconds } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index 70c2e6d56ca3..19fd7f9168e7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -20,18 +20,29 @@ import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher @SysUISingleton class FromGlanceableHubTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, -) : TransitionInteractor(fromState = KeyguardState.GLANCEABLE_HUB) { + transitionInteractor: KeyguardTransitionInteractor, + @Main mainDispatcher: CoroutineDispatcher, + @Background bgDispatcher: CoroutineDispatcher, +) : + TransitionInteractor( + fromState = KeyguardState.GLANCEABLE_HUB, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, + ) { override fun start() { if (!Flags.communalHub()) { return diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index 62a0b0ebc08c..742790eeaedb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled @@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -37,13 +39,18 @@ class FromGoneTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val powerInteractor: PowerInteractor, ) : TransitionInteractor( fromState = KeyguardState.GONE, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -57,7 +64,7 @@ constructor( private fun listenForGoneToLockscreen() { scope.launch { keyguardInteractor.isKeyguardShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isKeyguardShowing, lastStartedStep) -> if (isKeyguardShowing && lastStartedStep.to == KeyguardState.GONE) { startTransitionTo(KeyguardState.LOCKSCREEN) @@ -69,7 +76,7 @@ constructor( private fun listenForGoneToDreamingLockscreenHosted() { scope.launch { keyguardInteractor.isActiveDreamLockscreenHosted - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isActiveDreamLockscreenHosted, lastStartedStep) -> if (isActiveDreamLockscreenHosted && lastStartedStep.to == KeyguardState.GONE) { startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) @@ -83,7 +90,7 @@ constructor( keyguardInteractor.isAbleToDream .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair ), @@ -106,7 +113,7 @@ constructor( powerInteractor.isAsleep .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isAodAvailable, ::Pair ), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index cecc6537e16e..2d0baa8be1b4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -18,9 +18,9 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository @@ -39,20 +39,25 @@ import dagger.Lazy import java.util.UUID import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext @SysUISingleton class FromLockscreenTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, @@ -61,6 +66,9 @@ constructor( ) : TransitionInteractor( fromState = KeyguardState.LOCKSCREEN, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -147,12 +155,12 @@ constructor( private fun listenForLockscreenToDreaming() { val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING) - scope.launch("$TAG#listenForLockscreenToDreaming") { + scope.launch { keyguardInteractor.isAbleToDream .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, - transitionInteractor.finishedKeyguardState, + startedKeyguardTransitionStep, + finishedKeyguardState, keyguardInteractor.isActiveDreamLockscreenHosted, ::Triple ), @@ -180,9 +188,9 @@ constructor( } private fun listenForLockscreenToPrimaryBouncer() { - scope.launch("$TAG#listenForLockscreenToPrimaryBouncer") { + scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isBouncerShowing, lastStartedTransitionStep) = pair if ( @@ -195,9 +203,9 @@ constructor( } private fun listenForLockscreenToAlternateBouncer() { - scope.launch("$TAG#listenForLockscreenToAlternateBouncer") { + scope.launch { keyguardInteractor.alternateBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isAlternateBouncerShowing, lastStartedTransitionStep) = pair if ( @@ -213,11 +221,11 @@ constructor( /* Starts transitions when manually dragging up the bouncer from the lockscreen. */ private fun listenForLockscreenToPrimaryBouncerDragging() { var transitionId: UUID? = null - scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") { + scope.launch { shadeRepository.legacyShadeExpansion .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.statusBarState, keyguardInteractor.isKeyguardUnlocked, ::Triple @@ -225,72 +233,74 @@ constructor( ::toQuad ) .collect { (shadeExpansion, keyguardState, statusBarState, isKeyguardUnlocked) -> - val id = transitionId - if (id != null) { - if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) { - // An existing `id` means a transition is started, and calls to - // `updateTransition` will control it until FINISHED or CANCELED - var nextState = - if (shadeExpansion == 0f) { - TransitionState.FINISHED - } else if (shadeExpansion == 1f) { - TransitionState.CANCELED - } else { - TransitionState.RUNNING + withContext(mainDispatcher) { + val id = transitionId + if (id != null) { + if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) { + // An existing `id` means a transition is started, and calls to + // `updateTransition` will control it until FINISHED or CANCELED + var nextState = + if (shadeExpansion == 0f) { + TransitionState.FINISHED + } else if (shadeExpansion == 1f) { + TransitionState.CANCELED + } else { + TransitionState.RUNNING + } + transitionRepository.updateTransition( + id, + 1f - shadeExpansion, + nextState, + ) + + if ( + nextState == TransitionState.CANCELED || + nextState == TransitionState.FINISHED + ) { + transitionId = null } - transitionRepository.updateTransition( - id, - 1f - shadeExpansion, - nextState, - ) + // If canceled, just put the state back + // TODO(b/278086361): This logic should happen in + // FromPrimaryBouncerInteractor. + if (nextState == TransitionState.CANCELED) { + transitionRepository.startTransition( + TransitionInfo( + ownerName = name, + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.LOCKSCREEN, + animator = + getDefaultAnimatorForTransitionsToState( + KeyguardState.LOCKSCREEN + ) + .apply { duration = 0 } + ) + ) + } + } + } else { + // TODO (b/251849525): Remove statusbarstate check when that state is + // integrated into KeyguardTransitionRepository if ( - nextState == TransitionState.CANCELED || - nextState == TransitionState.FINISHED + keyguardState.to == KeyguardState.LOCKSCREEN && + shadeRepository.legacyShadeTracking.value && + !isKeyguardUnlocked && + statusBarState == KEYGUARD ) { - transitionId = null - } - - // If canceled, just put the state back - // TODO(b/278086361): This logic should happen in - // FromPrimaryBouncerInteractor. - if (nextState == TransitionState.CANCELED) { - transitionRepository.startTransition( - TransitionInfo( - ownerName = name, - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.LOCKSCREEN, - animator = - getDefaultAnimatorForTransitionsToState( - KeyguardState.LOCKSCREEN - ) - .apply { duration = 0 } + transitionId = + startTransitionTo( + toState = KeyguardState.PRIMARY_BOUNCER, + animator = null, // transition will be manually controlled ) - ) } } - } else { - // TODO (b/251849525): Remove statusbarstate check when that state is - // integrated into KeyguardTransitionRepository - if ( - keyguardState.to == KeyguardState.LOCKSCREEN && - shadeRepository.legacyShadeTracking.value && - !isKeyguardUnlocked && - statusBarState == KEYGUARD - ) { - transitionId = - startTransitionTo( - toState = KeyguardState.PRIMARY_BOUNCER, - animator = null, // transition will be manually controlled - ) - } } } } } fun dismissKeyguard() { - startTransitionTo(KeyguardState.GONE) + scope.launch { startTransitionTo(KeyguardState.GONE) } } private fun listenForLockscreenToGone() { @@ -298,9 +308,9 @@ constructor( return } - scope.launch("$TAG#listenForLockscreenToGone") { + scope.launch { keyguardInteractor.isKeyguardGoingAway - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isKeyguardGoingAway, lastStartedStep) = pair if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) { @@ -315,9 +325,9 @@ constructor( return } - scope.launch("$TAG#listenForLockscreenToGoneDragging") { + scope.launch { keyguardInteractor.isKeyguardGoingAway - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { pair -> val (isKeyguardGoingAway, lastStartedStep) = pair if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) { @@ -328,23 +338,22 @@ constructor( } private fun listenForLockscreenToOccluded() { - scope.launch("$TAG#listenForLockscreenToOccluded") { - keyguardInteractor.isKeyguardOccluded - .sample(transitionInteractor.startedKeyguardState, ::Pair) - .collect { (isOccluded, keyguardState) -> - if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) { - startTransitionTo(KeyguardState.OCCLUDED) - } + scope.launch { + keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect { + (isOccluded, keyguardState) -> + if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) { + startTransitionTo(KeyguardState.OCCLUDED) } + } } } private fun listenForLockscreenToAodOrDozing() { - scope.launch("$TAG#listenForLockscreenToAodOrDozing") { + scope.launch { powerInteractor.isAsleep .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isAodAvailable, ::Pair ), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index 6a8555cb7f6b..40061f410456 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.power.domain.interactor.PowerInteractor @@ -27,6 +28,7 @@ import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -36,13 +38,18 @@ class FromOccludedTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val powerInteractor: PowerInteractor, ) : TransitionInteractor( fromState = KeyguardState.OCCLUDED, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -57,7 +64,7 @@ constructor( private fun listenForOccludedToPrimaryBouncer() { scope.launch { keyguardInteractor.primaryBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isBouncerShowing, lastStartedTransitionStep) -> if ( isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.OCCLUDED @@ -70,13 +77,12 @@ constructor( private fun listenForOccludedToDreaming() { scope.launch { - keyguardInteractor.isAbleToDream - .sample(transitionInteractor.finishedKeyguardState, ::Pair) - .collect { (isAbleToDream, keyguardState) -> - if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) { - startTransitionTo(KeyguardState.DREAMING) - } + keyguardInteractor.isAbleToDream.sample(finishedKeyguardState, ::Pair).collect { + (isAbleToDream, keyguardState) -> + if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) { + startTransitionTo(KeyguardState.DREAMING) } + } } } @@ -86,7 +92,7 @@ constructor( .sample( combine( keyguardInteractor.isKeyguardShowing, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair ), ::toTriple @@ -111,7 +117,7 @@ constructor( .sample( combine( keyguardInteractor.isKeyguardShowing, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair ), ::toTriple @@ -135,7 +141,7 @@ constructor( powerInteractor.isAsleep .sample( combine( - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isAodAvailable, ::Pair ), @@ -154,7 +160,7 @@ constructor( private fun listenForOccludedToAlternateBouncer() { scope.launch { keyguardInteractor.alternateBouncerShowing - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isAlternateBouncerShowing, lastStartedTransitionStep) -> if ( isAlternateBouncerShowing && @@ -183,6 +189,7 @@ constructor( } companion object { + const val TAG = "FromOccludedTransitionInteractor" private val DEFAULT_DURATION = 500.milliseconds val TO_LOCKSCREEN_DURATION = 933.milliseconds val TO_AOD_DURATION = DEFAULT_DURATION diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index 5f246e181c26..c62055f83517 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -19,7 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.keyguard.KeyguardSecurityModel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository @@ -35,6 +36,7 @@ import com.android.systemui.util.kotlin.sample import com.android.wm.shell.animation.Interpolators import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -47,8 +49,10 @@ class FromPrimaryBouncerTransitionInteractor @Inject constructor( override val transitionRepository: KeyguardTransitionRepository, - override val transitionInteractor: KeyguardTransitionInteractor, - @Application private val scope: CoroutineScope, + transitionInteractor: KeyguardTransitionInteractor, + @Background private val scope: CoroutineScope, + @Background bgDispatcher: CoroutineDispatcher, + @Main mainDispatcher: CoroutineDispatcher, private val keyguardInteractor: KeyguardInteractor, private val flags: FeatureFlags, private val keyguardSecurityModel: KeyguardSecurityModel, @@ -57,6 +61,9 @@ constructor( ) : TransitionInteractor( fromState = KeyguardState.PRIMARY_BOUNCER, + transitionInteractor = transitionInteractor, + mainDispatcher = mainDispatcher, + bgDispatcher = bgDispatcher, ) { override fun start() { @@ -115,7 +122,7 @@ constructor( .distinctUntilChanged() fun dismissPrimaryBouncer() { - startTransitionTo(KeyguardState.GONE) + scope.launch { startTransitionTo(KeyguardState.GONE) } } private fun listenForPrimaryBouncerToLockscreenOrOccluded() { @@ -124,7 +131,7 @@ constructor( .sample( combine( powerInteractor.isAwake, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isKeyguardOccluded, keyguardInteractor.isActiveDreamLockscreenHosted, ::toQuad @@ -158,7 +165,7 @@ constructor( .sample( combine( powerInteractor.isAsleep, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, keyguardInteractor.isAodAvailable, ::Triple ), @@ -185,7 +192,7 @@ constructor( .sample( combine( keyguardInteractor.isActiveDreamLockscreenHosted, - transitionInteractor.startedKeyguardTransitionStep, + startedKeyguardTransitionStep, ::Pair ), ::toTriple @@ -213,7 +220,7 @@ constructor( scope.launch { keyguardInteractor.isKeyguardGoingAway - .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) + .sample(startedKeyguardTransitionStep, ::Pair) .collect { (isKeyguardGoingAway, lastStartedTransitionStep) -> if ( isKeyguardGoingAway && diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index d5ac2838a2f1..5c2df4581ff0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -24,8 +24,11 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled import com.android.systemui.util.kotlin.sample import java.util.UUID +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** * Each TransitionInteractor is responsible for determining under which conditions to notify @@ -40,14 +43,25 @@ import kotlinx.coroutines.launch */ sealed class TransitionInteractor( val fromState: KeyguardState, + val transitionInteractor: KeyguardTransitionInteractor, + val mainDispatcher: CoroutineDispatcher, + val bgDispatcher: CoroutineDispatcher, ) { val name = this::class.simpleName ?: "UnknownTransitionInteractor" - abstract val transitionRepository: KeyguardTransitionRepository - abstract val transitionInteractor: KeyguardTransitionInteractor abstract fun start() - fun startTransitionTo( + /* Use background dispatcher for all [KeyguardTransitionInteractor] flows. Necessary because + * the [sample] utility internally runs a collect on the Unconfined dispatcher, resulting + * in continuations on the main thread. We don't want that for classes that inherit from this. + */ + val startedKeyguardTransitionStep = + transitionInteractor.startedKeyguardTransitionStep.flowOn(bgDispatcher) + // The following are MutableSharedFlows, and do not require flowOn + val startedKeyguardState = transitionInteractor.startedKeyguardState + val finishedKeyguardState = transitionInteractor.finishedKeyguardState + + suspend fun startTransitionTo( toState: KeyguardState, animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState), modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE @@ -67,16 +81,17 @@ sealed class TransitionInteractor( ) return null } - - return transitionRepository.startTransition( - TransitionInfo( - name, - fromState, - toState, - animator, - modeOnCanceled, + return withContext(mainDispatcher) { + transitionRepository.startTransition( + TransitionInfo( + name, + fromState, + toState, + animator, + modeOnCanceled, + ) ) - ) + } } /** This signal may come in before the occlusion signal, and can provide a custom transition */ diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt index 0385aeba32b7..523414cfddbf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt @@ -1153,7 +1153,7 @@ constructor( qsExpansion > 0.4f && onLockscreen -> LOCATION_QS onLockscreen && isSplitShadeExpanding() -> LOCATION_QS onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS - // TODO(b/308813166): revisit logic once interactions between the hub and + // TODO(b/311234666): revisit logic once interactions between the hub and // shade/keyguard state are finalized isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 3be60b74af21..782d6519468c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -17,6 +17,8 @@ package com.android.systemui.shade import android.content.Context +import android.os.PowerManager +import android.os.SystemClock import android.view.GestureDetector import android.view.MotionEvent import android.view.View @@ -44,6 +46,7 @@ constructor( private val communalViewModel: CommunalViewModel, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val shadeInteractor: ShadeInteractor, + private val powerManager: PowerManager, ) { /** The container view for the hub. This will not be initialized until [initView] is called. */ private lateinit var communalContainerView: View @@ -157,7 +160,7 @@ constructor( // If the hub is fully visible, send all touch events to it. val communalVisible = hubShowing && !hubOccluded if (communalVisible) { - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a gesture // may return false from dispatchTouchEvent. return true @@ -175,7 +178,7 @@ constructor( x >= communalContainerView.width - edgeSwipeRegionWidth if (inOpeningSwipeRegion && !hubOccluded) { isTrackingOpenGesture = true - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a // gesture may return false from dispatchTouchEvent. return true @@ -184,7 +187,7 @@ constructor( if (isUp || isCancel) { isTrackingOpenGesture = false } - communalContainerView.dispatchTouchEvent(ev) + dispatchTouchEvent(ev) // Return true regardless of dispatch result as some touches at the start of a gesture // may return false from dispatchTouchEvent. return true @@ -192,4 +195,17 @@ constructor( return false } + + /** + * Dispatches the touch event to the communal container and sends a user activity event to reset + * the screen timeout. + */ + private fun dispatchTouchEvent(ev: MotionEvent) { + communalContainerView.dispatchTouchEvent(ev) + powerManager.userActivity( + SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_TOUCH, + 0 + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 2438298f6a6e..7f8be1cc7e55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -22,6 +22,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver +import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.media.controls.ui.MediaHierarchyManager import com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll import com.android.systemui.plugins.ActivityStarter @@ -888,6 +889,9 @@ class DragDownHelper( isDraggingDown = false isTrackpadReverseScroll = false shadeRepository.setLegacyLockscreenShadeTracking(false) + if (KeyguardShadeMigrationNssl.isEnabled) { + return true + } } else { stopDragging() return false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index abc04b87f831..a30c29456b3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -69,6 +69,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; +import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.media.controls.ui.KeyguardMediaController; @@ -1957,18 +1958,34 @@ public class NotificationStackScrollLayoutController implements Dumpable { mView.dispatchDownEventToScroller(ev); } } - boolean scrollerWantsIt = false; - if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping() - && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) { - scrollerWantsIt = mView.onScrollTouch(ev); - } boolean horizontalSwipeWantsIt = false; - if (mLongPressedView == null && !mView.isBeingDragged() - && !expandingNotification - && !mView.getExpandedInThisMotion() - && !onlyScrollingInThisMotion - && !mView.getDisallowDismissInThisMotion()) { - horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + boolean scrollerWantsIt = false; + if (KeyguardShadeMigrationNssl.isEnabled()) { + // Reverse the order relative to the else statement. onScrollTouch will reset on an + // UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes. + if (mLongPressedView == null && !mView.isBeingDragged() + && !expandingNotification + && !mView.getExpandedInThisMotion() + && !onlyScrollingInThisMotion + && !mView.getDisallowDismissInThisMotion()) { + horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + } + if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping() + && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) { + scrollerWantsIt = mView.onScrollTouch(ev); + } + } else { + if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping() + && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) { + scrollerWantsIt = mView.onScrollTouch(ev); + } + if (mLongPressedView == null && !mView.isBeingDragged() + && !expandingNotification + && !mView.getExpandedInThisMotion() + && !onlyScrollingInThisMotion + && !mView.getDisallowDismissInThisMotion()) { + horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + } } // Check if we need to clear any snooze leavebehinds diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt index 8e81185d6dcf..809947d2fec7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt @@ -63,6 +63,8 @@ class FromPrimaryBouncerTransitionInteractorTest : KeyguardTransitionInteractorT transitionRepository = super.transitionRepository, transitionInteractor = super.transitionInteractor, scope = super.testScope.backgroundScope, + bgDispatcher = super.testDispatcher, + mainDispatcher = super.testDispatcher, keyguardInteractor = super.keyguardInteractor, flags = FakeFeatureFlags(), keyguardSecurityModel = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index b8a8bdf06954..e531d44d5fdd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -20,7 +20,6 @@ import android.app.StatusBarManager import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN -import com.android.keyguard.TestScopeProvider import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.flags.FakeFeatureFlags @@ -51,6 +50,7 @@ import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runCurrent @@ -109,7 +109,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - testScope = TestScopeProvider.getTestScope() + val testDispatcher = StandardTestDispatcher() + testScope = TestScope(testDispatcher) keyguardRepository = FakeKeyguardRepository() bouncerRepository = FakeKeyguardBouncerRepository() @@ -139,6 +140,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -160,6 +163,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -173,6 +178,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDreamingTransitionInteractor = FromDreamingTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -182,6 +189,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDreamingLockscreenHostedTransitionInteractor = FromDreamingLockscreenHostedTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -191,6 +200,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromAodTransitionInteractor = FromAodTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -200,6 +211,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromGoneTransitionInteractor = FromGoneTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -210,6 +223,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDozingTransitionInteractor = FromDozingTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -220,6 +235,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromOccludedTransitionInteractor = FromOccludedTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, @@ -230,6 +247,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromAlternateBouncerTransitionInteractor = FromAlternateBouncerTransitionInteractor( scope = testScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, transitionRepository = transitionRepository, transitionInteractor = transitionInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index 5569ca9520e9..b7a9ea751438 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import android.os.PowerManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils @@ -43,6 +44,8 @@ import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -52,6 +55,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { @Mock private lateinit var communalViewModel: CommunalViewModel @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock private lateinit var shadeInteractor: ShadeInteractor + @Mock private lateinit var powerManager: PowerManager private lateinit var containerView: View private lateinit var testableLooper: TestableLooper @@ -76,7 +80,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { communalInteractor, communalViewModel, keyguardTransitionInteractor, - shadeInteractor + shadeInteractor, + powerManager ) testableLooper = TestableLooper.get(this) @@ -90,14 +95,14 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun isEnabled_interactorEnabled_returnsTrue() { + fun isEnabled_interactorEnabled_interceptsTouches() { communalRepository.setIsCommunalEnabled(true) assertThat(underTest.isEnabled()).isTrue() } @Test - fun isEnabled_interactorDisabled_returnsFalse() { + fun isEnabled_interactorDisabled_doesNotIntercept() { communalRepository.setIsCommunalEnabled(false) assertThat(underTest.isEnabled()).isFalse() @@ -120,7 +125,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_touchInsideGestureRegion_returnsTrue() { + fun onTouchEvent_touchInsideGestureRegion_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -131,7 +136,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_subsequentTouchesAfterGestureStart_returnsTrue() { + fun onTouchEvent_subsequentTouchesAfterGestureStart_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -146,7 +151,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test - fun onTouchEvent_communalOpen_returnsTrue() { + fun onTouchEvent_communalOpen_interceptsTouches() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -155,10 +160,12 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { // Touch events are intercepted. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue() + // User activity sent to PowerManager. + verify(powerManager).userActivity(any(), any(), any()) } @Test - fun onTouchEvent_communalAndBouncerShowing_returnsFalse() { + fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) @@ -170,10 +177,12 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { // Touch events are not intercepted. assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() + // User activity is not sent to PowerManager. + verify(powerManager, times(0)).userActivity(any(), any(), any()) } @Test - fun onTouchEvent_communalAndShadeShowing_returnsFalse() { + fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() { // Communal is open. communalRepository.setDesiredScene(CommunalSceneKey.Communal) 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 437d00ac8723..5d663d2d9dc6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -216,6 +216,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, shadeRepository, @@ -234,6 +236,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, mKeyguardSecurityModel, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index 39051eba3ad9..982787b7e9a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -251,6 +251,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, mShadeRepository, @@ -269,6 +271,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, mock(KeyguardSecurityModel.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index aed616349eb3..83590ee83bda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -79,6 +79,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope + private val testDispatcher = utils.testDispatcher private lateinit var shadeInteractor: ShadeInteractor private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor private lateinit var fromPrimaryBouncerTransitionInteractor: @@ -143,6 +144,8 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { keyguardTransitionRepository, keyguardTransitionInteractor, testScope.backgroundScope, + testDispatcher, + testDispatcher, keyguardInteractor, featureFlags, shadeRepository, @@ -162,6 +165,8 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { keyguardTransitionRepository, keyguardTransitionInteractor, testScope.backgroundScope, + testDispatcher, + testDispatcher, keyguardInteractor, featureFlags, mock(), 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 b7529a82dd3d..048120ad0b95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -444,6 +444,8 @@ public class BubblesTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, shadeRepository, @@ -462,6 +464,8 @@ public class BubblesTest extends SysuiTestCase { keyguardTransitionRepository, keyguardTransitionInteractor, mTestScope.getBackgroundScope(), + mUtils.getTestDispatcher(), + mUtils.getTestDispatcher(), keyguardInteractor, featureFlags, mock(KeyguardSecurityModel.class), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt index e24ba265e260..c2dc67319fff 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt @@ -48,6 +48,9 @@ private constructor( @get:[Provides Application] val appScope: CoroutineScope = scope.backgroundScope + @get:[Provides Background] + val bgScope: CoroutineScope = scope.backgroundScope + @Module interface Bindings { @Binds @Main fun bindMainContext(dispatcher: TestDispatcher): CoroutineContext diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt index b03d0b822161..b1a0b67d6648 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -20,6 +20,7 @@ import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.shade.data.repository.shadeRepository import dagger.Lazy @@ -30,6 +31,8 @@ val Kosmos.fromLockscreenTransitionInteractor by transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, flags = featureFlagsClassic, shadeRepository = shadeRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt index ade3e1a82297..97536e20cb0a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.user.domain.interactor.selectedUserInteractor @@ -30,6 +31,8 @@ val Kosmos.fromPrimaryBouncerTransitionInteractor by transitionRepository = keyguardTransitionRepository, transitionInteractor = keyguardTransitionInteractor, scope = applicationCoroutineScope, + bgDispatcher = testDispatcher, + mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, flags = featureFlagsClassic, keyguardSecurityModel = keyguardSecurityModel, diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp index e52cefb2d7e4..81fd8ce12f05 100644 --- a/packages/SystemUI/unfold/Android.bp +++ b/packages/SystemUI/unfold/Android.bp @@ -39,7 +39,4 @@ android_library { sdk_version: "current", min_sdk_version: "current", plugins: ["dagger2-compiler"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } diff --git a/packages/SystemUI/unfold/lint-baseline.xml b/packages/SystemUI/unfold/lint-baseline.xml deleted file mode 100644 index 449ed2e60853..000000000000 --- a/packages/SystemUI/unfold/lint-baseline.xml +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev"> -</issues> diff --git a/services/Android.bp b/services/Android.bp index 0b484f473d36..7e8333c7ba5f 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -148,9 +148,6 @@ filegroup { java_library { name: "Slogf", srcs: ["core/java/com/android/server/utils/Slogf.java"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } // merge all required services into one jar diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp index a3546716d5ca..69cc68a55108 100644 --- a/services/accessibility/Android.bp +++ b/services/accessibility/Android.bp @@ -22,6 +22,7 @@ java_library_static { lint: { error_checks: ["MissingPermissionAnnotation"], baseline_filename: "lint-baseline.xml", + }, srcs: [ ":services.accessibility-sources", @@ -50,9 +51,6 @@ java_library_static { libs: [ "androidx.annotation_annotation", ], - lint: { - baseline_filename: "lint-baseline.xml", - }, } aconfig_declarations { diff --git a/services/accessibility/lint-baseline.xml b/services/accessibility/lint-baseline.xml index 6bec8cf5f018..b808219ef9c6 100644 --- a/services/accessibility/lint-baseline.xml +++ b/services/accessibility/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -23,4 +23,4 @@ column="9"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/backup/lint-baseline.xml b/services/backup/lint-baseline.xml index 93c9390feb9c..46de2cdd7a47 100644 --- a/services/backup/lint-baseline.xml +++ b/services/backup/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -36,4 +36,4 @@ line="207"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/companion/lint-baseline.xml b/services/companion/lint-baseline.xml index 03eae3901e51..020126f75ea0 100644 --- a/services/companion/lint-baseline.xml +++ b/services/companion/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -12,4 +12,4 @@ column="14"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/core/Android.bp b/services/core/Android.bp index a3fc3bf5ec72..a6ed498e93db 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -234,9 +234,6 @@ java_genrule { java_library { name: "services.core", static_libs: ["services.core.priorityboosted"], - lint: { - baseline_filename: "lint-baseline.xml", - }, } java_library_host { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 02f4485d5b40..a78bebd0e87b 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -5453,7 +5453,7 @@ public final class ActiveServices { // Force an immediate oomAdjUpdate, so the client app could be in the correct process state // before doing any service related transactions mAm.enqueueOomAdjTargetLocked(app); - mAm.updateOomAdjLocked(app, OOM_ADJ_REASON_START_SERVICE); + mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE); boolean created = false; try { diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index c2bc1e4f6be2..a30cdc47a461 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -62,6 +62,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -147,6 +148,15 @@ public class SoundDoseHelper { private static final int SAFE_MEDIA_VOLUME_UNINITIALIZED = -1; + // see {@link #recordToPersistedString(SoundDoseRecord)} + // this is computed conservatively to accommodate the legacy persisting of SoundDoseRecords in + // which we materialized more decimal values. + // TODO: adjust value after soaking in + private static final int MAX_RECORDS_STRING_LENGTH = 50; + private static final int MAX_SETTINGS_LENGTH = 32768; + private static final int MAX_NUMBER_OF_CACHED_RECORDS = + MAX_SETTINGS_LENGTH / MAX_RECORDS_STRING_LENGTH; + private final EventLogger mLogger = new EventLogger(AudioService.LOG_NB_EVENTS_SOUND_DOSE, "CSD updates"); @@ -923,7 +933,7 @@ public class SoundDoseHelper { Log.v(TAG, "Initializing sound dose"); try { - if (mCachedAudioDeviceCategories.size() > 0) { + if (!mCachedAudioDeviceCategories.isEmpty()) { soundDose.initCachedAudioDeviceCategories(mCachedAudioDeviceCategories.toArray( new ISoundDose.AudioDeviceCategory[0])); mCachedAudioDeviceCategories.clear(); @@ -957,6 +967,7 @@ public class SoundDoseHelper { mGlobalTimeOffsetInSecs); if (records != null) { mDoseRecords.addAll(records); + sanitizeDoseRecords_l(); } } } @@ -1176,17 +1187,35 @@ public class SoundDoseHelper { && r.duration == record.duration)) { Log.w(TAG, "Could not find cached record to remove: " + record); } - } else { + } else if (record.value > 0) { mDoseRecords.add(record); } } + sanitizeDoseRecords_l(); + mAudioHandler.sendMessageAtTime(mAudioHandler.obtainMessage(MSG_PERSIST_CSD_VALUES, /* arg1= */0, /* arg2= */0, /* obj= */null), /* delay= */0); mLogger.enqueue(SoundDoseEvent.getDoseUpdateEvent(currentCsd, totalDuration)); } + @GuardedBy("mCsdStateLock") + private void sanitizeDoseRecords_l() { + if (mDoseRecords.size() > MAX_NUMBER_OF_CACHED_RECORDS) { + int nrToRemove = MAX_NUMBER_OF_CACHED_RECORDS - mDoseRecords.size(); + Log.w(TAG, + "Removing " + nrToRemove + " records from the total of " + mDoseRecords.size()); + // Remove older elements to fit into persisted settings max length + Iterator<SoundDoseRecord> recordIterator = mDoseRecords.iterator(); + while (recordIterator.hasNext() && nrToRemove > 0) { + recordIterator.next(); + recordIterator.remove(); + --nrToRemove; + } + } + } + @SuppressWarnings("GuardedBy") // avoid limitation with intra-procedural analysis of lambdas private void onPersistSoundDoseRecords() { synchronized (mCsdStateLock) { @@ -1213,8 +1242,8 @@ public class SoundDoseHelper { long globalTimeOffsetInSecs) { return convertToGlobalTime(record.timestamp, globalTimeOffsetInSecs) + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.duration - + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.value - + PERSIST_CSD_RECORD_FIELD_SEPARATOR + record.averageMel; + + PERSIST_CSD_RECORD_FIELD_SEPARATOR + String.format("%.3f", record.value) + + PERSIST_CSD_RECORD_FIELD_SEPARATOR + String.format("%.3f", record.averageMel); } private static long convertToGlobalTime(long bootTimeInSecs, long globalTimeOffsetInSecs) { diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index d02b6f4cff53..171fbb6f8a16 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -25,6 +25,7 @@ import static com.android.server.location.gnss.GnssManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; +import android.location.GnssMeasurement; import android.location.GnssMeasurementRequest; import android.location.GnssMeasurementsEvent; import android.location.IGnssMeasurementsListener; @@ -33,6 +34,7 @@ import android.os.IBinder; import android.stats.location.LocationStatsEnums; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.server.location.gnss.GnssConfiguration.HalInterfaceVersion; import com.android.server.location.gnss.hal.GnssNative; import com.android.server.location.injector.AppOpsHelper; @@ -40,6 +42,8 @@ import com.android.server.location.injector.Injector; import com.android.server.location.injector.LocationUsageLogger; import com.android.server.location.injector.SettingsHelper; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.Collection; /** @@ -91,6 +95,9 @@ public final class GnssMeasurementsProvider extends private final LocationUsageLogger mLogger; private final GnssNative mGnssNative; + @GuardedBy("mMultiplexerLock") + private GnssMeasurementsEvent mLastGnssMeasurementsEvent; + public GnssMeasurementsProvider(Injector injector, GnssNative gnssNative) { super(injector); mAppOpsHelper = injector.getAppOpsHelper(); @@ -264,5 +271,46 @@ public final class GnssMeasurementsProvider extends return null; } }); + synchronized (mMultiplexerLock) { + mLastGnssMeasurementsEvent = event; + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + super.dump(fd, pw, args); + pw.print("last measurements="); + pw.println(getLastMeasurementEventSummary()); + } + + /** + * Returns a string of GnssMeasurementsEvent summary including received time, satellite count + * and average baseband C/No. + */ + private String getLastMeasurementEventSummary() { + synchronized (mMultiplexerLock) { + if (mLastGnssMeasurementsEvent == null) { + return null; + } + StringBuilder builder = new StringBuilder("["); + builder.append("elapsedRealtimeNs=").append( + mLastGnssMeasurementsEvent.getClock().getElapsedRealtimeNanos()); + builder.append(" measurementCount=").append( + mLastGnssMeasurementsEvent.getMeasurements().size()); + + float sumBasebandCn0 = 0; + int countBasebandCn0 = 0; + for (GnssMeasurement measurement : mLastGnssMeasurementsEvent.getMeasurements()) { + if (measurement.hasBasebandCn0DbHz()) { + sumBasebandCn0 += measurement.getBasebandCn0DbHz(); + countBasebandCn0++; + } + } + if (countBasebandCn0 > 0) { + builder.append(" avgBasebandCn0=").append(sumBasebandCn0 / countBasebandCn0); + } + builder.append("]"); + return builder.toString(); + } } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ad090829a2f6..542b3b06184a 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -52,6 +52,7 @@ import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYP import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IActivityManager; @@ -74,6 +75,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.content.pm.UserProperties; import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; @@ -303,7 +305,7 @@ public class LockSettingsService extends ILockSettings.Stub { private boolean mThirdPartyAppsStarted; // Current password metrics for all secured users on the device. Updated when user unlocks the - // device or changes password. Removed when user is stopped. + // device or changes password. Removed if user is stopped with its CE key evicted. @GuardedBy("this") private final SparseArray<PasswordMetrics> mUserPasswordMetrics = new SparseArray<>(); @VisibleForTesting @@ -793,13 +795,33 @@ public class LockSettingsService extends ILockSettings.Stub { } @VisibleForTesting + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.QUERY_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) void onUserStopped(int userId) { hideEncryptionNotification(new UserHandle(userId)); - // User is stopped with its CE key evicted. Restore strong auth requirement to the default - // flags after boot since stopping and restarting a user later is equivalent to rebooting - // the device. + + // Normally, CE storage is locked when a user is stopped, and restarting the user requires + // strong auth. Therefore, reset the user's strong auth flags. The exception is users that + // allow delayed locking; under some circumstances, biometric authentication is allowed to + // restart such users. Don't reset the strong auth flags for such users. + // + // TODO(b/319142556): It might make more sense to reset the strong auth flags when CE + // storage is locked, instead of when the user is stopped. This would ensure the flags get + // reset if CE storage is locked later for a user that allows delayed locking. + if (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) { + UserProperties userProperties = mUserManager.getUserProperties(UserHandle.of(userId)); + if (userProperties != null && userProperties.getAllowStoppingUserWithDelayedLocking()) { + return; + } + } int strongAuthRequired = LockPatternUtils.StrongAuthTracker.getDefaultFlags(mContext); requireStrongAuth(strongAuthRequired, userId); + + // Don't keep the password metrics in memory for a stopped user that will require strong + // auth to start again, since strong auth will make the password metrics available again. synchronized (this) { mUserPasswordMetrics.remove(userId); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7aa7b7e1bfc1..9ddc362769f6 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -215,7 +215,6 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; @@ -373,6 +372,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; +import java.time.Clock; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -2466,8 +2466,8 @@ public class NotificationManagerService extends SystemService { mMetricsLogger = new MetricsLogger(); mRankingHandler = rankingHandler; mConditionProviders = conditionProviders; - mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders, - flagResolver, new ZenModeEventLogger(mPackageManagerClient)); + mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), Clock.systemUTC(), + mConditionProviders, flagResolver, new ZenModeEventLogger(mPackageManagerClient)); mZenModeHelper.addCallback(new ZenModeHelper.Callback() { @Override public void onConfigChanged() { diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 911643b1a634..afbf08d9b77d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -117,6 +117,9 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.PrintWriter; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -130,9 +133,12 @@ public class ZenModeHelper { static final String TAG = "ZenModeHelper"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String PACKAGE_ANDROID = "android"; + // The amount of time rules instances can exist without their owning app being installed. private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72; static final int RULE_LIMIT_PER_PACKAGE = 100; + private static final Duration DELETED_RULE_KEPT_FOR = Duration.ofDays(30); private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name @@ -148,6 +154,7 @@ public class ZenModeHelper { private final Context mContext; private final H mHandler; + private final Clock mClock; private final SettingsObserver mSettingsObserver; private final AppOpsManager mAppOps; private final NotificationManager mNotificationManager; @@ -189,11 +196,13 @@ public class ZenModeHelper { private String[] mPriorityOnlyDndExemptPackages; - public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders, + public ZenModeHelper(Context context, Looper looper, Clock clock, + ConditionProviders conditionProviders, SystemUiSystemPropertiesFlags.FlagResolver flagResolver, ZenModeEventLogger zenModeEventLogger) { mContext = context; mHandler = new H(looper); + mClock = clock; addCallback(mMetrics); mAppOps = context.getSystemService(AppOpsManager.class); mNotificationManager = context.getSystemService(NotificationManager.class); @@ -452,6 +461,7 @@ public class ZenModeHelper { newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true); + rule = maybeRestoreRemovedRule(newConfig, rule, automaticZenRule, origin); newConfig.automaticRules.put(rule.id, rule); maybeReplaceDefaultRule(newConfig, automaticZenRule); @@ -463,6 +473,37 @@ public class ZenModeHelper { } } + private ZenRule maybeRestoreRemovedRule(ZenModeConfig config, ZenRule ruleToAdd, + AutomaticZenRule azrToAdd, @ConfigChangeOrigin int origin) { + if (!Flags.modesApi()) { + return ruleToAdd; + } + String deletedKey = ZenModeConfig.deletedRuleKey(ruleToAdd); + if (deletedKey == null) { + // Couldn't calculate the deletedRuleKey (condition or pkg null?). This should + // never happen for an app-provided rule because NMS validates both. + return ruleToAdd; + } + ZenRule ruleToRestore = config.deletedRules.get(deletedKey); + if (ruleToRestore == null) { + return ruleToAdd; // Cannot restore. + } + + // We have found a previous rule to maybe restore. Whether we do that or not, we don't need + // to keep it around (if not restored now, it won't be in future calls either). + config.deletedRules.remove(deletedKey); + ruleToRestore.deletionInstant = null; + + if (origin != UPDATE_ORIGIN_APP) { + return ruleToAdd; // Okay to create anew. + } + + // "Preserve" the previous rule by considering the azrToAdd an update instead. + // Only app-modifiable fields will actually be modified. + populateZenRule(ruleToRestore.pkg, azrToAdd, ruleToRestore, origin, /* isNew= */ false); + return ruleToRestore; + } + private static void maybeReplaceDefaultRule(ZenModeConfig config, AutomaticZenRule addedRule) { if (!Flags.modesApi()) { return; @@ -644,7 +685,7 @@ public class ZenModeHelper { ZenRule rule = new ZenRule(); rule.id = implicitRuleId(pkg); rule.pkg = pkg; - rule.creationTime = System.currentTimeMillis(); + rule.creationTime = mClock.millis(); Binder.withCleanCallingIdentity(() -> { try { @@ -664,7 +705,7 @@ public class ZenModeHelper { rule.condition = null; rule.conditionId = new Uri.Builder() .scheme(Condition.SCHEME) - .authority("android") + .authority(PACKAGE_ANDROID) .appendPath("implicit") .appendPath(pkg) .build(); @@ -693,7 +734,9 @@ public class ZenModeHelper { if (ruleToRemove == null) return false; if (canManageAutomaticZenRule(ruleToRemove)) { newConfig.automaticRules.remove(id); - if (ruleToRemove.getPkg() != null && !"android".equals(ruleToRemove.getPkg())) { + maybePreserveRemovedRule(newConfig, ruleToRemove, origin); + if (ruleToRemove.getPkg() != null + && !PACKAGE_ANDROID.equals(ruleToRemove.getPkg())) { for (ZenRule currRule : newConfig.automaticRules.values()) { if (currRule.getPkg() != null && currRule.getPkg().equals(ruleToRemove.getPkg())) { @@ -723,12 +766,44 @@ public class ZenModeHelper { ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i)); if (Objects.equals(rule.getPkg(), packageName) && canManageAutomaticZenRule(rule)) { newConfig.automaticRules.removeAt(i); + maybePreserveRemovedRule(newConfig, rule, origin); + } + } + // If the system is clearing all rules this means DND access is revoked or the package + // was uninstalled, so also clear the preserved-deleted rules. + if (origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI) { + for (int i = newConfig.deletedRules.size() - 1; i >= 0; i--) { + ZenRule rule = newConfig.deletedRules.get(newConfig.deletedRules.keyAt(i)); + if (Objects.equals(rule.getPkg(), packageName)) { + newConfig.deletedRules.removeAt(i); + } } } return setConfigLocked(newConfig, origin, reason, null, true, callingUid); } } + private void maybePreserveRemovedRule(ZenModeConfig config, ZenRule ruleToRemove, + @ConfigChangeOrigin int origin) { + if (!Flags.modesApi()) { + return; + } + // If an app deletes a previously customized rule, keep it around to preserve + // the user's customization when/if it's recreated later. + // We don't try to preserve system-owned rules because their conditionIds (used as + // deletedRuleKey) are not stable. This is almost moot anyway because an app cannot + // delete a system-owned rule. + if (origin == UPDATE_ORIGIN_APP && !ruleToRemove.canBeUpdatedByApp() + && !PACKAGE_ANDROID.equals(ruleToRemove.pkg)) { + String deletedKey = ZenModeConfig.deletedRuleKey(ruleToRemove); + if (deletedKey != null) { + ruleToRemove.deletionInstant = Instant.now(mClock); + // Overwrites a previously-deleted rule with the same conditionId, but that's okay. + config.deletedRules.put(deletedKey, ruleToRemove); + } + } + } + void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin, int callingUid) { ZenModeConfig newConfig; @@ -919,7 +994,7 @@ public class ZenModeHelper { // These values can always be edited by the app, so we apply changes immediately. if (isNew) { rule.id = ZenModeConfig.newRuleId(); - rule.creationTime = System.currentTimeMillis(); + rule.creationTime = mClock.millis(); rule.component = automaticZenRule.getOwner(); rule.pkg = pkg; } @@ -1379,7 +1454,7 @@ public class ZenModeHelper { boolean hasDefaultRules = config.automaticRules.containsAll( ZenModeConfig.DEFAULT_RULE_IDS); - long time = System.currentTimeMillis(); + long time = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis(); if (config.automaticRules != null && config.automaticRules.size() > 0) { for (ZenRule automaticRule : config.automaticRules.values()) { if (forRestore) { @@ -1419,6 +1494,12 @@ public class ZenModeHelper { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId); } + + if (Flags.modesApi() && forRestore) { + // Note: forBackup doesn't write deletedRules, but just in case. + config.deletedRules.clear(); + } + if (DEBUG) Log.d(TAG, reason); synchronized (mConfigLock) { setConfigLocked(config, null, @@ -1436,7 +1517,7 @@ public class ZenModeHelper { if (forBackup && mConfigs.keyAt(i) != userId) { continue; } - mConfigs.valueAt(i).writeXml(out, version); + mConfigs.valueAt(i).writeXml(out, version, forBackup); } } } @@ -1468,28 +1549,51 @@ public class ZenModeHelper { } /** - * Removes old rule instances whose owner is not installed. + * Cleans up obsolete rules: + * <ul> + * <li>Rule instances whose owner is not installed. + * <li>Deleted rules that were deleted more than 30 days ago. + * </ul> */ private void cleanUpZenRules() { - long currentTime = System.currentTimeMillis(); + Instant keptRuleThreshold = mClock.instant().minus(DELETED_RULE_KEPT_FOR); synchronized (mConfigLock) { final ZenModeConfig newConfig = mConfig.copy(); - if (newConfig.automaticRules != null) { - for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) { - ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i)); - if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) { - try { - if (rule.getPkg() != null) { - mPm.getPackageInfo(rule.getPkg(), PackageManager.MATCH_ANY_USER); - } - } catch (PackageManager.NameNotFoundException e) { - newConfig.automaticRules.removeAt(i); + + deleteRulesWithoutOwner(newConfig.automaticRules); + if (Flags.modesApi()) { + deleteRulesWithoutOwner(newConfig.deletedRules); + for (int i = newConfig.deletedRules.size() - 1; i >= 0; i--) { + ZenRule deletedRule = newConfig.deletedRules.valueAt(i); + if (deletedRule.deletionInstant == null + || deletedRule.deletionInstant.isBefore(keptRuleThreshold)) { + newConfig.deletedRules.removeAt(i); + } + } + } + + if (!newConfig.equals(mConfig)) { + setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "cleanUpZenRules", Process.SYSTEM_UID); + } + } + } + + private void deleteRulesWithoutOwner(ArrayMap<String, ZenRule> ruleList) { + long currentTime = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis(); + if (ruleList != null) { + for (int i = ruleList.size() - 1; i >= 0; i--) { + ZenRule rule = ruleList.valueAt(i); + if (RULE_INSTANCE_GRACE_PERIOD < (currentTime - rule.creationTime)) { + try { + if (rule.getPkg() != null) { + mPm.getPackageInfo(rule.getPkg(), PackageManager.MATCH_ANY_USER); } + } catch (PackageManager.NameNotFoundException e) { + ruleList.removeAt(i); } } } - setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "cleanUpZenRules", - Process.SYSTEM_UID); } } diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java deleted file mode 100644 index 4454601f2254..000000000000 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.pm; - -import static android.os.Process.THREAD_PRIORITY_BACKGROUND; - -import android.annotation.NonNull; -import android.app.BackgroundInstallControlManager; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IRemoteCallback; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.ServiceThread; - -public class BackgroundInstallControlCallbackHelper { - - @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; - @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId"; - private static final String TAG = "BackgroundInstallControlCallbackHelper"; - - private final Handler mHandler; - - BackgroundInstallControlCallbackHelper() { - HandlerThread backgroundThread = - new ServiceThread( - "BackgroundInstallControlCallbackHelperBg", - THREAD_PRIORITY_BACKGROUND, - true); - backgroundThread.start(); - mHandler = new Handler(backgroundThread.getLooper()); - } - - @NonNull @VisibleForTesting - final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); - - /** Registers callback that gets invoked upon detection of an MBA - * - * NOTE: The callback is user context agnostic and currently broadcasts to all users of other - * users app installs. This is fine because the API is for SystemServer use only. - */ - public void registerBackgroundInstallCallback(IRemoteCallback callback) { - synchronized (mCallbacks) { - mCallbacks.register(callback, null); - } - } - - /** Unregisters callback */ - public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { - synchronized (mCallbacks) { - mCallbacks.unregister(callback); - } - } - - /** - * Invokes all registered callbacks Callbacks are processed through user provided-threads and - * parameters are passed in via {@link BackgroundInstallControlManager} InstallEvent - */ - public void notifyAllCallbacks(int userId, String packageName) { - Bundle extras = new Bundle(); - extras.putCharSequence(FLAGGED_PACKAGE_NAME_KEY, packageName); - extras.putInt(FLAGGED_USER_ID_KEY, userId); - synchronized (mCallbacks) { - mHandler.post( - () -> - mCallbacks.broadcast( - callback -> { - try { - callback.sendResult(extras); - } catch (RemoteException e) { - Slog.e( - TAG, - "error detected: " + e.getLocalizedMessage(), - e); - } - })); - } - } -} diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java index 3a9dedcf2d7b..7f0aadce3143 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java @@ -16,12 +16,7 @@ package com.android.server.pm; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.QUERY_ALL_PACKAGES; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; @@ -35,7 +30,6 @@ import android.content.pm.ParceledListSlice; import android.os.Build; import android.os.Environment; import android.os.Handler; -import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.SystemClock; @@ -75,10 +69,8 @@ public class BackgroundInstallControlService extends SystemService { private static final String DISK_FILE_NAME = "states"; private static final String DISK_DIR_NAME = "bic"; - private static final String ENFORCE_PERMISSION_ERROR_MSG = - "User is not permitted to call service: "; - private static final int MAX_FOREGROUND_TIME_FRAMES_SIZE = 10; + private static final int MSG_USAGE_EVENT_RECEIVED = 0; private static final int MSG_PACKAGE_ADDED = 1; private static final int MSG_PACKAGE_REMOVED = 2; @@ -86,20 +78,19 @@ public class BackgroundInstallControlService extends SystemService { private final Context mContext; private final BinderService mBinderService; private final PackageManager mPackageManager; - // TODO migrate all internal PackageManager calls to PackageManagerInternal where possible. - // b/310983905 private final PackageManagerInternal mPackageManagerInternal; private final UsageStatsManagerInternal mUsageStatsManagerInternal; private final PermissionManagerServiceInternal mPermissionManager; private final Handler mHandler; private final File mDiskFile; + private SparseSetArray<String> mBackgroundInstalledPackages = null; - private final BackgroundInstallControlCallbackHelper mCallbackHelper; // User ID -> package name -> set of foreground time frame - private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>> - mInstallerForegroundTimeFrames = new SparseArrayMap<>(); + private final SparseArrayMap<String, + TreeSet<ForegroundTimeFrame>> mInstallerForegroundTimeFrames = + new SparseArrayMap<>(); public BackgroundInstallControlService(@NonNull Context context) { this(new InjectorImpl(context)); @@ -115,11 +106,13 @@ public class BackgroundInstallControlService extends SystemService { mHandler = new EventHandler(injector.getLooper(), this); mDiskFile = injector.getDiskFile(); mUsageStatsManagerInternal = injector.getUsageStatsManagerInternal(); - mCallbackHelper = injector.getBackgroundInstallControlCallbackHelper(); mUsageStatsManagerInternal.registerListener( (userId, event) -> - mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED, userId, 0, event) - .sendToTarget()); + mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED, + userId, + 0, + event).sendToTarget() + ); mBinderService = new BinderService(this); } @@ -133,15 +126,12 @@ public class BackgroundInstallControlService extends SystemService { @Override public ParceledListSlice<PackageInfo> getBackgroundInstalledPackages( @PackageManager.PackageInfoFlagsBits long flags, int userId) { - mService.enforceCallerQueryPackagesPermissions(); if (!Build.IS_DEBUGGABLE) { return mService.getBackgroundInstalledPackages(flags, userId); } // The debug.transparency.bg-install-apps (only works for debuggable builds) // is used to set mock list of background installed apps for testing. // The list of apps' names is delimited by ",". - // TODO: Remove after migrating test to new background install method using - // {@link BackgroundInstallControlCallbackHelperTest}.installPackage b/310983905 String propertyString = SystemProperties.get("debug.transparency.bg-install-apps"); if (TextUtils.isEmpty(propertyString)) { return mService.getBackgroundInstalledPackages(flags, userId); @@ -149,41 +139,16 @@ public class BackgroundInstallControlService extends SystemService { return mService.getMockBackgroundInstalledPackages(propertyString); } } - - @Override - public void registerBackgroundInstallCallback(IRemoteCallback callback) { - mService.enforceCallerQueryPackagesPermissions(); - mService.enforceCallerInteractCrossUserPermissions(); - mService.mCallbackHelper.registerBackgroundInstallCallback(callback); - } - - @Override - public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { - mService.enforceCallerQueryPackagesPermissions(); - mService.enforceCallerInteractCrossUserPermissions(); - mService.mCallbackHelper.unregisterBackgroundInstallCallback(callback); - } - } - - @RequiresPermission(QUERY_ALL_PACKAGES) - void enforceCallerQueryPackagesPermissions() throws SecurityException { - mContext.enforceCallingPermission(QUERY_ALL_PACKAGES, - ENFORCE_PERMISSION_ERROR_MSG + QUERY_ALL_PACKAGES); - } - - @RequiresPermission(INTERACT_ACROSS_USERS_FULL) - void enforceCallerInteractCrossUserPermissions() throws SecurityException { - mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, - ENFORCE_PERMISSION_ERROR_MSG + INTERACT_ACROSS_USERS_FULL); } @VisibleForTesting ParceledListSlice<PackageInfo> getBackgroundInstalledPackages( @PackageManager.PackageInfoFlagsBits long flags, int userId) { List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( - PackageManager.PackageInfoFlags.of(flags), userId); + PackageManager.PackageInfoFlags.of(flags), userId); initBackgroundInstalledPackages(); + ListIterator<PackageInfo> iter = packages.listIterator(); while (iter.hasNext()) { String packageName = iter.next().packageName; @@ -205,9 +170,8 @@ public class BackgroundInstallControlService extends SystemService { List<PackageInfo> mockPackages = new ArrayList<>(); for (String name : mockPackageNames) { try { - PackageInfo packageInfo = - mPackageManager.getPackageInfo( - name, PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL)); + PackageInfo packageInfo = mPackageManager.getPackageInfo(name, + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL)); mockPackages.add(packageInfo); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Package's PackageInfo not found " + name); @@ -228,16 +192,18 @@ public class BackgroundInstallControlService extends SystemService { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_USAGE_EVENT_RECEIVED: - mService.handleUsageEvent( - (UsageEvents.Event) msg.obj, msg.arg1 /* userId */); + case MSG_USAGE_EVENT_RECEIVED: { + mService.handleUsageEvent((UsageEvents.Event) msg.obj, msg.arg1 /* userId */); break; - case MSG_PACKAGE_ADDED: + } + case MSG_PACKAGE_ADDED: { mService.handlePackageAdd((String) msg.obj, msg.arg1 /* userId */); break; - case MSG_PACKAGE_REMOVED: + } + case MSG_PACKAGE_REMOVED: { mService.handlePackageRemove((String) msg.obj, msg.arg1 /* userId */); break; + } default: Slog.w(TAG, "Unknown message: " + msg.what); } @@ -247,9 +213,8 @@ public class BackgroundInstallControlService extends SystemService { void handlePackageAdd(String packageName, int userId) { ApplicationInfo appInfo = null; try { - appInfo = - mPackageManager.getApplicationInfoAsUser( - packageName, PackageManager.ApplicationInfoFlags.of(0), userId); + appInfo = mPackageManager.getApplicationInfoAsUser(packageName, + PackageManager.ApplicationInfoFlags.of(0), userId); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Package's appInfo not found " + packageName); return; @@ -268,18 +233,15 @@ public class BackgroundInstallControlService extends SystemService { // the installers without INSTALL_PACKAGES perm can't perform // the installation in background. So we can just filter out them. - if (mPermissionManager.checkPermission( - installerPackageName, - android.Manifest.permission.INSTALL_PACKAGES, - Context.DEVICE_ID_DEFAULT, - userId) - != PERMISSION_GRANTED) { + if (mPermissionManager.checkPermission(installerPackageName, + android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT, + userId) != PackageManager.PERMISSION_GRANTED) { return; } // convert up-time to current time. - final long installTimestamp = - System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp); + final long installTimestamp = System.currentTimeMillis() + - (SystemClock.uptimeMillis() - appInfo.createTimestamp); if (installedByAdb(initiatingPackageName) || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) { @@ -288,7 +250,6 @@ public class BackgroundInstallControlService extends SystemService { initBackgroundInstalledPackages(); mBackgroundInstalledPackages.add(userId, packageName); - mCallbackHelper.notifyAllCallbacks(userId, packageName); writeBackgroundInstalledPackagesToDisk(); } @@ -298,8 +259,8 @@ public class BackgroundInstallControlService extends SystemService { return PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName); } - private boolean wasForegroundInstallation( - String installerPackageName, int userId, long installTimestamp) { + private boolean wasForegroundInstallation(String installerPackageName, + int userId, long installTimestamp) { TreeSet<BackgroundInstallControlService.ForegroundTimeFrame> foregroundTimeFrames = mInstallerForegroundTimeFrames.get(userId, installerPackageName); @@ -388,12 +349,12 @@ public class BackgroundInstallControlService extends SystemService { for (int i = 0; i < mBackgroundInstalledPackages.size(); i++) { int userId = mBackgroundInstalledPackages.keyAt(i); for (String packageName : mBackgroundInstalledPackages.get(userId)) { - long token = - protoOutputStream.start( - BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); protoOutputStream.write( BackgroundInstalledPackageProto.PACKAGE_NAME, packageName); - protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, userId + 1); + protoOutputStream.write( + BackgroundInstalledPackageProto.USER_ID, userId + 1); protoOutputStream.end(token); } } @@ -426,28 +387,23 @@ public class BackgroundInstallControlService extends SystemService { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = - protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoInputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = - protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = - protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - - 1; + userId = protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) - 1; break; default: - Slog.w( - TAG, - "Undefined field in proto: " - + protoInputStream.getFieldNumber()); + Slog.w(TAG, "Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -476,12 +432,9 @@ public class BackgroundInstallControlService extends SystemService { if (mInstallerForegroundTimeFrames.contains(userId, pkgName)) { return true; } - return mPermissionManager.checkPermission( - pkgName, - android.Manifest.permission.INSTALL_PACKAGES, - Context.DEVICE_ID_DEFAULT, - userId) - == PERMISSION_GRANTED; + return mPermissionManager.checkPermission(pkgName, + android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT, + userId) == PackageManager.PERMISSION_GRANTED; } @Override @@ -495,22 +448,21 @@ public class BackgroundInstallControlService extends SystemService { publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService); } - mPackageManagerInternal.getPackageList( - new PackageManagerInternal.PackageListObserver() { - @Override - public void onPackageAdded(String packageName, int uid) { - final int userId = UserHandle.getUserId(uid); - mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName) - .sendToTarget(); - } + mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() { + @Override + public void onPackageAdded(String packageName, int uid) { + final int userId = UserHandle.getUserId(uid); + mHandler.obtainMessage(MSG_PACKAGE_ADDED, + userId, 0, packageName).sendToTarget(); + } - @Override - public void onPackageRemoved(String packageName, int uid) { - final int userId = UserHandle.getUserId(uid); - mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName) - .sendToTarget(); - } - }); + @Override + public void onPackageRemoved(String packageName, int uid) { + final int userId = UserHandle.getUserId(uid); + mHandler.obtainMessage(MSG_PACKAGE_REMOVED, + userId, 0, packageName).sendToTarget(); + } + }); } // The foreground time frame (ForegroundTimeFrame) represents the period @@ -566,7 +518,7 @@ public class BackgroundInstallControlService extends SystemService { } /** - * Dependency injector for {@link BackgroundInstallControlService}. + * Dependency injector for {@link #BackgroundInstallControlService)}. */ interface Injector { Context getContext(); @@ -582,8 +534,6 @@ public class BackgroundInstallControlService extends SystemService { Looper getLooper(); File getDiskFile(); - - BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper(); } private static final class InjectorImpl implements Injector { @@ -620,11 +570,11 @@ public class BackgroundInstallControlService extends SystemService { @Override public Looper getLooper() { - ServiceThread serviceThread = - new ServiceThread( - TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */); + ServiceThread serviceThread = new ServiceThread(TAG, + android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */); serviceThread.start(); return serviceThread.getLooper(); + } @Override @@ -633,10 +583,5 @@ public class BackgroundInstallControlService extends SystemService { File file = new File(dir, DISK_FILE_NAME); return file; } - - @Override - public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() { - return new BackgroundInstallControlCallbackHelper(); - } } } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 3adeb4b5925f..446c6293aa35 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -77,6 +77,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.LocaleList; import android.os.Looper; @@ -113,7 +114,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.infra.AndroidFuture; import com.android.internal.logging.MetricsLogger; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; @@ -485,7 +485,14 @@ public class ShortcutService extends IShortcutService.Stub { } public ShortcutService(Context context) { - this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); + this(context, getBgLooper(), /*onyForPackgeManagerApis*/ false); + } + + private static Looper getBgLooper() { + final HandlerThread handlerThread = new HandlerThread("shortcut", + android.os.Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + return handlerThread.getLooper(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index 6ce868540070..ed04e5fde024 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -34,6 +34,7 @@ public class VcnContext { @NonNull private final Looper mLooper; @NonNull private final VcnNetworkProvider mVcnNetworkProvider; @NonNull private final FeatureFlags mFeatureFlags; + @NonNull private final com.android.net.flags.FeatureFlags mCoreNetFeatureFlags; private final boolean mIsInTestMode; public VcnContext( @@ -48,6 +49,7 @@ public class VcnContext { // Auto-generated class mFeatureFlags = new FeatureFlagsImpl(); + mCoreNetFeatureFlags = new com.android.net.flags.FeatureFlagsImpl(); } @NonNull @@ -69,6 +71,14 @@ public class VcnContext { return mIsInTestMode; } + public boolean isFlagNetworkMetricMonitorEnabled() { + return mFeatureFlags.networkMetricMonitor(); + } + + public boolean isFlagIpSecTransformStateEnabled() { + return mCoreNetFeatureFlags.ipsecTransformState(); + } + @NonNull public FeatureFlags getFeatureFlags() { return mFeatureFlags; diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java new file mode 100644 index 000000000000..5f4852f77727 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn.routeselection; + +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.IpSecTransformState; +import android.net.Network; +import android.net.vcn.VcnManager; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.OutcomeReceiver; +import android.os.PowerManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.VcnContext; + +import java.util.BitSet; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * IpSecPacketLossDetector is responsible for continuously monitoring IPsec packet loss + * + * <p>When the packet loss rate surpass the threshold, IpSecPacketLossDetector will report it to the + * caller + * + * <p>IpSecPacketLossDetector will start monitoring when the network being monitored is selected AND + * an inbound IpSecTransform has been applied to this network. + * + * <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state" + */ +public class IpSecPacketLossDetector extends NetworkMetricMonitor { + private static final String TAG = IpSecPacketLossDetector.class.getSimpleName(); + + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PACKET_LOSS_UNAVALAIBLE = -1; + + // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality + // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and + // Security"). For audio and video streaming, above 10-12% packet loss is unacceptable (as per + // "ICTP-SDU: About PingER"). Thus choose 12% as a conservative default threshold to declare a + // validation failure. + private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT = 12; + + private static final int POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT = 20; + + private long mPollIpSecStateIntervalMs; + private final int mPacketLossRatePercentThreshold; + + @NonNull private final Handler mHandler; + @NonNull private final PowerManager mPowerManager; + @NonNull private final Object mCancellationToken = new Object(); + @NonNull private final PacketLossCalculator mPacketLossCalculator; + + @Nullable private IpSecTransformWrapper mInboundTransform; + @Nullable private IpSecTransformState mLastIpSecTransformState; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public IpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback, + @NonNull Dependencies deps) + throws IllegalAccessException { + super(vcnContext, network, carrierConfig, callback); + + Objects.requireNonNull(deps, "Missing deps"); + + if (!vcnContext.isFlagIpSecTransformStateEnabled()) { + // Caller error + logWtf("ipsecTransformState flag disabled"); + throw new IllegalAccessException("ipsecTransformState flag disabled"); + } + + mHandler = new Handler(getVcnContext().getLooper()); + + mPowerManager = getVcnContext().getContext().getSystemService(PowerManager.class); + + mPacketLossCalculator = deps.getPacketLossCalculator(); + + mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig); + + // Register for system broadcasts to monitor idle mode change + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + getVcnContext() + .getContext() + .registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals( + intent.getAction()) + && mPowerManager.isDeviceIdleMode()) { + mLastIpSecTransformState = null; + } + } + }, + intentFilter, + null /* broadcastPermission not required */, + mHandler); + } + + public IpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + this(vcnContext, network, carrierConfig, callback, new Dependencies()); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + public PacketLossCalculator getPacketLossCalculator() { + return new PacketLossCalculator(); + } + } + + private static long getPollIpSecStateIntervalMs( + @Nullable PersistableBundleWrapper carrierConfig) { + final int seconds; + + if (carrierConfig != null) { + seconds = + carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, + POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT); + } else { + seconds = POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT; + } + + return TimeUnit.SECONDS.toMillis(seconds); + } + + private static int getPacketLossRatePercentThreshold( + @Nullable PersistableBundleWrapper carrierConfig) { + if (carrierConfig != null) { + return carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, + IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT); + } + return IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT; + } + + @Override + protected void onSelectedUnderlyingNetworkChanged() { + if (!isSelectedUnderlyingNetwork()) { + mInboundTransform = null; + stop(); + } + + // No action when the underlying network got selected. Wait for the inbound transform to + // start the monitor + } + + @Override + public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inboundTransform) { + Objects.requireNonNull(inboundTransform, "inboundTransform is null"); + + if (Objects.equals(inboundTransform, mInboundTransform)) { + return; + } + + if (!isSelectedUnderlyingNetwork()) { + logWtf("setInboundTransform called but network not selected"); + return; + } + + // When multiple parallel inbound transforms are created, NetworkMetricMonitor will be + // enabled on the last one as a sample + mInboundTransform = inboundTransform; + start(); + } + + @Override + public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) { + // The already scheduled event will not be affected. The followup events will be scheduled + // with the new interval + mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + } + + @Override + protected void start() { + super.start(); + clearTransformStateAndPollingEvents(); + mHandler.postDelayed(new PollIpSecStateRunnable(), mCancellationToken, 0L); + } + + @Override + public void stop() { + super.stop(); + clearTransformStateAndPollingEvents(); + } + + private void clearTransformStateAndPollingEvents() { + mHandler.removeCallbacksAndEqualMessages(mCancellationToken); + mLastIpSecTransformState = null; + } + + @Override + public void close() { + super.close(); + + if (mInboundTransform != null) { + mInboundTransform.close(); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @Nullable + public IpSecTransformState getLastTransformState() { + return mLastIpSecTransformState; + } + + @VisibleForTesting(visibility = Visibility.PROTECTED) + @Nullable + public IpSecTransformWrapper getInboundTransformInternal() { + return mInboundTransform; + } + + private class PollIpSecStateRunnable implements Runnable { + @Override + public void run() { + if (!isStarted()) { + logWtf("Monitor stopped but PollIpSecStateRunnable not removed from Handler"); + return; + } + + getInboundTransformInternal() + .getIpSecTransformState( + new HandlerExecutor(mHandler), new IpSecTransformStateReceiver()); + + // Schedule for next poll + mHandler.postDelayed( + new PollIpSecStateRunnable(), mCancellationToken, mPollIpSecStateIntervalMs); + } + } + + private class IpSecTransformStateReceiver + implements OutcomeReceiver<IpSecTransformState, RuntimeException> { + @Override + public void onResult(@NonNull IpSecTransformState state) { + getVcnContext().ensureRunningOnLooperThread(); + + if (!isStarted()) { + return; + } + + onIpSecTransformStateReceived(state); + } + + @Override + public void onError(@NonNull RuntimeException error) { + getVcnContext().ensureRunningOnLooperThread(); + + // Nothing we can do here + logW("TransformStateReceiver#onError " + error.toString()); + } + } + + private void onIpSecTransformStateReceived(@NonNull IpSecTransformState state) { + if (mLastIpSecTransformState == null) { + // This is first time to poll the state + mLastIpSecTransformState = state; + return; + } + + final int packetLossRate = + mPacketLossCalculator.getPacketLossRatePercentage( + mLastIpSecTransformState, state, getLogPrefix()); + + if (packetLossRate == PACKET_LOSS_UNAVALAIBLE) { + return; + } + + final String logMsg = + "packetLossRate: " + + packetLossRate + + "% in the past " + + (state.getTimestamp() - mLastIpSecTransformState.getTimestamp()) + + "ms"; + + mLastIpSecTransformState = state; + if (packetLossRate < mPacketLossRatePercentThreshold) { + logV(logMsg); + onValidationResultReceivedInternal(false /* isFailed */); + } else { + logInfo(logMsg); + onValidationResultReceivedInternal(true /* isFailed */); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class PacketLossCalculator { + /** Calculate the packet loss rate between two timestamps */ + public int getPacketLossRatePercentage( + @NonNull IpSecTransformState oldState, + @NonNull IpSecTransformState newState, + String logPrefix) { + logVIpSecTransform("oldState", oldState, logPrefix); + logVIpSecTransform("newState", newState, logPrefix); + + final int replayWindowSize = oldState.getReplayBitmap().length * 8; + final long oldSeqHi = oldState.getRxHighestSequenceNumber(); + final long oldSeqLow = Math.max(0L, oldSeqHi - replayWindowSize + 1); + final long newSeqHi = newState.getRxHighestSequenceNumber(); + final long newSeqLow = Math.max(0L, newSeqHi - replayWindowSize + 1); + + if (oldSeqHi == newSeqHi || newSeqHi < replayWindowSize) { + // The replay window did not proceed and all packets might have been delivered out + // of order + return PACKET_LOSS_UNAVALAIBLE; + } + + // Get the expected packet count by assuming there is no packet loss. In this case, SA + // should receive all packets whose sequence numbers are smaller than the lower bound of + // the replay window AND the packets received within the window. + // When the lower bound is 0, it's not possible to tell whether packet with seqNo 0 is + // received or not. For simplicity just assume that packet is received. + final long newExpectedPktCnt = newSeqLow + getPacketCntInReplayWindow(newState); + final long oldExpectedPktCnt = oldSeqLow + getPacketCntInReplayWindow(oldState); + + final long expectedPktCntDiff = newExpectedPktCnt - oldExpectedPktCnt; + final long actualPktCntDiff = newState.getPacketCount() - oldState.getPacketCount(); + + logV( + TAG, + logPrefix + + " expectedPktCntDiff: " + + expectedPktCntDiff + + " actualPktCntDiff: " + + actualPktCntDiff); + + if (expectedPktCntDiff < 0 + || expectedPktCntDiff == 0 + || actualPktCntDiff < 0 + || actualPktCntDiff > expectedPktCntDiff) { + logWtf(TAG, "Impossible values for expectedPktCntDiff or" + " actualPktCntDiff"); + return PACKET_LOSS_UNAVALAIBLE; + } + + return 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); + } + } + + private static void logVIpSecTransform( + String transformTag, IpSecTransformState state, String logPrefix) { + final String stateString = + " seqNo: " + + state.getRxHighestSequenceNumber() + + " | pktCnt: " + + state.getPacketCount() + + " | pktCntInWindow: " + + getPacketCntInReplayWindow(state); + logV(TAG, logPrefix + " " + transformTag + stateString); + } + + /** Get the number of received packets within the replay window */ + private static long getPacketCntInReplayWindow(@NonNull IpSecTransformState state) { + return BitSet.valueOf(state.getReplayBitmap()).cardinality(); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java new file mode 100644 index 000000000000..a79f188713e1 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn.routeselection; + +import static com.android.server.VcnManagementService.LOCAL_LOG; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.IpSecTransform; +import android.net.IpSecTransformState; +import android.net.Network; +import android.os.OutcomeReceiver; +import android.util.CloseGuard; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.VcnContext; + +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * NetworkMetricMonitor is responsible for managing metric monitoring and tracking validation + * results. + * + * <p>This class is flag gated by "network_metric_monitor" + */ +public abstract class NetworkMetricMonitor implements AutoCloseable { + private static final String TAG = NetworkMetricMonitor.class.getSimpleName(); + + private static final boolean VDBG = false; // STOPSHIP: if true + + @NonNull private final CloseGuard mCloseGuard = new CloseGuard(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final Network mNetwork; + @NonNull private final NetworkMetricMonitorCallback mCallback; + + private boolean mIsSelectedUnderlyingNetwork; + private boolean mIsStarted; + private boolean mIsValidationFailed; + + protected NetworkMetricMonitor( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + if (!vcnContext.isFlagNetworkMetricMonitorEnabled()) { + // Caller error + logWtf("networkMetricMonitor flag disabled"); + throw new IllegalAccessException("networkMetricMonitor flag disabled"); + } + + mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); + mNetwork = Objects.requireNonNull(network, "Missing network"); + mCallback = Objects.requireNonNull(callback, "Missing callback"); + + mIsSelectedUnderlyingNetwork = false; + mIsStarted = false; + mIsValidationFailed = false; + } + + /** Callback to notify caller of the validation result */ + public interface NetworkMetricMonitorCallback { + /** Called when there is a validation result is ready */ + void onValidationResultReceived(); + } + + /** + * Start monitoring + * + * <p>This method might be called on a an already started monitor for updating monitor + * properties (e.g. IpSecTransform, carrier config) + * + * <p>Subclasses MUST call super.start() when overriding this method + */ + protected void start() { + mIsStarted = true; + } + + /** + * Stop monitoring + * + * <p>Subclasses MUST call super.stop() when overriding this method + */ + public void stop() { + mIsValidationFailed = false; + mIsStarted = false; + } + + /** Called by the subclasses when the validation result is ready */ + protected void onValidationResultReceivedInternal(boolean isFailed) { + mIsValidationFailed = isFailed; + mCallback.onValidationResultReceived(); + } + + /** Called when the underlying network changes to selected or unselected */ + protected abstract void onSelectedUnderlyingNetworkChanged(); + + /** + * Mark the network being monitored selected or unselected + * + * <p>Subclasses MUST call super when overriding this method + */ + public void setIsSelectedUnderlyingNetwork(boolean isSelectedUnderlyingNetwork) { + if (mIsSelectedUnderlyingNetwork == isSelectedUnderlyingNetwork) { + return; + } + + mIsSelectedUnderlyingNetwork = isSelectedUnderlyingNetwork; + onSelectedUnderlyingNetworkChanged(); + } + + /** Wrapper that allows injection for testing purposes */ + @VisibleForTesting(visibility = Visibility.PROTECTED) + public static class IpSecTransformWrapper { + @NonNull public final IpSecTransform ipSecTransform; + + public IpSecTransformWrapper(@NonNull IpSecTransform ipSecTransform) { + this.ipSecTransform = ipSecTransform; + } + + /** Poll an IpSecTransformState */ + public void getIpSecTransformState( + @NonNull Executor executor, + @NonNull OutcomeReceiver<IpSecTransformState, RuntimeException> callback) { + ipSecTransform.getIpSecTransformState(executor, callback); + } + + /** Close this instance and release the underlying resources */ + public void close() { + ipSecTransform.close(); + } + + @Override + public int hashCode() { + return Objects.hash(ipSecTransform); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IpSecTransformWrapper)) { + return false; + } + + final IpSecTransformWrapper other = (IpSecTransformWrapper) o; + + return Objects.equals(ipSecTransform, other.ipSecTransform); + } + } + + /** Set the IpSecTransform that applied to the Network being monitored */ + public void setInboundTransform(@NonNull IpSecTransform inTransform) { + setInboundTransformInternal(new IpSecTransformWrapper(inTransform)); + } + + /** + * Set the IpSecTransform that applied to the Network being monitored * + * + * <p>Subclasses MUST call super when overriding this method + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inTransform) { + // Subclasses MUST override it if they care + } + + /** Update the carrierconfig */ + public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) { + // Subclasses MUST override it if they care + } + + public boolean isValidationFailed() { + return mIsValidationFailed; + } + + public boolean isSelectedUnderlyingNetwork() { + return mIsSelectedUnderlyingNetwork; + } + + public boolean isStarted() { + return mIsStarted; + } + + @NonNull + public VcnContext getVcnContext() { + return mVcnContext; + } + + // Override methods for AutoCloseable. Subclasses MUST call super when overriding this method + @Override + public void close() { + mCloseGuard.close(); + + stop(); + } + + // Override #finalize() to use closeGuard for flagging that #close() was not called + @SuppressWarnings("Finalize") + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } finally { + super.finalize(); + } + } + + private String getClassName() { + return this.getClass().getSimpleName(); + } + + protected String getLogPrefix() { + return " [Network " + mNetwork + "] "; + } + + protected void logV(String msg) { + if (VDBG) { + Slog.v(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[VERBOSE ] " + getClassName() + getLogPrefix() + msg); + } + } + + protected void logInfo(String msg) { + Slog.i(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[INFO ] " + getClassName() + getLogPrefix() + msg); + } + + protected void logW(String msg) { + Slog.w(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[WARN ] " + getClassName() + getLogPrefix() + msg); + } + + protected void logWtf(String msg) { + Slog.wtf(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[WTF ] " + getClassName() + getLogPrefix() + msg); + } + + protected static void logV(String className, String msgWithPrefix) { + if (VDBG) { + Slog.wtf(className, msgWithPrefix); + LOCAL_LOG.log("[VERBOSE ] " + className + msgWithPrefix); + } + } + + protected static void logWtf(String className, String msgWithPrefix) { + Slog.wtf(className, msgWithPrefix); + LOCAL_LOG.log("[WTF ] " + className + msgWithPrefix); + } +} diff --git a/services/core/java/com/android/server/wearable/OWNERS b/services/core/java/com/android/server/wearable/OWNERS index 073e2d79850b..eca48b742cef 100644 --- a/services/core/java/com/android/server/wearable/OWNERS +++ b/services/core/java/com/android/server/wearable/OWNERS @@ -1,3 +1 @@ -charliewang@google.com -oni@google.com -volnov@google.com
\ No newline at end of file +include /core/java/android/app/wearable/OWNERS
\ No newline at end of file diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java index cd48f5d527c1..106be5f124a0 100644 --- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java +++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java @@ -218,7 +218,7 @@ public class WearableSensingManagerService extends PersistableBundle data, SharedMemory sharedMemory, RemoteCallback callback) { - Slog.i(TAG, "WearableSensingManagerInternal provideData."); + Slog.d(TAG, "WearableSensingManagerInternal provideData."); Objects.requireNonNull(data); Objects.requireNonNull(callback); mContext.enforceCallingOrSelfPermission( diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 9e56d7a9d785..8aaf76a165ab 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -1596,7 +1596,9 @@ class BackNavigationController { // skip commitVisibility call in setVisibility cause the activity won't visible here. // Call it again to make sure the activity could be visible while handling the pending // animation. - activity.commitVisibility(true, true); + // Do not performLayout during prepare animation, because it could cause focus window + // change. Let that happen after the BackNavigationInfo has returned to shell. + activity.commitVisibility(true, false /* performLayout */); activity.mTransitionController.mSnapshotController .mActivitySnapshotController.addOnBackPressedActivity(activity); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6033220e260d..02b3f15979ce 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -808,7 +808,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAtmService.mTaskFragmentOrganizerController.dispatchPendingEvents(); mWmService.mSyncEngine.onSurfacePlacement(); - mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml index 070bd4b1c5a9..2ccd1e4c00c7 100644 --- a/services/core/lint-baseline.xml +++ b/services/core/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 7.2.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -145,4 +145,4 @@ line="7158"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/lint-baseline.xml b/services/lint-baseline.xml index 8489c17dd878..a311d07e52fb 100644 --- a/services/lint-baseline.xml +++ b/services/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -56,4 +56,4 @@ column="13"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/print/lint-baseline.xml b/services/print/lint-baseline.xml index 1bf031a9e289..11c0cc8ea93c 100644 --- a/services/print/lint-baseline.xml +++ b/services/print/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 8.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="8.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="SimpleManualPermissionEnforcement" @@ -12,4 +12,4 @@ column="13"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp index e3954355491d..4fcdbfc21f6c 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp +++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp @@ -33,7 +33,6 @@ java_test_host { ":BackgroundInstallControlServiceTestApp", ":BackgroundInstallControlMockApp1", ":BackgroundInstallControlMockApp2", - ":BackgroundInstallControlMockApp3", ], test_suites: [ "general-tests", diff --git a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml index 031d57fbe182..1e7a78aa6f93 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml +++ b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml @@ -29,14 +29,11 @@ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="cleanup" value="true" /> <option name="push-file" - key="BackgroundInstallControlMockApp1.apk" - value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" /> + key="BackgroundInstallControlMockApp1.apk" + value="/data/local/tmp/BackgroundInstallControlMockApp1.apk" /> <option name="push-file" - key="BackgroundInstallControlMockApp2.apk" - value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" /> - <option name="push-file" - key="BackgroundInstallControlMockApp3.apk" - value="/data/local/tmp/BackgroundInstallControlMockApp3.apk" /> + key="BackgroundInstallControlMockApp2.apk" + value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" /> </target_preparer> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > diff --git a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java index 5092a4659eb9..74506076d82f 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java +++ b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java @@ -41,26 +41,17 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit private static final String MOCK_APK_FILE_1 = "BackgroundInstallControlMockApp1.apk"; private static final String MOCK_APK_FILE_2 = "BackgroundInstallControlMockApp2.apk"; - // TODO: Move the silent installs to test-app using {@link - // BackgroundInstallControlServiceTest#installPackage(String, String)} and remove deviceConfig - // branch in BICS. - // b/310983905 @Test public void testGetMockBackgroundInstalledPackages() throws Exception { - installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1); + installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1); installPackage(TEST_DATA_DIR + MOCK_APK_FILE_2); assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_1)).isNotNull(); assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNotNull(); - assertThat( - getDevice() - .setProperty( - "debug.transparency.bg-install-apps", - MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)) - .isTrue(); - runDeviceTest( - "BackgroundInstallControlServiceTest", "testGetMockBackgroundInstalledPackages"); + assertThat(getDevice().setProperty("debug.transparency.bg-install-apps", + MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)).isTrue(); + runDeviceTest("testGetMockBackgroundInstalledPackages"); assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_1)).isNull(); assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_2)).isNull(); @@ -68,30 +59,16 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNull(); } - @Test - public void testRegisterCallback() throws Exception { - runDeviceTest( - "BackgroundInstallControlServiceTest", - "testRegisterBackgroundInstallControlCallback"); - } - - @Test - public void testUnregisterCallback() throws Exception { - runDeviceTest( - "BackgroundInstallControlServiceTest", - "testUnregisterBackgroundInstallControlCallback"); - } - private void installPackage(String path) throws DeviceNotAvailableException { String cmd = "pm install -t --force-queryable " + path; CommandResult result = getDevice().executeShellV2Command(cmd); assertThat(result.getStatus() == CommandStatus.SUCCESS).isTrue(); } - private void runDeviceTest(String testName, String method) throws DeviceNotAvailableException { + private void runDeviceTest(String method) throws DeviceNotAvailableException { var options = new DeviceTestRunOptions(PACKAGE_NAME); - options.setTestClassName(PACKAGE_NAME + "." + testName); + options.setTestClassName(PACKAGE_NAME + ".BackgroundInstallControlServiceTest"); options.setTestMethodName(method); runDeviceTests(options); } -}
\ No newline at end of file +} diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml index b5b8ea0f40c7..1fa1f84cd04e 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/AndroidManifest.xml @@ -21,9 +21,6 @@ <uses-library android:name="android.test.runner" /> </application> - <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> - <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:label="APCT tests for background install control service" android:targetPackage="com.android.server.pm.test.app" /> diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java index f033fed73b27..b74e5619fd0c 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java @@ -16,256 +16,54 @@ package com.android.server.pm.test.app; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.QUERY_ALL_PACKAGES; - -import static com.android.compatibility.common.util.SystemUtil.runShellCommand; - import static com.google.common.truth.Truth.assertThat; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.IBackgroundInstallControlService; import android.content.pm.PackageInfo; -import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; -import android.os.Bundle; -import android.os.IRemoteCallback; -import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.util.Pair; +import android.os.UserHandle; -import androidx.annotation.NonNull; -import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.compatibility.common.util.ShellIdentityUtils; -import com.android.compatibility.common.util.ThrowingRunnable; - -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.FileInputStream; -import java.io.OutputStream; -import java.util.ArrayList; import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import java.util.stream.Collectors; @RunWith(AndroidJUnit4.class) public class BackgroundInstallControlServiceTest { private static final String TAG = "BackgroundInstallControlServiceTest"; - private static final String ACTION_INSTALL_COMMIT = - "com.android.server.pm.test.app.BackgroundInstallControlServiceTest" - + ".ACTION_INSTALL_COMMIT"; - private static final String MOCK_PACKAGE_NAME = "com.android.servicestests.apps.bicmockapp3"; - - private static final String TEST_DATA_DIR = "/data/local/tmp/"; - private static final String MOCK_APK_FILE = "BackgroundInstallControlMockApp3.apk"; private IBackgroundInstallControlService mIBics; @Before public void setUp() { - mIBics = - IBackgroundInstallControlService.Stub.asInterface( - ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); + mIBics = IBackgroundInstallControlService.Stub.asInterface( + ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); assertThat(mIBics).isNotNull(); } - @After - public void tearDown() { - runShellCommand("pm uninstall " + MOCK_PACKAGE_NAME); - } - @Test public void testGetMockBackgroundInstalledPackages() throws RemoteException { - ParceledListSlice<PackageInfo> slice = - ShellIdentityUtils.invokeMethodWithShellPermissions( - mIBics, - (bics) -> { - try { - return bics.getBackgroundInstalledPackages( - PackageManager.MATCH_ALL, Process.myUserHandle() - .getIdentifier()); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - }, - QUERY_ALL_PACKAGES); + ParceledListSlice<PackageInfo> slice = mIBics.getBackgroundInstalledPackages( + PackageManager.MATCH_ALL, + UserHandle.USER_ALL); assertThat(slice).isNotNull(); var packageList = slice.getList(); assertThat(packageList).isNotNull(); assertThat(packageList).hasSize(2); - var expectedPackageNames = - Set.of( - "com.android.servicestests.apps.bicmockapp1", - "com.android.servicestests.apps.bicmockapp2"); - var actualPackageNames = - packageList.stream() - .map((packageInfo) -> packageInfo.packageName) - .collect(Collectors.toSet()); + var expectedPackageNames = Set.of("com.android.servicestests.apps.bicmockapp1", + "com.android.servicestests.apps.bicmockapp2"); + var actualPackageNames = packageList.stream().map((packageInfo) -> packageInfo.packageName) + .collect(Collectors.toSet()); assertThat(actualPackageNames).containsExactlyElementsIn(expectedPackageNames); } - - @Test - public void testRegisterBackgroundInstallControlCallback() - throws Exception { - String testPackageName = "test"; - int testUserId = 1; - ArrayList<Pair<String, Integer>> sharedResource = new ArrayList<>(); - IRemoteCallback testCallback = - new IRemoteCallback.Stub() { - private final ArrayList<Pair<String, Integer>> mArray = sharedResource; - - @Override - public void sendResult(Bundle data) throws RemoteException { - mArray.add(new Pair(testPackageName, testUserId)); - } - }; - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( - mIBics, - (bics) -> { - try { - bics.registerBackgroundInstallCallback(testCallback); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - }, - QUERY_ALL_PACKAGES, - INTERACT_ACROSS_USERS_FULL); - installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME); - - assertUntil(() -> sharedResource.size() == 1, 2000); - assertThat(sharedResource.get(0).first).isEqualTo(testPackageName); - assertThat(sharedResource.get(0).second).isEqualTo(testUserId); - } - - @Test - public void testUnregisterBackgroundInstallControlCallback() { - String testValue = "test"; - ArrayList<String> sharedResource = new ArrayList<>(); - IRemoteCallback testCallback = - new IRemoteCallback.Stub() { - private final ArrayList<String> mArray = sharedResource; - - @Override - public void sendResult(Bundle data) throws RemoteException { - mArray.add(testValue); - } - }; - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( - mIBics, - (bics) -> { - try { - bics.registerBackgroundInstallCallback(testCallback); - bics.unregisterBackgroundInstallCallback(testCallback); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - }, - QUERY_ALL_PACKAGES, - INTERACT_ACROSS_USERS_FULL); - installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME); - - assertUntil(() -> sharedResource.isEmpty(), 2000); - } - - private static boolean installPackage(String apkPath, String packageName) { - Context context = InstrumentationRegistry.getInstrumentation().getContext(); - final CountDownLatch installLatch = new CountDownLatch(1); - final BroadcastReceiver installReceiver = - new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - int packageInstallStatus = - intent.getIntExtra( - PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE_INVALID); - if (packageInstallStatus == PackageInstaller.STATUS_SUCCESS) { - installLatch.countDown(); - } - } - }; - final IntentFilter intentFilter = new IntentFilter(ACTION_INSTALL_COMMIT); - context.registerReceiver(installReceiver, intentFilter, Context.RECEIVER_EXPORTED); - - PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); - PackageInstaller.SessionParams params = - new PackageInstaller.SessionParams( - PackageInstaller.SessionParams.MODE_FULL_INSTALL); - params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED); - try { - int sessionId = packageInstaller.createSession(params); - PackageInstaller.Session session = packageInstaller.openSession(sessionId); - OutputStream out = session.openWrite(packageName, 0, -1); - FileInputStream fis = new FileInputStream(apkPath); - byte[] buffer = new byte[65536]; - int size; - while ((size = fis.read(buffer)) != -1) { - out.write(buffer, 0, size); - } - session.fsync(out); - fis.close(); - out.close(); - - runWithShellPermissionIdentity( - () -> { - session.commit(createPendingIntent(context).getIntentSender()); - installLatch.await(5, TimeUnit.SECONDS); - }); - return true; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static PendingIntent createPendingIntent(Context context) { - PendingIntent pendingIntent = - PendingIntent.getBroadcast( - context, - 1, - new Intent(ACTION_INSTALL_COMMIT) - .setPackage( - BackgroundInstallControlServiceTest.class.getPackageName()), - PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE); - return pendingIntent; - } - - private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command) - throws Exception { - InstrumentationRegistry.getInstrumentation() - .getUiAutomation() - .adoptShellPermissionIdentity(); - try { - command.run(); - } finally { - InstrumentationRegistry.getInstrumentation() - .getUiAutomation() - .dropShellPermissionIdentity(); - } - } - - private static void assertUntil(Supplier<Boolean> condition, int timeoutMs) { - long endTime = System.currentTimeMillis() + timeoutMs; - while (System.currentTimeMillis() <= endTime) { - if (condition.get()) return; - try { - Thread.sleep(10); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - assertThat(condition.get()).isTrue(); - } } diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp index 39b0ff782b72..7804f4ce9d02 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp +++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp @@ -50,11 +50,3 @@ android_test_helper_app { "--rename-manifest-package com.android.servicestests.apps.bicmockapp2", ], } - -android_test_helper_app { - name: "BackgroundInstallControlMockApp3", - defaults: ["bic-mock-app-defaults"], - aaptflags: [ - "--rename-manifest-package com.android.servicestests.apps.bicmockapp3", - ], -} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java deleted file mode 100644 index e1fce9b75906..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.pm; - -import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY; -import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_USER_ID_KEY; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.after; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.os.Bundle; -import android.os.IRemoteCallback; -import android.os.RemoteException; -import android.platform.test.annotations.Presubmit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.ArgumentCaptor; - -/** Unit tests for {@link BackgroundInstallControlCallbackHelperTest} */ -@Presubmit -@RunWith(JUnit4.class) -public class BackgroundInstallControlCallbackHelperTest { - - private final IRemoteCallback mCallback = - spy( - new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle extras) {} - }); - - private BackgroundInstallControlCallbackHelper mCallbackHelper; - - @Before - public void setup() { - mCallbackHelper = new BackgroundInstallControlCallbackHelper(); - } - - @Test - public void registerBackgroundInstallControlCallback_registers_successfully() { - mCallbackHelper.registerBackgroundInstallCallback(mCallback); - - synchronized (mCallbackHelper.mCallbacks) { - assertEquals(1, mCallbackHelper.mCallbacks.getRegisteredCallbackCount()); - assertEquals(mCallback, mCallbackHelper.mCallbacks.getRegisteredCallbackItem(0)); - } - } - - @Test - public void unregisterBackgroundInstallControlCallback_unregisters_successfully() { - synchronized (mCallbackHelper.mCallbacks) { - mCallbackHelper.mCallbacks.register(mCallback); - } - - mCallbackHelper.unregisterBackgroundInstallCallback(mCallback); - - synchronized (mCallbackHelper.mCallbacks) { - assertEquals(0, mCallbackHelper.mCallbacks.getRegisteredCallbackCount()); - } - } - - @Test - public void notifyAllCallbacks_broadcastsToCallbacks() - throws RemoteException { - String testPackageName = "testname"; - int testUserId = 1; - mCallbackHelper.registerBackgroundInstallCallback(mCallback); - - mCallbackHelper.notifyAllCallbacks(testUserId, testPackageName); - - ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); - verify(mCallback, after(1000).times(1)).sendResult(bundleCaptor.capture()); - Bundle receivedBundle = bundleCaptor.getValue(); - assertEquals(testPackageName, receivedBundle.getString(FLAGGED_PACKAGE_NAME_KEY)); - assertEquals(testUserId, receivedBundle.getInt(FLAGGED_USER_ID_KEY)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java index 3069d25e39d7..daf18edaf2de 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java @@ -16,10 +16,6 @@ package com.android.server.pm; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.QUERY_ALL_PACKAGES; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -31,7 +27,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -102,6 +97,7 @@ public final class BackgroundInstallControlServiceTest { private Looper mLooper; private File mFile; + @Mock private Context mContext; @Mock @@ -112,12 +108,8 @@ public final class BackgroundInstallControlServiceTest { private UsageStatsManagerInternal mUsageStatsManagerInternal; @Mock private PermissionManagerServiceInternal mPermissionManager; - @Mock - private BackgroundInstallControlCallbackHelper mCallbackHelper; - @Captor private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor; - @Captor private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor; @@ -127,12 +119,11 @@ public final class BackgroundInstallControlServiceTest { mTestLooper = new TestLooper(); mLooper = mTestLooper.getLooper(); - mFile = - new File( - InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(), - "test"); - mBackgroundInstallControlService = - new BackgroundInstallControlService(new MockInjector(mContext)); + mFile = new File( + InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(), + "test"); + mBackgroundInstallControlService = new BackgroundInstallControlService( + new MockInjector(mContext)); verify(mUsageStatsManagerInternal).registerListener(mUsageEventListenerCaptor.capture()); mUsageEventListener = mUsageEventListenerCaptor.getValue(); @@ -152,7 +143,8 @@ public final class BackgroundInstallControlServiceTest { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); mBackgroundInstallControlService.initBackgroundInstalledPackages(); assertNotNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - assertEquals(0, mBackgroundInstallControlService.getBackgroundInstalledPackages().size()); + assertEquals(0, + mBackgroundInstallControlService.getBackgroundInstalledPackages().size()); } @Test @@ -169,9 +161,12 @@ public final class BackgroundInstallControlServiceTest { // Write test data to the file on the disk. try { ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream); - long token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); - protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); + long token = protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write( + BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); + protoOutputStream.write( + BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); protoOutputStream.end(token); protoOutputStream.flush(); atomicFile.finishWrite(fileOutputStream); @@ -203,14 +198,20 @@ public final class BackgroundInstallControlServiceTest { try { ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream); - long token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); - protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); + long token = protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write( + BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_1); + protoOutputStream.write( + BackgroundInstalledPackageProto.USER_ID, USER_ID_1 + 1); protoOutputStream.end(token); - token = protoOutputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); - protoOutputStream.write(BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_2); - protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, USER_ID_2 + 1); + token = protoOutputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + protoOutputStream.write( + BackgroundInstalledPackageProto.PACKAGE_NAME, PACKAGE_NAME_2); + protoOutputStream.write( + BackgroundInstalledPackageProto.USER_ID, USER_ID_2 + 1); protoOutputStream.end(token); protoOutputStream.flush(); @@ -240,7 +241,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -248,25 +249,23 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = - protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoInputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = - protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = - protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - - 1; + userId = protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) - 1; break; default: - fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -297,7 +296,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -305,25 +304,23 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = - protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoInputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = - protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = - protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - - 1; + userId = protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) - 1; break; default: - fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -356,7 +353,7 @@ public final class BackgroundInstallControlServiceTest { // Read the file on the disk to verify var packagesInDisk = new SparseSetArray<>(); AtomicFile atomicFile = new AtomicFile(mFile); - try (FileInputStream fileInputStream = atomicFile.openRead()) { + try (FileInputStream fileInputStream = atomicFile.openRead()) { ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream); while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { @@ -364,25 +361,23 @@ public final class BackgroundInstallControlServiceTest { != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) { continue; } - long token = - protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); + long token = protoInputStream.start( + BackgroundInstalledPackagesProto.BG_INSTALLED_PKG); String packageName = null; int userId = UserHandle.USER_NULL; while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (protoInputStream.getFieldNumber()) { case (int) BackgroundInstalledPackageProto.PACKAGE_NAME: - packageName = - protoInputStream.readString( - BackgroundInstalledPackageProto.PACKAGE_NAME); + packageName = protoInputStream.readString( + BackgroundInstalledPackageProto.PACKAGE_NAME); break; case (int) BackgroundInstalledPackageProto.USER_ID: - userId = - protoInputStream.readInt( - BackgroundInstalledPackageProto.USER_ID) - - 1; + userId = protoInputStream.readInt( + BackgroundInstalledPackageProto.USER_ID) - 1; break; default: - fail("Undefined field in proto: " + protoInputStream.getFieldNumber()); + fail("Undefined field in proto: " + + protoInputStream.getFieldNumber()); } } protoInputStream.end(token); @@ -404,55 +399,51 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_permissionDenied() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PackageManager.PERMISSION_DENIED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_DENIED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_permissionGranted() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals( - 1, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals(1, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_ignoredEvent() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent(UsageEvents.Event.USER_INTERACTION, USER_ID_1, INSTALLER_NAME_1, 0); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.USER_INTERACTION, + USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); } @Test public void testHandleUsageEvent_firstActivityResumedHalfTimeFrame() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_1); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -470,18 +461,14 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstActivityResumedOneTimeFrame() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -499,23 +486,16 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstActivityResumedOneAndHalfTimeFrame() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_3); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -537,13 +517,12 @@ public final class BackgroundInstallControlServiceTest { @Test public void testHandleUsageEvent_firstNoneActivityResumed() { - assertEquals( - 0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + assertEquals(0, + mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); var installerForegroundTimeFrames = @@ -556,26 +535,27 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedNoUsageEvent() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedNoUsageEvent() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -592,26 +572,27 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedInsideTimeFrame() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedInsideTimeFrame() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -623,16 +604,12 @@ public final class BackgroundInstallControlServiceTest { // The 2 usage events make the package adding inside a time frame. // So it's not a background install. Thus, it's null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_1); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -640,26 +617,27 @@ public final class BackgroundInstallControlServiceTest { } @Test - public void testHandleUsageEvent_packageAddedOutsideTimeFrame1() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedOutsideTimeFrame1() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -672,16 +650,12 @@ public final class BackgroundInstallControlServiceTest { // Compared to testHandleUsageEvent_packageAddedInsideTimeFrame, // it's a background install. Thus, it's not null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -691,28 +665,28 @@ public final class BackgroundInstallControlServiceTest { assertEquals(1, packages.size()); assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1)); } - @Test - public void testHandleUsageEvent_packageAddedOutsideTimeFrame2() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedOutsideTimeFrame2() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ INSTALLER_NAME_1, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ INSTALLER_NAME_1, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -726,16 +700,12 @@ public final class BackgroundInstallControlServiceTest { // Compared to testHandleUsageEvent_packageAddedInsideTimeFrame, // it's a background install. Thus, it's not null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_2, - INSTALLER_NAME_2, - USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_3); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -745,31 +715,31 @@ public final class BackgroundInstallControlServiceTest { assertEquals(1, packages.size()); assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1)); } - @Test - public void testHandleUsageEvent_packageAddedThroughAdb() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedThroughAdb() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); // This test is a duplicate of testHandleUsageEvent_packageAddedThroughAdb except the // initiatingPackageName used to be null but is now "com.android.shell". This test ensures // that the behavior is still the same for when the initiatingPackageName is null. - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ null, - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ null, + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); // b/265203007 when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -781,16 +751,12 @@ public final class BackgroundInstallControlServiceTest { // for ADB installs the initiatingPackageName used to be null, despite being detected // as a background install. Since we do not want to treat side-loaded apps as background // install getBackgroundInstalledPackages() is expected to return null - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -798,31 +764,31 @@ public final class BackgroundInstallControlServiceTest { var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages(); assertNull(packages); } - @Test - public void testHandleUsageEvent_packageAddedThroughAdb2() - throws NoSuchFieldException, PackageManager.NameNotFoundException { + public void testHandleUsageEvent_packageAddedThroughAdb2() throws + NoSuchFieldException, PackageManager.NameNotFoundException { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); // This test is a duplicate of testHandleUsageEvent_packageAddedThroughAdb except the // initiatingPackageName used to be null but is now "com.android.shell". This test ensures // that the behavior is still the same after this change. - InstallSourceInfo installSourceInfo = - new InstallSourceInfo( - /* initiatingPackageName= */ "com.android.shell", - /* initiatingPackageSigningInfo= */ null, - /* originatingPackageName= */ null, - /* installingPackageName= */ INSTALLER_NAME_1); + InstallSourceInfo installSourceInfo = new InstallSourceInfo( + /* initiatingPackageName = */ "com.android.shell", + /* initiatingPackageSigningInfo = */ null, + /* originatingPackageName = */ null, + /* installingPackageName = */ INSTALLER_NAME_1); // b/265203007 when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); ApplicationInfo appInfo = mock(ApplicationInfo.class); - when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) - .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(PACKAGE_NAME_1), + any(), + anyInt()) + ).thenReturn(appInfo); - long createTimestamp = - PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis()); - FieldSetter.setField( - appInfo, + long createTimestamp = PACKAGE_ADD_TIMESTAMP_1 + - (System.currentTimeMillis() - SystemClock.uptimeMillis()); + FieldSetter.setField(appInfo, ApplicationInfo.class.getDeclaredField("createTimestamp"), createTimestamp); @@ -834,16 +800,12 @@ public final class BackgroundInstallControlServiceTest { // for ADB installs the initiatingPackageName is com.android.shell, despite being detected // as a background install. Since we do not want to treat side-loaded apps as background // install getBackgroundInstalledPackages() is expected to return null - doReturn(PERMISSION_GRANTED) - .when(mPermissionManager) - .checkPermission(anyString(), anyString(), anyInt(), anyInt()); - generateUsageEvent( - UsageEvents.Event.ACTIVITY_RESUMED, - USER_ID_1, - INSTALLER_NAME_1, - USAGE_EVENT_TIMESTAMP_2); - generateUsageEvent( - Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); + doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( + anyString(), anyString(), anyInt(), anyInt()); + generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + generateUsageEvent(Event.ACTIVITY_STOPPED, + USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_3); mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); mTestLooper.dispatchAll(); @@ -851,7 +813,6 @@ public final class BackgroundInstallControlServiceTest { var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages(); assertNull(packages); } - @Test public void testPackageRemoved() { assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); @@ -898,7 +859,8 @@ public final class BackgroundInstallControlServiceTest { packages.add(packageInfo2); var packageInfo3 = makePackageInfo(PACKAGE_NAME_3); packages.add(packageInfo3); - doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser(any(), anyInt()); + doReturn(packages).when(mPackageManager).getInstalledPackagesAsUser( + any(), anyInt()); var resultPackages = mBackgroundInstallControlService.getBackgroundInstalledPackages(0L, USER_ID_1); @@ -908,44 +870,18 @@ public final class BackgroundInstallControlServiceTest { assertFalse(resultPackages.getList().contains(packageInfo3)); } - @Test(expected = SecurityException.class) - public void enforceCallerQueryPackagesPermissionsThrowsSecurityException() { - doThrow(new SecurityException("test")).when(mContext) - .enforceCallingPermission(eq(QUERY_ALL_PACKAGES), anyString()); - - mBackgroundInstallControlService.enforceCallerQueryPackagesPermissions(); - } - - @Test - public void enforceCallerQueryPackagesPermissionsDoesNotThrowSecurityException() { - //enforceCallerQueryPackagesPermissions do not throw - - mBackgroundInstallControlService.enforceCallerQueryPackagesPermissions(); - } - - @Test(expected = SecurityException.class) - public void enforceCallerInteractCrossUserPermissionsThrowsSecurityException() { - doThrow(new SecurityException("test")).when(mContext) - .enforceCallingPermission(eq(INTERACT_ACROSS_USERS_FULL), anyString()); - - mBackgroundInstallControlService.enforceCallerInteractCrossUserPermissions(); - } - @Test - public void enforceCallerInteractCrossUserPermissionsDoesNotThrowSecurityException() { - //enforceCallerQueryPackagesPermissions do not throw - - mBackgroundInstallControlService.enforceCallerInteractCrossUserPermissions(); - } - /** * Mock a usage event occurring. * * @param usageEventId id of a usage event - * @param userId user id of a usage event - * @param pkgName package name of a usage event - * @param timestamp timestamp of a usage event + * @param userId user id of a usage event + * @param pkgName package name of a usage event + * @param timestamp timestamp of a usage event */ - private void generateUsageEvent(int usageEventId, int userId, String pkgName, long timestamp) { + private void generateUsageEvent(int usageEventId, + int userId, + String pkgName, + long timestamp) { Event event = new Event(usageEventId, timestamp); event.mPackage = pkgName; mUsageEventListener.onUsageEvent(userId, event); @@ -999,10 +935,5 @@ public final class BackgroundInstallControlServiceTest { public File getDiskFile() { return mFile; } - - @Override - public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() { - return mCallbackHelper; - } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 53ca704b6d86..bf850cfe04db 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -44,8 +44,10 @@ import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.graphics.drawable.Icon; +import android.os.BadParcelableException; import android.os.Binder; import android.os.Build; +import android.os.DeadObjectException; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -99,6 +101,20 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { } @Test + public void testGetActiveNotifications_handlesBinderErrors() throws RemoteException { + TestListenerService service = new TestListenerService(); + INotificationManager noMan = service.getNoMan(); + when(noMan.getActiveNotificationsFromListener(any(), any(), anyInt())) + .thenThrow(new BadParcelableException("oops", new DeadObjectException(""))); + + assertNotNull(service.getActiveNotifications()); + assertNotNull(service.getActiveNotifications(NotificationListenerService.TRIM_FULL)); + assertNotNull(service.getActiveNotifications(new String[0])); + assertNull(service.getActiveNotifications( + new String[0], NotificationListenerService.TRIM_LIGHT)); + } + + @Test public void testGetActiveNotifications_preP_mapsExtraPeople() throws RemoteException { TestListenerService service = new TestListenerService(); service.attachBaseContext(mContext); 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 177d64555899..dd252f3ffd20 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -60,6 +60,7 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.time.Instant; @SmallTest @RunWith(AndroidJUnit4.class) @@ -407,6 +408,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.userModifiedFields = 16; rule.iconResName = ICON_RES_NAME; rule.triggerDescription = TRIGGER_DESC; + rule.deletionInstant = Instant.ofEpochMilli(1701790147000L); Parcel parcel = Parcel.obtain(); rule.writeToParcel(parcel, 0); @@ -432,9 +434,10 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.userModifiedFields, parceled.userModifiedFields); assertEquals(rule.triggerDescription, parceled.triggerDescription); assertEquals(rule.zenPolicy, parceled.zenPolicy); + assertEquals(rule.deletionInstant, parceled.deletionInstant); + assertEquals(rule, parceled); assertEquals(rule.hashCode(), parceled.hashCode()); - } @Test @@ -510,6 +513,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.userModifiedFields = 4; rule.iconResName = ICON_RES_NAME; rule.triggerDescription = TRIGGER_DESC; + rule.deletionInstant = Instant.ofEpochMilli(1701790147000L); ByteArrayOutputStream baos = new ByteArrayOutputStream(); writeRuleXml(rule, baos); @@ -539,6 +543,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.userModifiedFields, fromXml.userModifiedFields); assertEquals(rule.triggerDescription, fromXml.triggerDescription); assertEquals(rule.iconResName, fromXml.iconResName); + assertEquals(rule.deletionInstant, fromXml.deletionInstant); } @Test 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 7e92e427b9a4..9d7cf53e62db 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java @@ -65,18 +65,22 @@ import java.util.Set; @TestableLooper.RunWithLooper public class ZenModeDiffTest extends UiServiceTestCase { // Base set of exempt fields independent of fields that are enabled/disabled via flags. - // version is not included in the diff; manual & automatic rules have special handling + // version is not included in the diff; manual & automatic rules have special handling; + // deleted rules are not included in the diff. public static final Set<String> ZEN_MODE_CONFIG_EXEMPT_FIELDS = - Set.of("version", "manualRule", "automaticRules"); + android.app.Flags.modesApi() + ? Set.of("version", "manualRule", "automaticRules", "deletedRules") + : Set.of("version", "manualRule", "automaticRules"); // Differences for flagged fields are only generated if the flag is enabled. - // TODO: b/310620812 - Remove this exempt list when flag is inlined. + // "Metadata" fields (userModifiedFields, deletionInstant) are not compared. private static final Set<String> ZEN_RULE_EXEMPT_FIELDS = android.app.Flags.modesApi() - ? Set.of() + ? Set.of("userModifiedFields", "deletionInstant") : Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION, RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL, - RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, RuleDiff.FIELD_USER_MODIFIED_FIELDS); + RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, "userModifiedFields", + "deletionInstant"); // allowPriorityChannels is flagged by android.app.modes_api public static final Set<String> ZEN_MODE_CONFIG_FLAGGED_FIELDS = 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 0224ff35219b..9e3e336fa12f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -43,6 +43,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; import static android.provider.Settings.Global.ZEN_MODE_OFF; @@ -92,6 +93,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; +import android.Manifest; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.AppGlobals; @@ -104,6 +106,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; @@ -116,6 +119,7 @@ import android.media.VolumePolicy; import android.net.Uri; import android.os.Parcel; import android.os.Process; +import android.os.SimpleClock; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -172,12 +176,16 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; @SmallTest @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service. @@ -232,6 +240,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Mock PackageManager mPackageManager; private Resources mResources; private TestableLooper mTestableLooper; + private final TestClock mTestClock = new TestClock(); private ZenModeHelper mZenModeHelper; private ContentResolver mContentResolver; @Mock DeviceEffectsApplier mDeviceEffectsApplier; @@ -269,7 +278,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mConditionProviders.addSystemProvider(new CountdownConditionProvider()); mConditionProviders.addSystemProvider(new ScheduleConditionProvider()); mZenModeEventLogger = new ZenModeEventLoggerFake(mPackageManager); - mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(), + mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(), mTestClock, mConditionProviders, mTestFlagResolver, mZenModeEventLogger); ResolveInfo ri = new ResolveInfo(); @@ -1198,7 +1207,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void ruleUidAutomaticZenRuleRemovedUpdatesCache() throws Exception { when(mContext.checkCallingPermission(anyString())) - .thenReturn(PackageManager.PERMISSION_GRANTED); + .thenReturn(PERMISSION_GRANTED); setupZenConfig(); // one enabled automatic rule @@ -1780,7 +1789,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testDoNotUpdateModifiedDefaultAutoRule() { // mDefaultConfig is set to default config in setup by getDefaultConfigParser when(mContext.checkCallingPermission(anyString())) - .thenReturn(PackageManager.PERMISSION_GRANTED); + .thenReturn(PERMISSION_GRANTED); // shouldn't update rule that's been modified ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule(); @@ -1806,7 +1815,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testDoNotUpdateEnabledDefaultAutoRule() { // mDefaultConfig is set to default config in setup by getDefaultConfigParser when(mContext.checkCallingPermission(anyString())) - .thenReturn(PackageManager.PERMISSION_GRANTED); + .thenReturn(PERMISSION_GRANTED); // shouldn't update the rule that's enabled ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule(); @@ -1833,7 +1842,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // mDefaultConfig is set to default config in setup by getDefaultConfigParser final String defaultRuleName = "rule name test"; when(mContext.checkCallingPermission(anyString())) - .thenReturn(PackageManager.PERMISSION_GRANTED); + .thenReturn(PERMISSION_GRANTED); // will update rule that is not enabled and modified ZenModeConfig.ZenRule customDefaultRule = new ZenModeConfig.ZenRule(); @@ -4318,6 +4327,324 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + public void removeAndAddAutomaticZenRule_wasCustomized_isRestored() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + // Start with a rule. + mZenModeHelper.mConfig.automaticRules.clear(); + mTestClock.setNowMillis(1000); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build()) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).canUpdate()).isTrue(); + + // User customizes it. + AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build()) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate", + Process.SYSTEM_UID); + + // App deletes it. + mTestClock.advanceByMillis(1000); + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1); + + // App adds it again. + mTestClock.advanceByMillis(1000); + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID); + + // Verify that the rule was restored: + // - id and creation time is the same as the original one. + // - ZenPolicy is the one that the user had set. + // - rule still has the user-modified fields. + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getCreationTime()).isEqualTo(1000); // And not 3000. + assertThat(newRuleId).isEqualTo(ruleId); + assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); + assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo( + ZenPolicy.STATE_ALLOW); + assertThat(finalRule.getUserModifiedFields()).isEqualTo( + AutomaticZenRule.FIELD_INTERRUPTION_FILTER); + assertThat(finalRule.getZenPolicy().getUserModifiedFields()).isEqualTo( + ZenPolicy.FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS); + + // Also, we discarded the "deleted rule" since we already used it for restoration. + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); + } + + @Test + public void removeAndAddAutomaticZenRule_wasNotCustomized_isNotRestored() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + // Start with a single rule. + mZenModeHelper.mConfig.automaticRules.clear(); + mTestClock.setNowMillis(1000); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build()) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000); + + // App deletes it. + mTestClock.advanceByMillis(1000); + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); + + // App adds it again. + mTestClock.advanceByMillis(1000); + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID); + + // Verify that the rule was recreated. This means id and creation time are new. + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getCreationTime()).isEqualTo(3000); + assertThat(newRuleId).isNotEqualTo(ruleId); + } + + @Test + public void removeAndAddAutomaticZenRule_recreatedButNotByApp_isNotRestored() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + // Start with a single rule. + mZenModeHelper.mConfig.automaticRules.clear(); + mTestClock.setNowMillis(1000); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build()) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000); + + // User customizes it. + mTestClock.advanceByMillis(1000); + AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build()) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate", + Process.SYSTEM_UID); + + // App deletes it. + mTestClock.advanceByMillis(1000); + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1); + + // User creates it again (unusual case, but ok). + mTestClock.advanceByMillis(1000); + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_USER, "add it anew", CUSTOM_PKG_UID); + + // Verify that the rule was recreated. This means id and creation time are new, and the rule + // matches the latest data supplied to addAZR. + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getCreationTime()).isEqualTo(4000); + assertThat(newRuleId).isNotEqualTo(ruleId); + assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); + assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo( + ZenPolicy.STATE_DISALLOW); + + // Also, we discarded the "deleted rule" since we're not interested in recreating it. + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); + } + + @Test + public void removeAndAddAutomaticZenRule_removedByUser_isNotRestored() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + // Start with a single rule. + mZenModeHelper.mConfig.automaticRules.clear(); + mTestClock.setNowMillis(1000); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(false).build()) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000); + + // User customizes it. + mTestClock.advanceByMillis(1000); + AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build()) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate", + Process.SYSTEM_UID); + + // User deletes it. + mTestClock.advanceByMillis(1000); + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_USER, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); + + // App creates it again. + mTestClock.advanceByMillis(1000); + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID); + + // Verify that the rule was recreated. This means id and creation time are new. + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getCreationTime()).isEqualTo(4000); + assertThat(newRuleId).isNotEqualTo(ruleId); + } + + @Test + public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS, + PERMISSION_GRANTED); // So that canManageAZR passes although packages don't match. + mZenModeHelper.mConfig.automaticRules.clear(); + + // Start with a bunch of customized rules where conditionUris are not unique. + String id1 = mZenModeHelper.addAutomaticZenRule("pkg1", + new AutomaticZenRule.Builder("Test1", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + String id2 = mZenModeHelper.addAutomaticZenRule("pkg1", + new AutomaticZenRule.Builder("Test2", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + String id3 = mZenModeHelper.addAutomaticZenRule("pkg1", + new AutomaticZenRule.Builder("Test3", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + String id4 = mZenModeHelper.addAutomaticZenRule("pkg2", + new AutomaticZenRule.Builder("Test4", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + String id5 = mZenModeHelper.addAutomaticZenRule("pkg2", + new AutomaticZenRule.Builder("Test5", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + for (ZenRule zenRule : mZenModeHelper.mConfig.automaticRules.values()) { + zenRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER; + } + + mZenModeHelper.removeAutomaticZenRule(id1, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + mZenModeHelper.removeAutomaticZenRule(id2, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + mZenModeHelper.removeAutomaticZenRule(id3, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + mZenModeHelper.removeAutomaticZenRule(id4, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + mZenModeHelper.removeAutomaticZenRule(id5, UPDATE_ORIGIN_APP, "begone", CUSTOM_PKG_UID); + + assertThat(mZenModeHelper.mConfig.deletedRules.keySet()) + .containsExactly("pkg1|uri1", "pkg1|uri2", "pkg2|uri1"); + assertThat(mZenModeHelper.mConfig.deletedRules.values().stream().map(zr -> zr.name) + .collect(Collectors.toList())) + .containsExactly("Test1", "Test3", "Test5"); + } + + @Test + public void removeAllZenRules_preservedForRestoring() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + mZenModeHelper.mConfig.automaticRules.clear(); + + mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + new AutomaticZenRule.Builder("Test1", Uri.parse("uri1")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + new AutomaticZenRule.Builder("Test2", Uri.parse("uri2")).build(), UPDATE_ORIGIN_APP, + "add it", CUSTOM_PKG_UID); + + for (ZenRule zenRule : mZenModeHelper.mConfig.automaticRules.values()) { + zenRule.userModifiedFields = AutomaticZenRule.FIELD_INTERRUPTION_FILTER; + } + + mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), UPDATE_ORIGIN_APP, + "begone", CUSTOM_PKG_UID); + + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(2); + } + + @Test + public void removeAllZenRules_fromSystem_deletesPreservedRulesToo() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + mZenModeHelper.mConfig.automaticRules.clear(); + + // Start with deleted rules from 2 different packages. + Instant now = Instant.ofEpochMilli(1701796461000L); + ZenRule pkg1Rule = newZenRule("pkg1", now.minus(1, ChronoUnit.DAYS), now); + ZenRule pkg2Rule = newZenRule("pkg2", now.minus(2, ChronoUnit.DAYS), now); + mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg1Rule), pkg1Rule); + mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg2Rule), pkg2Rule); + + mZenModeHelper.removeAutomaticZenRules("pkg1", + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "goodbye pkg1", Process.SYSTEM_UID); + + // Preserved rules from pkg1 are gone; those from pkg2 are still there. + assertThat(mZenModeHelper.mConfig.deletedRules.values().stream().map(r -> r.pkg) + .collect(Collectors.toSet())).containsExactly("pkg2"); + } + + @Test + public void testRuleCleanup() throws Exception { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + Instant now = Instant.ofEpochMilli(1701796461000L); + Instant yesterday = now.minus(1, ChronoUnit.DAYS); + Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS); + Instant twoMonthsAgo = now.minus(60, ChronoUnit.DAYS); + mTestClock.setNowMillis(now.toEpochMilli()); + + when(mPackageManager.getPackageInfo(eq("good_pkg"), anyInt())) + .thenReturn(new PackageInfo()); + when(mPackageManager.getPackageInfo(eq("bad_pkg"), anyInt())) + .thenThrow(new PackageManager.NameNotFoundException("bad_pkg is not here")); + + // Set up a config for another user containing: + ZenModeConfig config = new ZenModeConfig(); + config.user = 42; + mZenModeHelper.mConfigs.put(42, config); + // okay rules (not deleted, package exists, with a range of creation dates). + config.automaticRules.put("ar1", newZenRule("good_pkg", now, null)); + config.automaticRules.put("ar2", newZenRule("good_pkg", yesterday, null)); + config.automaticRules.put("ar3", newZenRule("good_pkg", twoMonthsAgo, null)); + // newish rules for a missing package + config.automaticRules.put("ar4", newZenRule("bad_pkg", yesterday, null)); + // oldish rules belonging to a missing package + config.automaticRules.put("ar5", newZenRule("bad_pkg", aWeekAgo, null)); + // rules deleted recently + config.deletedRules.put("del1", newZenRule("good_pkg", twoMonthsAgo, yesterday)); + config.deletedRules.put("del2", newZenRule("good_pkg", twoMonthsAgo, aWeekAgo)); + // rules deleted a long time ago + config.deletedRules.put("del3", newZenRule("good_pkg", twoMonthsAgo, twoMonthsAgo)); + // rules for a missing package, created recently and deleted recently + config.deletedRules.put("del4", newZenRule("bad_pkg", yesterday, now)); + // rules for a missing package, created a long time ago and deleted recently + config.deletedRules.put("del5", newZenRule("bad_pkg", twoMonthsAgo, now)); + // rules for a missing package, created a long time ago and deleted a long time ago + config.deletedRules.put("del6", newZenRule("bad_pkg", twoMonthsAgo, twoMonthsAgo)); + + mZenModeHelper.onUserUnlocked(42); // copies config and cleans it up. + + assertThat(mZenModeHelper.mConfig.automaticRules.keySet()) + .containsExactly("ar1", "ar2", "ar3", "ar4"); + assertThat(mZenModeHelper.mConfig.deletedRules.keySet()) + .containsExactly("del1", "del2", "del4"); + } + + private static ZenRule newZenRule(String pkg, Instant createdAt, @Nullable Instant deletedAt) { + ZenRule rule = new ZenRule(); + rule.pkg = pkg; + rule.creationTime = createdAt.toEpochMilli(); + rule.deletionInstant = deletedAt; + // Plus stuff so that isValidAutomaticRule() passes + rule.name = "A rule from " + pkg + " created on " + createdAt; + rule.conditionId = Uri.parse(rule.name); + return rule; + } + + @Test public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); @@ -4919,4 +5246,25 @@ public class ZenModeHelperTest extends UiServiceTestCase { return parser.nextTag(); } } + + private static class TestClock extends SimpleClock { + private long mNowMillis = 441644400000L; + + private TestClock() { + super(ZoneOffset.UTC); + } + + @Override + public long millis() { + return mNowMillis; + } + + private void setNowMillis(long millis) { + mNowMillis = millis; + } + + private void advanceByMillis(long millis) { + mNowMillis += millis; + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index f5282cb492f0..9bb2da0ff70c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -3309,7 +3309,7 @@ public class ActivityRecordTests extends WindowTestsBase { // keyguard to back to the app, expect IME insets is not frozen app.mActivityRecord.commitVisibility(true, false); mDisplayContent.updateImeInputAndControlTarget(app); - mDisplayContent.mWmService.mRoot.performSurfacePlacement(); + performSurfacePlacementAndWaitForWindowAnimator(); assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); @@ -3358,7 +3358,7 @@ public class ActivityRecordTests extends WindowTestsBase { mDisplayContent.setImeLayeringTarget(app2); app2.mActivityRecord.commitVisibility(true, false); mDisplayContent.updateImeInputAndControlTarget(app2); - mDisplayContent.mWmService.mRoot.performSurfacePlacement(); + performSurfacePlacementAndWaitForWindowAnimator(); // Verify after unfreezing app2's IME insets state, we won't dispatch visible IME insets // to client if the app didn't request IME visible. @@ -3412,7 +3412,7 @@ public class ActivityRecordTests extends WindowTestsBase { // frozen until the input started. mDisplayContent.setImeLayeringTarget(app1); mDisplayContent.updateImeInputAndControlTarget(app1); - mDisplayContent.mWmService.mRoot.performSurfacePlacement(); + performSurfacePlacementAndWaitForWindowAnimator(); assertEquals(app1, mDisplayContent.getImeInputTarget()); assertFalse(activity1.mImeInsetsFrozenUntilStartInput); diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java index 8bd54731c7c2..2d3c4bbe8bdc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java @@ -28,6 +28,7 @@ import android.os.HandlerExecutor; import android.os.Looper; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; +import android.util.Log; import androidx.test.filters.MediumTest; @@ -147,6 +148,8 @@ public class SplashScreenExceptionListTest { private void setExceptionListAndWaitForCallback(String commaSeparatedList) { CountDownLatch latch = new CountDownLatch(1); mOnUpdateDeviceConfig = rawList -> { + Log.i(getClass().getSimpleName(), "updateDeviceConfig expected=" + + commaSeparatedList + " actual=" + rawList); if (commaSeparatedList.equals(rawList)) { latch.countDown(); } @@ -155,7 +158,7 @@ public class SplashScreenExceptionListTest { KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false); try { assertTrue("Timed out waiting for DeviceConfig to be updated.", - latch.await(1, TimeUnit.SECONDS)); + latch.await(5, TimeUnit.SECONDS)); } catch (InterruptedException e) { Assert.fail(e.getMessage()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 60e84b03ec89..9c421ba29796 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1054,6 +1054,19 @@ class WindowTestsBase extends SystemServiceTestsBase { } /** + * Performs surface placement and waits for WindowAnimator to complete the frame. It is used + * to execute the callbacks if the surface placement is expected to add some callbacks via + * {@link WindowAnimator#addAfterPrepareSurfacesRunnable}. + */ + void performSurfacePlacementAndWaitForWindowAnimator() { + mWm.mAnimator.ready(); + if (!mWm.mWindowPlacerLocked.isTraversalScheduled()) { + mRootWindowContainer.performSurfacePlacement(); + } + waitUntilWindowAnimatorIdle(); + } + + /** * Avoids rotating screen disturbed by some conditions. It is usually used for the default * display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions). * diff --git a/services/usb/lint-baseline.xml b/services/usb/lint-baseline.xml index c2c0a350d5ad..62a2ee56a0cf 100644 --- a/services/usb/lint-baseline.xml +++ b/services/usb/lint-baseline.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01"> <issue id="NonUserGetterCalled" @@ -12,4 +12,4 @@ column="42"/> </issue> -</issues> +</issues>
\ No newline at end of file diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 56156024fbab..a5c6d57aed82 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1315,7 +1315,7 @@ public class SubscriptionManager { * A source of phone number: the EF-MSISDN (see 3GPP TS 31.102), * or EF-MDN for CDMA (see 3GPP2 C.P0065-B), from UICC application. * - * <p>The availability and a of the number depends on the carrier. + * <p>The availability and accuracy of the number depends on the carrier. * The number may be updated by over-the-air update to UICC applications * from the carrier, or by other means with physical access to the SIM. */ @@ -1557,12 +1557,21 @@ public class SubscriptionManager { * caller can see all subscription across user profiles as it does today today even if it's * {@code false}. */ - private boolean mIsForAllUserProfiles = false; + private final boolean mIsForAllUserProfiles; /** @hide */ @UnsupportedAppUsage public SubscriptionManager(Context context) { - if (DBG) logd("SubscriptionManager created"); + this(context, false /*isForAllUserProfiles*/); + } + + /** Constructor */ + private SubscriptionManager(Context context, boolean isForAllUserProfiles) { + if (DBG) { + logd("SubscriptionManager created " + + (isForAllUserProfiles ? "for all user profile" : "")); + } + mIsForAllUserProfiles = isForAllUserProfiles; mContext = context; } @@ -1998,7 +2007,7 @@ public class SubscriptionManager { } /** - * Convert this subscription manager instance into one that can see all subscriptions across + * Create a new subscription manager instance that can see all subscriptions across * user profiles. * * @return a SubscriptionManager that can see all subscriptions regardless its user profile @@ -2008,13 +2017,12 @@ public class SubscriptionManager { * @see #getActiveSubscriptionInfoCount * @see UserHandle */ - @FlaggedApi(Flags.FLAG_WORK_PROFILE_API_SPLIT) + @FlaggedApi(Flags.FLAG_ENFORCE_SUBSCRIPTION_USER_FILTER) // @RequiresPermission(TODO(b/308809058)) // The permission check for accessing all subscriptions will be enforced upon calling the // individual APIs linked above. @NonNull public SubscriptionManager createForAllUserProfiles() { - mIsForAllUserProfiles = true; - return this; + return new SubscriptionManager(mContext, true/*isForAllUserProfiles*/); } /** diff --git a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java index fee1b25f04e5..2bc056ee743f 100644 --- a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java +++ b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java @@ -16,9 +16,6 @@ package android.transparency.test.app; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.Manifest.permission.QUERY_ALL_PACKAGES; - import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -30,7 +27,6 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.compatibility.common.util.ShellIdentityUtils; import com.android.internal.os.IBinaryTransparencyService.AppInfo; import org.junit.Before; @@ -120,13 +116,7 @@ public class BinaryTransparencyTest { @Test public void testCollectAllSilentInstalledMbaInfo() { // Action - var appInfoList = - ShellIdentityUtils.invokeMethodWithShellPermissions( - mBt, - (Bt) -> - mBt.collectAllSilentInstalledMbaInfo(new Bundle()), - QUERY_ALL_PACKAGES, - INTERACT_ACROSS_USERS_FULL); + var appInfoList = mBt.collectAllSilentInstalledMbaInfo(new Bundle()); // Verify assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java new file mode 100644 index 000000000000..9daba6a79a27 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn.routeselection; + +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY; +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY; + +import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.PACKET_LOSS_UNAVALAIBLE; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.net.IpSecTransformState; +import android.os.OutcomeReceiver; +import android.os.PowerManager; + +import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator; +import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper; +import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.concurrent.TimeUnit; + +public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { + private static final String TAG = IpSecPacketLossDetectorTest.class.getSimpleName(); + + private static final int REPLAY_BITMAP_LEN_BYTE = 512; + private static final int REPLAY_BITMAP_LEN_BIT = REPLAY_BITMAP_LEN_BYTE * 8; + private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD = 5; + private static final long POLL_IPSEC_STATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30L); + + @Mock private IpSecTransformWrapper mIpSecTransform; + @Mock private NetworkMetricMonitorCallback mMetricMonitorCallback; + @Mock private PersistableBundleWrapper mCarrierConfig; + @Mock private IpSecPacketLossDetector.Dependencies mDependencies; + @Spy private PacketLossCalculator mPacketLossCalculator = new PacketLossCalculator(); + + @Captor private ArgumentCaptor<OutcomeReceiver> mTransformStateReceiverCaptor; + @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + + private IpSecPacketLossDetector mIpSecPacketLossDetector; + private IpSecTransformState mTransformStateInitial; + + @Before + public void setUp() throws Exception { + super.setUp(); + mTransformStateInitial = newTransformState(0, 0, newReplayBitmap(0)); + + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt())) + .thenReturn((int) TimeUnit.MILLISECONDS.toSeconds(POLL_IPSEC_STATE_INTERVAL_MS)); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY), + anyInt())) + .thenReturn(IPSEC_PACKET_LOSS_PERCENT_THRESHOLD); + + when(mDependencies.getPacketLossCalculator()).thenReturn(mPacketLossCalculator); + + mIpSecPacketLossDetector = + new IpSecPacketLossDetector( + mVcnContext, + mNetwork, + mCarrierConfig, + mMetricMonitorCallback, + mDependencies); + } + + private static IpSecTransformState newTransformState( + long rxSeqNo, long packtCount, byte[] replayBitmap) { + return new IpSecTransformState.Builder() + .setRxHighestSequenceNumber(rxSeqNo) + .setPacketCount(packtCount) + .setReplayBitmap(replayBitmap) + .build(); + } + + private static byte[] newReplayBitmap(int receivedPktCnt) { + final BitSet bitSet = new BitSet(REPLAY_BITMAP_LEN_BIT); + for (int i = 0; i < receivedPktCnt; i++) { + bitSet.set(i); + } + return Arrays.copyOf(bitSet.toByteArray(), REPLAY_BITMAP_LEN_BYTE); + } + + private void verifyStopped() { + assertFalse(mIpSecPacketLossDetector.isStarted()); + assertFalse(mIpSecPacketLossDetector.isValidationFailed()); + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + + // No event scheduled + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNull(mTestLooper.nextMessage()); + } + + @Test + public void testInitialization() throws Exception { + assertFalse(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork()); + verifyStopped(); + } + + private OutcomeReceiver<IpSecTransformState, RuntimeException> + startMonitorAndCaptureStateReceiver() { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + // Trigger the runnable + mTestLooper.dispatchAll(); + + verify(mIpSecTransform) + .getIpSecTransformState(any(), mTransformStateReceiverCaptor.capture()); + return mTransformStateReceiverCaptor.getValue(); + } + + @Test + public void testStartMonitor() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertFalse(mIpSecPacketLossDetector.isValidationFailed()); + assertTrue(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork()); + assertEquals(mIpSecTransform, mIpSecPacketLossDetector.getInboundTransformInternal()); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + + // Verify the first polled state is stored + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + + // Verify next poll is scheduled + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNotNull(mTestLooper.nextMessage()); + } + + @Test + public void testStartedMonitor_enterDozeMoze() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + + // Mock entering doze mode + final Intent intent = mock(Intent.class); + when(intent.getAction()).thenReturn(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + when(mPowerManagerService.isDeviceIdleMode()).thenReturn(true); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); + final BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue(); + broadcastReceiver.onReceive(mContext, intent); + + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + } + + @Test + public void testStartedMonitor_updateInboundTransform() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + + // Update the inbound transform + final IpSecTransformWrapper newTransform = mock(IpSecTransformWrapper.class); + mIpSecPacketLossDetector.setInboundTransformInternal(newTransform); + + // Verifications + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + mTestLooper.dispatchAll(); + verify(newTransform).getIpSecTransformState(any(), any()); + } + + @Test + public void testStartedMonitor_updateCarrierConfig() throws Exception { + startMonitorAndCaptureStateReceiver(); + + final int additionalPollIntervalMs = (int) TimeUnit.SECONDS.toMillis(10L); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt())) + .thenReturn( + (int) + TimeUnit.MILLISECONDS.toSeconds( + POLL_IPSEC_STATE_INTERVAL_MS + additionalPollIntervalMs)); + mIpSecPacketLossDetector.setCarrierConfig(mCarrierConfig); + mTestLooper.dispatchAll(); + + // The already scheduled event is still fired with the old timeout + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + mTestLooper.dispatchAll(); + + // The next scheduled event will take 10 more seconds to fire + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(additionalPollIntervalMs); + assertNotNull(mTestLooper.nextMessage()); + } + + @Test + public void testStopMonitor() throws Exception { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertNotNull(mTestLooper.nextMessage()); + + // Unselect the monitor + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */); + verifyStopped(); + } + + @Test + public void testClose() throws Exception { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertNotNull(mTestLooper.nextMessage()); + + // Stop the monitor + mIpSecPacketLossDetector.close(); + verifyStopped(); + verify(mIpSecTransform).close(); + } + + @Test + public void testTransformStateReceiverOnResultWhenStopped() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + xfrmStateReceiver.onResult(mTransformStateInitial); + + // Unselect the monitor + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */); + verifyStopped(); + + xfrmStateReceiver.onResult(newTransformState(1, 1, newReplayBitmap(1))); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + } + + @Test + public void testTransformStateReceiverOnError() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + xfrmStateReceiver.onResult(mTransformStateInitial); + + xfrmStateReceiver.onError(new RuntimeException("Test")); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + } + + private void checkHandleLossRate( + int mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected) + throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + doReturn(mockPacketLossRate) + .when(mPacketLossCalculator) + .getPacketLossRatePercentage(any(), any(), anyString()); + + // Mock receiving two states with mTransformStateInitial and an arbitrary transformNew + final IpSecTransformState transformNew = newTransformState(1, 1, newReplayBitmap(1)); + xfrmStateReceiver.onResult(mTransformStateInitial); + xfrmStateReceiver.onResult(transformNew); + + // Verifications + verify(mPacketLossCalculator) + .getPacketLossRatePercentage( + eq(mTransformStateInitial), eq(transformNew), anyString()); + + if (isLastStateExpectedToUpdate) { + assertEquals(transformNew, mIpSecPacketLossDetector.getLastTransformState()); + } else { + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + } + + if (isCallbackExpected) { + verify(mMetricMonitorCallback).onValidationResultReceived(); + } else { + verify(mMetricMonitorCallback, never()).onValidationResultReceived(); + } + } + + @Test + public void testHandleLossRate_validationPass() throws Exception { + checkHandleLossRate( + 2, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_validationFail() throws Exception { + checkHandleLossRate( + 22, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_resultUnavalaible() throws Exception { + checkHandleLossRate( + PACKET_LOSS_UNAVALAIBLE, + false /* isLastStateExpectedToUpdate */, + false /* isCallbackExpected */); + } + + private void checkGetPacketLossRate( + IpSecTransformState oldState, IpSecTransformState newState, int expectedLossRate) + throws Exception { + assertEquals( + expectedLossRate, + mPacketLossCalculator.getPacketLossRatePercentage(oldState, newState, TAG)); + } + + private void checkGetPacketLossRate( + IpSecTransformState oldState, + int rxSeqNo, + int packetCount, + int packetInWin, + int expectedDataLossRate) + throws Exception { + final IpSecTransformState newState = + newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin)); + checkGetPacketLossRate(oldState, newState, expectedDataLossRate); + } + + @Test + public void testGetPacketLossRate_replayWindowUnchanged() throws Exception { + checkGetPacketLossRate( + mTransformStateInitial, mTransformStateInitial, PACKET_LOSS_UNAVALAIBLE); + checkGetPacketLossRate(mTransformStateInitial, 3000, 2000, 2000, PACKET_LOSS_UNAVALAIBLE); + } + + @Test + public void testGetPacketLossRate_againstInitialState() throws Exception { + checkGetPacketLossRate(mTransformStateInitial, 7000, 7001, 4096, 0); + checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4096, 15); + checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4000, 14); + } + + @Test + public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_overlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500)); + + checkGetPacketLossRate(oldState, 5000, 5001, 4096, 0); + checkGetPacketLossRate(oldState, 5000, 4000, 4096, 29); + checkGetPacketLossRate(oldState, 5000, 4000, 4000, 27); + } + + @Test + public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_notOverlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500)); + + checkGetPacketLossRate(oldState, 7000, 7001, 4096, 0); + checkGetPacketLossRate(oldState, 7000, 5000, 4096, 37); + checkGetPacketLossRate(oldState, 7000, 5000, 3000, 21); + } + + @Test + public void testGetPktLossRate_oldHiSeqLargerThanWinSize_overlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000)); + + checkGetPacketLossRate(oldState, 12000, 8096, 4096, 0); + checkGetPacketLossRate(oldState, 12000, 7000, 4096, 36); + checkGetPacketLossRate(oldState, 12000, 7000, 3000, 0); + } + + @Test + public void testGetPktLossRate_oldHiSeqLargerThanWinSize_notOverlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000)); + + checkGetPacketLossRate(oldState, 20000, 16096, 4096, 0); + checkGetPacketLossRate(oldState, 20000, 14000, 4096, 19); + checkGetPacketLossRate(oldState, 20000, 14000, 3000, 10); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java index bf84bbeeedad..355c22156a78 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java @@ -20,6 +20,7 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -29,7 +30,12 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; +import android.net.vcn.FeatureFlags; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.IThermalService; import android.os.ParcelUuid; +import android.os.PowerManager; import android.os.test.TestLooper; import android.telephony.TelephonyManager; @@ -90,32 +96,49 @@ public abstract class NetworkEvaluationTestBase { protected static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface"); + @Mock protected Context mContext; @Mock protected Network mNetwork; + @Mock protected FeatureFlags mFeatureFlags; + @Mock protected com.android.net.flags.FeatureFlags mCoreNetFeatureFlags; @Mock protected TelephonySubscriptionSnapshot mSubscriptionSnapshot; @Mock protected TelephonyManager mTelephonyManager; + @Mock protected IPowerManager mPowerManagerService; protected TestLooper mTestLooper; protected VcnContext mVcnContext; + protected PowerManager mPowerManager; @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - final Context mockContext = mock(Context.class); + when(mNetwork.getNetId()).thenReturn(-1); + mTestLooper = new TestLooper(); mVcnContext = spy( new VcnContext( - mockContext, + mContext, mTestLooper.getLooper(), mock(VcnNetworkProvider.class), false /* isInTestMode */)); doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled(); + doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled(); + setupSystemService( - mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); + mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); + + mPowerManager = + new PowerManager( + mContext, + mPowerManagerService, + mock(IThermalService.class), + mock(Handler.class)); + setupSystemService(mContext, mPowerManager, Context.POWER_SERVICE, PowerManager.class); } } diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index dbf2f514fe89..d85c5150f53f 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -57,7 +57,7 @@ public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase { private UnderlyingNetworkRecord mCellNetworkRecord; @Before - public void setUp() { + public void setUp() throws Exception { super.setUp(); mWifiNetworkRecord = getTestNetworkRecord(WIFI_NETWORK_CAPABILITIES); diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java index a4567ddc20a1..985e70c9771e 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java @@ -34,7 +34,7 @@ public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase { private PersistableBundleWrapper mCarrierConfig; @Before - public void setUp() { + public void setUp() throws Exception { super.setUp(); mCarrierConfig = new PersistableBundleWrapper(new PersistableBundle()); } |