Merge "Address remaining comment of ag/25829379" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 9e30843..70cda8b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -71,6 +71,8 @@
":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 @@
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 f3b2ebb..c84de51 100644
--- a/Android.bp
+++ b/Android.bp
@@ -175,9 +175,6 @@
// 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 @@
],
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 @@
],
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 @@
],
compile_dex: false,
headers_only: true,
- lint: {
- baseline_filename: "lint-baseline.xml",
- },
}
java_library {
@@ -534,7 +522,7 @@
},
lint: {
enabled: false,
- baseline_filename: "lint-baseline.xml",
+
},
}
@@ -559,9 +547,6 @@
],
sdk_version: "core_platform",
apex_available: ["//apex_available:platform"],
- lint: {
- baseline_filename: "lint-baseline.xml",
- },
}
java_library {
@@ -577,9 +562,6 @@
"calendar-provider-compat-config",
"contacts-provider-platform-compat-config",
],
- lint: {
- baseline_filename: "lint-baseline.xml",
- },
}
platform_compat_config {
@@ -634,9 +616,6 @@
"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 7c34812..15fcf66 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4371,7 +4371,7 @@
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 @@
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 @@
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 @@
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 c9e12c0..a532cdb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -862,10 +862,6 @@
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 1e1f155..5840f02 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -25,8 +25,6 @@
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 @@
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 @@
*/
@Nullable
public Object getTarget() {
- return mTarget == null ? null : mTarget.get();
+ return mTarget;
}
@Override
@@ -929,7 +923,7 @@
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 @@
@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 be2582f..2103055 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1177,23 +1177,12 @@
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 f5b6878..0000000
--- 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 390fa22..9cf732a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1603,20 +1603,6 @@
}
});
- // 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 029b93a..0000000
--- 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 073e2d7..497eaf0 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 4bc8fe1..c8e7cae 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 c727a60..561db9c 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -102,6 +102,24 @@
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 @@
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 67a1906..7afd721 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -12,4 +12,11 @@
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 7d9c0a3..2d657c2 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -445,6 +445,19 @@
}
/**
+ * Retrieves the current {@link android.app.Activity} associated with the dream.
+ * This method behaves similarly to calling {@link android.app.Activity#getActivity()}.
+ *
+ * @return The current activity, or null if the dream is not associated with an activity
+ * or not started.
+ *
+ * @hide
+ */
+ public Activity getActivity() {
+ return mActivity;
+ }
+
+ /**
* Inflates a layout resource and set it to be the content view for this Dream.
* Behaves similarly to {@link android.app.Activity#setContentView(int)}.
*
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 92c516c..7658af5 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -42,6 +42,7 @@
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 @@
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 45a0c20..54248be 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -64,6 +64,7 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
}
}
+ 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 @@
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 @@
}
}
+ 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 @@
} 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 @@
&& 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 String iconResName;
public boolean allowManualInvocation;
public int userModifiedFields;
+ @Nullable public Instant deletionInstant; // Only set on deleted rules.
public ZenRule() { }
@@ -2031,6 +2095,9 @@
triggerDescription = source.readString();
type = source.readInt();
userModifiedFields = source.readInt();
+ if (source.readInt() == 1) {
+ deletionInstant = Instant.ofEpochMilli(source.readLong());
+ }
}
}
@@ -2091,6 +2158,12 @@
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 @@
.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 @@
&& 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 @@
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 8902368..91ef11c 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -30,6 +30,11 @@
/**
* 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 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 @@
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 073e2d7..eca48b7 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 118d028..1ca7ac7 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 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 0000000..fd80127
--- /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 a74b06a..9f9b7b4 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 bdaad2b..473b814 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -47,6 +47,7 @@
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 @@
"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 db391f7..a854e36 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -18,6 +18,7 @@
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 @@
# 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 af30532..31acd9a 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 5ad144d..45540e0 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -175,3 +175,74 @@
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 0000000..f8f8338
--- /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 0000000..ffcd7d4
--- /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 0000000..36fe8ec
--- /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 0000000..7a0527c
--- /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 0000000..ea7c6ed
--- /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 0000000..20ee34a
--- /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 0000000..20ee34a
--- /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 81d9638..bb433db 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 @@
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 @@
mShellBackAnimationRegistry.resetDefaultCrossActivity();
cancelLatencyTracking();
if (mBackNavigationInfo != null) {
+ mPreviousNavigationType = mBackNavigationInfo.getType();
mBackNavigationInfo.onBackNavigationFinished(triggerBack);
mBackNavigationInfo = null;
}
@@ -983,7 +988,9 @@
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 8c861c6..bf783e6 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 @@
@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 @@
@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;
+ 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();
+ }
+ break;
+ case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING:
+ mPipHandler.end();
+ mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
+ 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 {
- 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,
- 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_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_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);
+ 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 6ebee73..0000000
--- 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 6c3172a..d58c872 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -56,6 +56,7 @@
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 @@
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 bca57e9..b956fac 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -143,6 +143,15 @@
#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 @@
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 234f42d..756b937 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 @@
/**
* 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 @@
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 4592711..777d1a2 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 @@
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 30d4612..eb4d494 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -269,13 +269,14 @@
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 79b2155..660884a 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 8f8cd0b..9616b5d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -719,7 +719,7 @@
mImpl.transfer(
controller.getRoutingSessionInfo(),
route,
- android.os.Process.myUserHandle(),
+ Process.myUserHandle(),
mContext.getPackageName());
}
@@ -1560,7 +1560,7 @@
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);
}
@@ -2327,11 +2327,7 @@
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
@@ -3198,8 +3194,12 @@
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 262f5f1..096e8ad 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 @@
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 @@
// 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 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 9d16f32d..0000000
--- 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 2f0c83b..5becc86 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 e4e9f7a..0000000
--- 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 0000000..cb6c6b4
--- /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 63b9f24..dcb7ef9 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 67003a3..2a4719d 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 4a5761a..7de941e 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 b2c23a4..58467af 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.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 @@
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 @@
} 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 4dc7f00..e039dea 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.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 @@
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 @@
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 7f16517..8127e1a 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 8b16d64..5da4b95 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -61,7 +61,6 @@
"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 e9c687f..13bf5f5 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 d9f74da..010a6ce 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -28,7 +28,4 @@
"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 cfa64a4..0000000
--- 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 26d05a6..45a07fe 100644
--- a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
@@ -1,38 +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">
-
- <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=" ~~">
- <location
- file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
- line="97"
- column="56"/>
- </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=" ~~">
- <location
- file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
- line="140"
- column="57"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 26 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`"
- errorLine1=" adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
- line="75"
- column="34"/>
- </issue>
+<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"
@@ -58,6 +25,28 @@
<issue
id="NewApi"
+ message="Call requires API level 26 (current min is 23): `android.app.admin.DevicePolicyManager#getDeviceOwnerComponentOnAnyUser`"
+ errorLine1=" adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
+ line="75"
+ column="34"/>
+ </issue>
+
+ <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=" ~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
+ line="97"
+ column="56"/>
+ </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=" ~~~~~~~~~~~~~~~~~~~">
@@ -67,4 +56,15 @@
column="17"/>
</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=" ~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
+ 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 0744710..db6a882 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 53346e0..3cfca1d 100644
--- a/packages/SettingsLib/SearchProvider/lint-baseline.xml
+++ b/packages/SettingsLib/SearchProvider/lint-baseline.xml
@@ -1,27 +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">
-
- <issue
- id="NewApi"
- 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="107"
- column="13"/>
- </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=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
- line="97"
- column="69"/>
- </issue>
+<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"
@@ -36,6 +14,39 @@
<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=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+ line="45"
+ column="54"/>
+ </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=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+ 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>
+
+ <issue
+ id="NewApi"
message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#className`"
errorLine1=" .add(XmlResource.COLUMN_CLASS_NAME, indexableResource.className)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -58,6 +69,39 @@
<issue
id="NewApi"
+ 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="56"
+ column="29"/>
+ </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=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
+ line="97"
+ column="69"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ 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="107"
+ column="13"/>
+ </issue>
+
+ <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=" ~~~~~~~~~~~~~~~~~">
@@ -81,17 +125,6 @@
<issue
id="NewApi"
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="56"
- column="29"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level 23 (current min is 21): `android.provider.SearchIndexableData#intentTargetClass`"
errorLine1=" this.intentTargetClass = className;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -100,37 +133,4 @@
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=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
- 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>
-
- <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=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SettingsLib/SearchProvider/src/com/android/settingslib/searchprovider/SettingsXmlIndexProvider.java"
- line="45"
- column="54"/>
- </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 a9974dc..514ad669 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -39,6 +39,9 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
@@ -68,6 +71,7 @@
) {
val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
Button(
+ modifier = Modifier.semantics { role = Role.DropdownList },
onClick = { expanded = true },
colors = ButtonDefaults.buttonColors(
containerColor = SettingsTheme.colorScheme.spinnerHeaderContainer,
diff --git a/packages/SettingsLib/Tile/lint-baseline.xml b/packages/SettingsLib/Tile/lint-baseline.xml
index 326ec0d..56b1bca 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 3fcd56c..2f6cc3a 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=" ~~">
+ 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="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=" ~~">
- <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,35 +25,13 @@
<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"/>
- </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=" ~~~~~~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java"
- line="62"
- column="60"/>
- </issue>
-
- <issue
- id="NewApi"
- 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="163"
- column="37"/>
+ line="80"
+ column="29"/>
</issue>
<issue
@@ -80,13 +47,13 @@
<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=" mContext.getPackageName(), 0, UserHandle.of(managedUserId)"
+ errorLine2=" ~~">
<location
file="frameworks/base/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java"
- line="80"
- column="29"/>
+ line="119"
+ column="70"/>
</issue>
<issue
@@ -102,6 +69,28 @@
<issue
id="NewApi"
+ 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="150"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ 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="163"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="NewApi"
message="Call requires API level 30 (current min is 21): `android.os.UserManager#getAllProfiles`"
errorLine1=" List<UserHandle> allProfiles = mUserManager.getAllProfiles();"
errorLine2=" ~~~~~~~~~~~~~~">
@@ -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 d6a23fd..0000000
--- 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 3b14712..390c9d2e 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -12,9 +12,6 @@
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 7ec512b..61cdb05 100644
--- a/packages/SettingsLib/search/lint-baseline.xml
+++ b/packages/SettingsLib/search/lint-baseline.xml
@@ -1,16 +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">
-
- <issue
- id="NewApi"
- 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="62"
- column="9"/>
- </issue>
+<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"
@@ -23,4 +12,15 @@
column="41"/>
</issue>
+ <issue
+ id="NewApi"
+ 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="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 5bc27195..943e3fc 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 @@
@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 @@
@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 d47527a..2fea6a5 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.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.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.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 @@
* 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 @@
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 @@
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 c8461d2..02d30c5e 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 @@
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 ff6fd43..54510a8 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.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.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 @@
@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 @@
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 8e3f664..a776062 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.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 @@
@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 @@
withDeps.communalInteractor,
WidgetInteractionHandler(mock()),
withDeps.tutorialInteractor,
- Provider { shadeViewController },
- powerManager,
mediaHost,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
index bd1c310..c104977 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
@@ -16,32 +16,45 @@
package com.android.systemui.qs.tiles.base.actions
+import android.app.PendingIntent
+import android.content.ComponentName
import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ResolveInfoFlags
+import android.content.pm.ResolveInfo
+import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class QSTileIntentUserInputHandlerTest : SysuiTestCase() {
-
- @Mock private lateinit var activityStarted: ActivityStarter
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var activityStarter: ActivityStarter
lateinit var underTest: QSTileIntentUserInputHandler
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- underTest = QSTileIntentUserInputHandlerImpl(activityStarted)
+ underTest = QSTileIntentUserInputHandlerImpl(activityStarter, packageManager, user)
}
@Test
@@ -50,6 +63,103 @@
underTest.handle(null, intent)
- verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
+ verify(activityStarter).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
+ }
+
+ @Test
+ fun testPassesActivityPendingIntentToStarterAsPendingIntent() {
+ val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+
+ underTest.handle(null, pendingIntent, true)
+
+ verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
+ }
+
+ @Test
+ fun testPassesActivityPendingIntentToStarterAsPendingIntentWhenNotRequestingActivityStart() {
+ val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+
+ underTest.handle(null, pendingIntent, false)
+
+ verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
+ }
+
+ @Test
+ fun testPassNonActivityPendingIntentAndRequestStartingActivity_findsIntentAndStarts() {
+ val pendingIntent =
+ mock<PendingIntent> {
+ whenever(isActivity).thenReturn(false)
+ whenever(creatorPackage).thenReturn(ORIGINAL_PACKAGE)
+ }
+ setUpQueryResult(listOf(createActivityInfo(testResolvedComponent, exported = true)))
+
+ underTest.handle(null, pendingIntent, true)
+
+ val expectedIntent =
+ Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setPackage(null)
+ .addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ )
+ .setComponent(testResolvedComponent)
+
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcher(expectedIntent)),
+ eq(0),
+ any()
+ )
+ }
+
+ @Test
+ fun testPassNonActivityPendingIntentAndDoNotRequestStartingActivity_doesNotStartActivity() {
+ val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) }
+
+ underTest.handle(null, pendingIntent, false)
+
+ verify(activityStarter, never())
+ .postStartActivityDismissingKeyguard(any(Intent::class.java), eq(0), any())
+ }
+
+ private fun createActivityInfo(
+ componentName: ComponentName,
+ exported: Boolean = false,
+ ): ActivityInfo {
+ return ActivityInfo().apply {
+ packageName = componentName.packageName
+ name = componentName.className
+ this.exported = exported
+ }
+ }
+
+ private fun setUpQueryResult(infos: List<ActivityInfo>) {
+ `when`(
+ packageManager.queryIntentActivitiesAsUser(
+ any(Intent::class.java),
+ any(ResolveInfoFlags::class.java),
+ eq(user.identifier)
+ )
+ )
+ .thenReturn(infos.map { ResolveInfo().apply { activityInfo = it } })
+ }
+
+ private class IntentMatcher(intent: Intent) : ArgumentMatcher<Intent> {
+ private val expectedIntent = intent
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.action.equals(expectedIntent.action) &&
+ argument?.`package`.equals(expectedIntent.`package`) &&
+ argument?.component?.equals(expectedIntent.component)!! &&
+ argument?.categories?.equals(expectedIntent.categories)!! &&
+ argument?.flags?.equals(expectedIntent.flags)!!
+ }
+ }
+
+ companion object {
+ private const val ORIGINAL_PACKAGE = "original_pkg"
+ private const val TEST_PACKAGE = "test_pkg"
+ private const val TEST_COMPONENT_CLASS_NAME = "test_component_class_name"
+ private val testResolvedComponent = ComponentName(TEST_PACKAGE, TEST_COMPONENT_CLASS_NAME)
+ private val user = UserHandle.of(0)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
index e44c849..be2da17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
@@ -18,42 +18,25 @@
import android.app.AlarmManager.AlarmClockInfo
import android.app.PendingIntent
-import android.content.Intent
import android.provider.AlarmClock
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
class AlarmTileUserActionInteractorTest : SysuiTestCase() {
- private lateinit var activityStarter: ActivityStarter
- private lateinit var intentCaptor: ArgumentCaptor<Intent>
- private lateinit var pendingIntentCaptor: ArgumentCaptor<PendingIntent>
-
- lateinit var underTest: AlarmTileUserActionInteractor
-
- @Before
- fun setup() {
- activityStarter = mock<ActivityStarter>()
- intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
- pendingIntentCaptor = ArgumentCaptor.forClass(PendingIntent::class.java)
- underTest = AlarmTileUserActionInteractor(activityStarter)
- }
+ private val inputHandler = FakeQSTileIntentUserInputHandler()
+ private val underTest = AlarmTileUserActionInteractor(inputHandler)
@Test
fun handleClickWithDefaultIntent() = runTest {
@@ -62,21 +45,21 @@
underTest.handleInput(click(inputModel))
- verify(activityStarter)
- .postStartActivityDismissingKeyguard(capture(intentCaptor), eq(0), nullable())
- assertThat(intentCaptor.value.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS)
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS)
+ }
}
@Test
fun handleClickWithPendingIntent() = runTest {
- val expectedIntent: PendingIntent = mock<PendingIntent>()
+ val expectedIntent = mock<PendingIntent>()
val alarmInfo = AlarmClockInfo(1L, expectedIntent)
val inputModel = AlarmTileModel.NextAlarmSet(true, alarmInfo)
underTest.handleInput(click(inputModel))
- verify(activityStarter)
- .postStartActivityDismissingKeyguard(capture(pendingIntentCaptor), nullable())
- assertThat(pendingIntentCaptor.value).isEqualTo(expectedIntent)
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOnePendingIntentInput {
+ assertThat(it.pendingIntent).isEqualTo(expectedIntent)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
new file mode 100644
index 0000000..8a0400d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RemoteInputRepositoryImplTest : SysuiTestCase() {
+ @Mock private lateinit var remoteInputManager: NotificationRemoteInputManager
+
+ private lateinit var testScope: TestScope
+ private lateinit var underTest: RemoteInputRepositoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testScope = TestScope()
+ underTest = RemoteInputRepositoryImpl(remoteInputManager)
+ }
+
+ @Test
+ fun isRemoteInputActive_updatesOnChange() =
+ testScope.runTest {
+ val active by collectLastValue(underTest.isRemoteInputActive)
+ runCurrent()
+ assertThat(active).isFalse()
+
+ val callback = withArgCaptor {
+ verify(remoteInputManager).addControllerCallback(capture())
+ }
+
+ callback.onRemoteInputActive(true)
+ runCurrent()
+ assertThat(active).isTrue()
+
+ callback.onRemoteInputActive(false)
+ runCurrent()
+ assertThat(active).isFalse()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt
new file mode 100644
index 0000000..12469dd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorTest.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RemoteInputInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val fakeRemoteInputRepository = kosmos.fakeRemoteInputRepository
+ private val underTest = kosmos.remoteInputInteractor
+
+ @Test
+ fun isRemoteInputActive_true() =
+ testScope.runTest {
+ val active by collectLastValue(underTest.isRemoteInputActive)
+
+ fakeRemoteInputRepository.isRemoteInputActive.value = true
+ runCurrent()
+
+ assertThat(active).isTrue()
+ }
+
+ @Test
+ fun isRemoteInputActive_false() =
+ testScope.runTest {
+ val active by collectLastValue(underTest.isRemoteInputActive)
+
+ fakeRemoteInputRepository.isRemoteInputActive.value = false
+ runCurrent()
+
+ assertThat(active).isFalse()
+ }
+}
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 05106c9..326c7ef 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -34,9 +34,6 @@
srcs: [
":statslog-SystemUI-java-gen",
],
- lint: {
- baseline_filename: "lint-baseline.xml",
- },
}
android_library {
@@ -74,9 +71,6 @@
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
kotlincflags: ["-Xjvm-default=all"],
- lint: {
- baseline_filename: "lint-baseline.xml",
- },
}
java_library {
@@ -88,9 +82,6 @@
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_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 4bd6729..0000000
--- 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 e457ca1..8e5d0da 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -1111,7 +1111,7 @@
}
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 28f48ce..84708a4 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 @@
/** 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 @@
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 0cbf3f13..7faf653 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.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.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 @@
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 1c69685..7a96fab 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.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 @@
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 699532c..89a983b 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 @@
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 @@
// 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 52f2759..d7a2aa0 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 @@
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.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 @@
@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 @@
.sample(
combine(
keyguardInteractor.primaryBouncerShowing,
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
powerInteractor.isAwake,
keyguardInteractor.isAodAvailable,
::toQuad
@@ -102,20 +109,19 @@
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 @@
}
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 8584401..fedd63b 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 @@
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.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 @@
@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 @@
.dozeTransitionTo(DozeStateModel.FINISH)
.sample(
combine(
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardOccluded,
::Pair
),
@@ -77,7 +84,6 @@
} else {
TransitionModeOnCanceled.LAST_VALUE
}
-
startTransitionTo(
toState = toState,
modeOnCanceled = modeOnCanceled,
@@ -89,16 +95,13 @@
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 @@
}
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 eca7088..fcb7698 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 @@
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.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 @@
@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 @@
powerInteractor.isAwake
.sample(
combine(
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardOccluded,
::Pair
),
@@ -76,7 +83,7 @@
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 @@
}
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 dac6ef5..a6cdaa8 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 @@
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.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 @@
@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 @@
.sample(
combine(
keyguardInteractor.dozeTransitionModel,
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
::Pair
),
::toTriple
@@ -88,7 +95,7 @@
.sample(
combine(
keyguardInteractor.isKeyguardOccluded,
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
::Pair,
),
::toTriple
@@ -108,7 +115,7 @@
private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
scope.launch {
keyguardInteractor.primaryBouncerShowing
- .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .sample(startedKeyguardTransitionStep, ::Pair)
.collect { (isBouncerShowing, lastStartedTransitionStep) ->
if (
isBouncerShowing &&
@@ -123,7 +130,7 @@
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 @@
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 7fdcf2f..13ffd63 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 @@
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.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 @@
@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 @@
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 @@
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 @@
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 @@
}
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 70c2e6d..19fd7f9 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 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 62a0b0e..742790e 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 @@
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.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 @@
@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 @@
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 @@
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 @@
keyguardInteractor.isAbleToDream
.sample(
combine(
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
keyguardInteractor.isActiveDreamLockscreenHosted,
::Pair
),
@@ -106,7 +113,7 @@
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 cecc653..2d0baa8 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 @@
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 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 @@
) :
TransitionInteractor(
fromState = KeyguardState.LOCKSCREEN,
+ transitionInteractor = transitionInteractor,
+ mainDispatcher = mainDispatcher,
+ bgDispatcher = bgDispatcher,
) {
override fun start() {
@@ -147,12 +155,12 @@
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 @@
}
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 @@
}
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 @@
/* 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,64 +233,66 @@
::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 (
- nextState == TransitionState.CANCELED ||
- nextState == TransitionState.FINISHED
- ) {
- 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 }
+ // 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 (
- keyguardState.to == KeyguardState.LOCKSCREEN &&
- shadeRepository.legacyShadeTracking.value &&
- !isKeyguardUnlocked &&
- statusBarState == KEYGUARD
- ) {
- 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
+ )
+ }
}
}
}
@@ -290,7 +300,7 @@
}
fun dismissKeyguard() {
- startTransitionTo(KeyguardState.GONE)
+ scope.launch { startTransitionTo(KeyguardState.GONE) }
}
private fun listenForLockscreenToGone() {
@@ -298,9 +308,9 @@
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 @@
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 @@
}
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 6a8555c..40061f4 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 @@
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.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 @@
@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 @@
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 @@
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 @@
.sample(
combine(
keyguardInteractor.isKeyguardShowing,
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
::Pair
),
::toTriple
@@ -111,7 +117,7 @@
.sample(
combine(
keyguardInteractor.isKeyguardShowing,
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
::Pair
),
::toTriple
@@ -135,7 +141,7 @@
powerInteractor.isAsleep
.sample(
combine(
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
keyguardInteractor.isAodAvailable,
::Pair
),
@@ -154,7 +160,7 @@
private fun listenForOccludedToAlternateBouncer() {
scope.launch {
keyguardInteractor.alternateBouncerShowing
- .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .sample(startedKeyguardTransitionStep, ::Pair)
.collect { (isAlternateBouncerShowing, lastStartedTransitionStep) ->
if (
isAlternateBouncerShowing &&
@@ -183,6 +189,7 @@
}
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 5f246e1..c62055f 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 @@
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.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 @@
@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 @@
) :
TransitionInteractor(
fromState = KeyguardState.PRIMARY_BOUNCER,
+ transitionInteractor = transitionInteractor,
+ mainDispatcher = mainDispatcher,
+ bgDispatcher = bgDispatcher,
) {
override fun start() {
@@ -115,7 +122,7 @@
.distinctUntilChanged()
fun dismissPrimaryBouncer() {
- startTransitionTo(KeyguardState.GONE)
+ scope.launch { startTransitionTo(KeyguardState.GONE) }
}
private fun listenForPrimaryBouncerToLockscreenOrOccluded() {
@@ -124,7 +131,7 @@
.sample(
combine(
powerInteractor.isAwake,
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardOccluded,
keyguardInteractor.isActiveDreamLockscreenHosted,
::toQuad
@@ -158,7 +165,7 @@
.sample(
combine(
powerInteractor.isAsleep,
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
keyguardInteractor.isAodAvailable,
::Triple
),
@@ -185,7 +192,7 @@
.sample(
combine(
keyguardInteractor.isActiveDreamLockscreenHosted,
- transitionInteractor.startedKeyguardTransitionStep,
+ startedKeyguardTransitionStep,
::Pair
),
::toTriple
@@ -213,7 +220,7 @@
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 d5ac283..5c2df45 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.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 @@
*/
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 @@
)
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 0385aeb..523414c 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 @@
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/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
index fc06090..fe10eaa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
@@ -18,6 +18,8 @@
import android.app.PendingIntent
import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.UserHandle
import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -32,13 +34,23 @@
interface QSTileIntentUserInputHandler {
fun handle(view: View?, intent: Intent)
- fun handle(view: View?, pendingIntent: PendingIntent)
+
+ /** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */
+ fun handle(
+ view: View?,
+ pendingIntent: PendingIntent,
+ requestLaunchingDefaultActivity: Boolean = false
+ )
}
@SysUISingleton
class QSTileIntentUserInputHandlerImpl
@Inject
-constructor(private val activityStarter: ActivityStarter) : QSTileIntentUserInputHandler {
+constructor(
+ private val activityStarter: ActivityStarter,
+ private val packageManager: PackageManager,
+ private val userHandle: UserHandle,
+) : QSTileIntentUserInputHandler {
override fun handle(view: View?, intent: Intent) {
val animationController: ActivityLaunchAnimator.Controller? =
@@ -52,21 +64,41 @@
}
// TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939
- override fun handle(view: View?, pendingIntent: PendingIntent) {
- if (!pendingIntent.isActivity) {
- return
- }
- val animationController: ActivityLaunchAnimator.Controller? =
- view?.let {
- ActivityLaunchAnimator.Controller.fromView(
- it,
- InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+ override fun handle(
+ view: View?,
+ pendingIntent: PendingIntent,
+ requestLaunchingDefaultActivity: Boolean
+ ) {
+ if (pendingIntent.isActivity) {
+ val animationController: ActivityLaunchAnimator.Controller? =
+ view?.let {
+ ActivityLaunchAnimator.Controller.fromView(
+ it,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+ )
+ }
+ activityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
+ } else if (requestLaunchingDefaultActivity) {
+ val intent =
+ Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setPackage(pendingIntent.creatorPackage)
+ .addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ )
+ val intents =
+ packageManager.queryIntentActivitiesAsUser(
+ intent,
+ PackageManager.ResolveInfoFlags.of(0L),
+ userHandle.identifier
)
- }
- activityStarter.startPendingIntentMaybeDismissingKeyguard(
- pendingIntent,
- null,
- animationController
- )
+ intents
+ .firstOrNull { it.activityInfo.exported }
+ ?.let { resolved ->
+ intent.setPackage(null)
+ intent.setComponent(resolved.activityInfo.componentName)
+ handle(view, intent)
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
index afca57c..0ad520b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
@@ -18,9 +18,7 @@
import android.content.Intent
import android.provider.AlarmClock
-import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
@@ -31,34 +29,20 @@
class AlarmTileUserActionInteractor
@Inject
constructor(
- private val activityStarter: ActivityStarter,
+ private val inputHandler: QSTileIntentUserInputHandler,
) : QSTileUserActionInteractor<AlarmTileModel> {
override suspend fun handleInput(input: QSTileInput<AlarmTileModel>): Unit =
with(input) {
when (action) {
is QSTileUserAction.Click -> {
- val animationController =
- action.view?.let {
- ActivityLaunchAnimator.Controller.fromView(
- it,
- InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
- )
- }
if (
data is AlarmTileModel.NextAlarmSet &&
data.alarmClockInfo.showIntent != null
) {
val pendingIndent = data.alarmClockInfo.showIntent
- activityStarter.postStartActivityDismissingKeyguard(
- pendingIndent,
- animationController
- )
+ inputHandler.handle(action.view, pendingIndent, true)
} else {
- activityStarter.postStartActivityDismissingKeyguard(
- Intent(AlarmClock.ACTION_SHOW_ALARMS),
- 0,
- animationController
- )
+ inputHandler.handle(action.view, Intent(AlarmClock.ACTION_SHOW_ALARMS))
}
}
is QSTileUserAction.LongClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 3be60b7..782d651 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 2438298..7f8be1c 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.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 @@
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/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
index 29d53fc..9f878b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.data
import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule
+import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule
import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule
import dagger.Module
@@ -24,6 +25,7 @@
includes =
[
KeyguardStatusBarRepositoryModule::class,
+ RemoteInputRepositoryModule::class,
StatusBarModeRepositoryModule::class,
StatusBarPhoneDataLayerModule::class
]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
new file mode 100644
index 0000000..c0302bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.RemoteInputController
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Repository used for tracking the state of notification remote input (e.g. when the user presses
+ * "reply" on a notification and the keyboard opens).
+ */
+interface RemoteInputRepository {
+ /** Whether remote input is currently active for any notification. */
+ val isRemoteInputActive: Flow<Boolean>
+}
+
+@SysUISingleton
+class RemoteInputRepositoryImpl
+@Inject
+constructor(
+ private val notificationRemoteInputManager: NotificationRemoteInputManager,
+) : RemoteInputRepository {
+ override val isRemoteInputActive: Flow<Boolean> = conflatedCallbackFlow {
+ trySend(false) // initial value is false
+ val callback =
+ object : RemoteInputController.Callback {
+ override fun onRemoteInputActive(active: Boolean) {
+ trySend(active)
+ }
+ }
+ notificationRemoteInputManager.addControllerCallback(callback)
+ awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) }
+ }
+}
+
+@Module
+interface RemoteInputRepositoryModule {
+ @Binds fun bindImpl(impl: RemoteInputRepositoryImpl): RemoteInputRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
new file mode 100644
index 0000000..68f727b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.data.repository.RemoteInputRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Interactor used for business logic pertaining to the notification remote input (e.g. when the
+ * user presses "reply" on a notification and the keyboard opens).
+ */
+@SysUISingleton
+class RemoteInputInteractor @Inject constructor(remoteInputRepository: RemoteInputRepository) {
+ /** Is remote input currently active for a notification? */
+ val isRemoteInputActive: Flow<Boolean> = remoteInputRepository.isRemoteInputActive
+}
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 abc04b8..a30c294 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.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 @@
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 8e81185..809947d 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 @@
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 b8a8bdf..e531d44 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 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.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 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- testScope = TestScopeProvider.getTestScope()
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
@@ -139,6 +140,8 @@
fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor(
scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
@@ -160,6 +163,8 @@
fromPrimaryBouncerTransitionInteractor =
FromPrimaryBouncerTransitionInteractor(
scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
@@ -173,6 +178,8 @@
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
@@ -182,6 +189,8 @@
fromDreamingLockscreenHostedTransitionInteractor =
FromDreamingLockscreenHostedTransitionInteractor(
scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
@@ -191,6 +200,8 @@
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
@@ -200,6 +211,8 @@
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
@@ -210,6 +223,8 @@
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
@@ -220,6 +235,8 @@
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
@@ -230,6 +247,8 @@
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 5569ca9..b7a9ea7 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.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 @@
@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 @@
communalInteractor,
communalViewModel,
keyguardTransitionInteractor,
- shadeInteractor
+ shadeInteractor,
+ powerManager
)
testableLooper = TestableLooper.get(this)
@@ -90,14 +95,14 @@
}
@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 @@
}
@Test
- fun onTouchEvent_touchInsideGestureRegion_returnsTrue() {
+ fun onTouchEvent_touchInsideGestureRegion_interceptsTouches() {
// Communal is open.
communalRepository.setDesiredScene(CommunalSceneKey.Communal)
@@ -131,7 +136,7 @@
}
@Test
- fun onTouchEvent_subsequentTouchesAfterGestureStart_returnsTrue() {
+ fun onTouchEvent_subsequentTouchesAfterGestureStart_interceptsTouches() {
// Communal is open.
communalRepository.setDesiredScene(CommunalSceneKey.Communal)
@@ -146,7 +151,7 @@
}
@Test
- fun onTouchEvent_communalOpen_returnsTrue() {
+ fun onTouchEvent_communalOpen_interceptsTouches() {
// Communal is open.
communalRepository.setDesiredScene(CommunalSceneKey.Communal)
@@ -155,10 +160,12 @@
// 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 @@
// 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 437d00a..5d663d2 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 @@
keyguardTransitionRepository,
keyguardTransitionInteractor,
mTestScope.getBackgroundScope(),
+ mUtils.getTestDispatcher(),
+ mUtils.getTestDispatcher(),
keyguardInteractor,
featureFlags,
shadeRepository,
@@ -234,6 +236,8 @@
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 39051eb..982787b 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 @@
keyguardTransitionRepository,
keyguardTransitionInteractor,
mTestScope.getBackgroundScope(),
+ mUtils.getTestDispatcher(),
+ mUtils.getTestDispatcher(),
keyguardInteractor,
featureFlags,
mShadeRepository,
@@ -269,6 +271,8 @@
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 aed6163..83590ee 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 @@
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 @@
keyguardTransitionRepository,
keyguardTransitionInteractor,
testScope.backgroundScope,
+ testDispatcher,
+ testDispatcher,
keyguardInteractor,
featureFlags,
shadeRepository,
@@ -162,6 +165,8 @@
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 b7529a8..048120a 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 @@
keyguardTransitionRepository,
keyguardTransitionInteractor,
mTestScope.getBackgroundScope(),
+ mUtils.getTestDispatcher(),
+ mUtils.getTestDispatcher(),
keyguardInteractor,
featureFlags,
shadeRepository,
@@ -462,6 +464,8 @@
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 e24ba26..c2dc673 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 @@
@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 b03d0b8..b1a0b67 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.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 @@
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 ade3e1a..97536e2 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.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 @@
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
scope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
flags = featureFlagsClassic,
keyguardSecurityModel = keyguardSecurityModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
index 1185f2e..0307c41 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
@@ -35,14 +35,21 @@
mutableInputs.add(Input.Intent(view, intent))
}
- override fun handle(view: View?, pendingIntent: PendingIntent) {
- mutableInputs.add(Input.PendingIntent(view, pendingIntent))
+ override fun handle(
+ view: View?,
+ pendingIntent: PendingIntent,
+ requestLaunchingDefaultActivity: Boolean
+ ) {
+ mutableInputs.add(Input.PendingIntent(view, pendingIntent, requestLaunchingDefaultActivity))
}
sealed interface Input {
data class Intent(val view: View?, val intent: android.content.Intent) : Input
- data class PendingIntent(val view: View?, val pendingIntent: android.app.PendingIntent) :
- Input
+ data class PendingIntent(
+ val view: View?,
+ val pendingIntent: android.app.PendingIntent,
+ val requestLaunchingDefaultActivity: Boolean
+ ) : Input
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
new file mode 100644
index 0000000..c416ea1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeRemoteInputRepository : RemoteInputRepository {
+ override val isRemoteInputActive = MutableStateFlow(false)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt
new file mode 100644
index 0000000..1684efb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.remoteInputRepository: RemoteInputRepository by
+ Kosmos.Fixture { fakeRemoteInputRepository }
+val Kosmos.fakeRemoteInputRepository by Kosmos.Fixture { FakeRemoteInputRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt
new file mode 100644
index 0000000..07b39dc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractorKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.data.repository.remoteInputRepository
+
+val Kosmos.remoteInputInteractor by Kosmos.Fixture { RemoteInputInteractor(remoteInputRepository) }
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index e52cefb..81fd8ce 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -39,7 +39,4 @@
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 449ed2e..0000000
--- 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 0b484f4..7e8333c 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -148,9 +148,6 @@
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 a354671..69cc68a 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -22,6 +22,7 @@
lint: {
error_checks: ["MissingPermissionAnnotation"],
baseline_filename: "lint-baseline.xml",
+
},
srcs: [
":services.accessibility-sources",
@@ -50,9 +51,6 @@
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 6bec8cf..b808219 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 93c9390..46de2cdd 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/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 62c6703..3e45626 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -125,7 +125,7 @@
* Send a message to remote devices through the transports
*/
public void sendMessage(int message, byte[] data, int[] associationIds) {
- Slog.i(TAG, "Sending message 0x" + Integer.toHexString(message)
+ Slog.d(TAG, "Sending message 0x" + Integer.toHexString(message)
+ " data length " + data.length);
synchronized (mTransports) {
for (int i = 0; i < associationIds.length; i++) {
diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java
index 22b18ac..8a5774e 100644
--- a/services/companion/java/com/android/server/companion/transport/Transport.java
+++ b/services/companion/java/com/android/server/companion/transport/Transport.java
@@ -284,7 +284,7 @@
if (mListeners.containsKey(message)) {
try {
mListeners.get(message).onMessageReceived(getAssociationId(), data);
- Slog.i(TAG, "Message 0x" + Integer.toHexString(message)
+ Slog.d(TAG, "Message 0x" + Integer.toHexString(message)
+ " is received from associationId " + mAssociationId
+ ", sending data length " + data.length + " to the listener.");
} catch (RemoteException ignored) {
diff --git a/services/companion/lint-baseline.xml b/services/companion/lint-baseline.xml
index 03eae39..020126f 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 a3fc3bf..a6ed498 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -234,9 +234,6 @@
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 02f4485..a78bebd 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 @@
// 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 c2bc1e4..a30cdc4 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.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 @@
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 @@
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 @@
mGlobalTimeOffsetInSecs);
if (records != null) {
mDoseRecords.addAll(records);
+ sanitizeDoseRecords_l();
}
}
}
@@ -1176,17 +1187,35 @@
&& 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 @@
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 d02b6f4..171fbb6 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 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.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.LocationUsageLogger;
import com.android.server.location.injector.SettingsHelper;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Collection;
/**
@@ -91,6 +95,9 @@
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 @@
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 ad09082..542b3b0 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 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.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 @@
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 @@
}
@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 7aa7b7e..9ddc362 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.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.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 @@
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 911643b..afbf08d 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 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
if (forBackup && mConfigs.keyAt(i) != userId) {
continue;
}
- mConfigs.valueAt(i).writeXml(out, version);
+ mConfigs.valueAt(i).writeXml(out, version, forBackup);
}
}
}
@@ -1468,28 +1549,51 @@
}
/**
- * 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);
}
}
}
- setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "cleanUpZenRules",
- Process.SYSTEM_UID);
+
+ 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);
+ }
+ }
+ }
}
}
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 4454601..0000000
--- 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 3a9dedc..7f0aadc 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.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 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
@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 @@
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 @@
// 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 @@
initBackgroundInstalledPackages();
mBackgroundInstalledPackages.add(userId, packageName);
- mCallbackHelper.notifyAllCallbacks(userId, packageName);
writeBackgroundInstalledPackagesToDisk();
}
@@ -298,8 +259,8 @@
return PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName);
}
- private boolean wasForegroundInstallation(
- String installerPackageName, int userId, long installTimestamp) {
+ private boolean wasForegroundInstallation(String installerPackageName,
+ int userId, long installTimestamp) {
TreeSet<BackgroundInstallControlService.ForegroundTimeFrame> foregroundTimeFrames =
mInstallerForegroundTimeFrames.get(userId, installerPackageName);
@@ -388,12 +349,12 @@
for (int i = 0; i < mBackgroundInstalledPackages.size(); i++) {
int userId = mBackgroundInstalledPackages.keyAt(i);
for (String packageName : mBackgroundInstalledPackages.get(userId)) {
- long token =
- protoOutputStream.start(
- BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
+ long token = protoOutputStream.start(
+ BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
protoOutputStream.write(
BackgroundInstalledPackageProto.PACKAGE_NAME, packageName);
- protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, userId + 1);
+ protoOutputStream.write(
+ BackgroundInstalledPackageProto.USER_ID, userId + 1);
protoOutputStream.end(token);
}
}
@@ -426,28 +387,23 @@
!= (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 @@
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 @@
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 @@
}
/**
- * Dependency injector for {@link BackgroundInstallControlService}.
+ * Dependency injector for {@link #BackgroundInstallControlService)}.
*/
interface Injector {
Context getContext();
@@ -582,8 +534,6 @@
Looper getLooper();
File getDiskFile();
-
- BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper();
}
private static final class InjectorImpl implements Injector {
@@ -620,11 +570,11 @@
@Override
public Looper getLooper() {
- ServiceThread serviceThread =
- new ServiceThread(
- TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
+ ServiceThread serviceThread = new ServiceThread(TAG,
+ android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
serviceThread.start();
return serviceThread.getLooper();
+
}
@Override
@@ -633,10 +583,5 @@
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 3adeb4b..446c629 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.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.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 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 6ce8685..ed04e5f 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 @@
@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 @@
// Auto-generated class
mFeatureFlags = new FeatureFlagsImpl();
+ mCoreNetFeatureFlags = new com.android.net.flags.FeatureFlagsImpl();
}
@NonNull
@@ -69,6 +71,14 @@
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 0000000..5f4852f
--- /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 0000000..a79f188
--- /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 073e2d7..eca48b7 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 cd48f5d..106be5f 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 @@
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 9e56d7a..8aaf76a 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 @@
// 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 6033220..02b3f15 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 @@
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 070bd4b..2ccd1e4 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 8489c17..a311d07 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 1bf031a..11c0cc8e 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 e3954355..4fcdbfc 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
+++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
@@ -33,7 +33,6 @@
":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 031d57f..1e7a78a 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 5092a46..7450607 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 @@
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 @@
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 b5b8ea0..1fa1f84 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 f033fed..b74e561 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 39b0ff7..7804f4c 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 @@
"--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 e1fce9b..0000000
--- 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 3069d25..daf18ed 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.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 @@
private Looper mLooper;
private File mFile;
+
@Mock
private Context mContext;
@Mock
@@ -112,12 +108,8 @@
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 @@
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 @@
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
mBackgroundInstallControlService.initBackgroundInstalledPackages();
assertNotNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
- assertEquals(0, mBackgroundInstallControlService.getBackgroundInstalledPackages().size());
+ assertEquals(0,
+ mBackgroundInstallControlService.getBackgroundInstalledPackages().size());
}
@Test
@@ -169,9 +161,12 @@
// 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 @@
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 @@
// 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 @@
!= (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 @@
// 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 @@
!= (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 @@
// 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 @@
!= (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 @@
@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 @@
@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 @@
@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 @@
@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 @@
}
@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 @@
}
@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 @@
// 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 @@
}
@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 @@
// 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages();
assertNull(packages);
}
-
@Test
public void testPackageRemoved() {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
@@ -898,7 +859,8 @@
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 @@
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 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 53ca704..bf850cf 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.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 @@
}
@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 177d645..dd252f3 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.time.Instant;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -407,6 +408,7 @@
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 @@
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 @@
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 @@
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 7e92e42..9d7cf53 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 @@
@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 0224ff3..9e3e336 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_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.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.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.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.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 @@
@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 @@
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 @@
@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 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 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 @@
// 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 @@
}
@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 @@
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 f5282cb..9bb2da0 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 @@
// 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 @@
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 @@
// 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 8bd5473..2d3c4bb 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.Looper;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
+import android.util.Log;
import androidx.test.filters.MediumTest;
@@ -147,6 +148,8 @@
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 @@
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 60e84b0..9c421ba 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 @@
}
/**
+ * 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 c2c0a35..62a2ee5 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 5615602..a5c6d57 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1315,7 +1315,7 @@
* 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 @@
* 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 @@
}
/**
- * 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 @@
* @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 fee1b25..2bc056e 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 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 @@
@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 0000000..9daba6a
--- /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 bf84bbe..355c221 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.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.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 @@
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 dbf2f51..d85c515 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 @@
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 a4567dd..985e70c 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 @@
private PersistableBundleWrapper mCarrierConfig;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
super.setUp();
mCarrierConfig = new PersistableBundleWrapper(new PersistableBundle());
}