summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-rw-r--r--api/current.txt3
-rw-r--r--api/system-current.txt1
-rw-r--r--core/java/android/hardware/face/FaceManager.java9
-rw-r--r--core/java/android/provider/DeviceConfig.java8
-rw-r--r--core/java/android/provider/Settings.java70
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java7
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--packages/SystemUI/res/layout/quick_qs_status_icons.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java376
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java707
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java25
-rw-r--r--packages/overlays/AccentColorBlackOverlay/Android.mk2
-rw-r--r--packages/overlays/AccentColorCinnamonOverlay/Android.mk2
-rw-r--r--packages/overlays/AccentColorGreenOverlay/Android.mk2
-rw-r--r--packages/overlays/AccentColorOceanOverlay/Android.mk2
-rw-r--r--packages/overlays/AccentColorOrchidOverlay/Android.mk2
-rw-r--r--packages/overlays/AccentColorPurpleOverlay/Android.mk2
-rw-r--r--packages/overlays/AccentColorSpaceOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk2
-rw-r--r--packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk2
-rw-r--r--packages/overlays/FontNotoSerifSourceOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackCircularAndroidOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackCircularLauncherOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackCircularSystemUIOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackFilledAndroidOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackFilledLauncherOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackFilledSystemUIOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackRoundedAndroidOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackRoundedLauncherOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/Android.mk2
-rw-r--r--packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk2
-rw-r--r--packages/overlays/IconShapeRoundedRectOverlay/Android.mk2
-rw-r--r--packages/overlays/IconShapeSquareOverlay/Android.mk2
-rw-r--r--packages/overlays/IconShapeSquircleOverlay/Android.mk2
-rw-r--r--packages/overlays/IconShapeTeardropOverlay/Android.mk2
-rw-r--r--packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk2
-rw-r--r--packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk2
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlay/Android.mk2
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java24
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java50
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java44
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java9
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java171
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java6
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp64
-rw-r--r--services/java/com/android/server/SystemServer.java7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java31
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java67
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java2
-rw-r--r--telephony/java/android/telephony/UiccCardInfo.java4
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl2
75 files changed, 1689 insertions, 419 deletions
diff --git a/Android.bp b/Android.bp
index 0fcc0d501a1d..abf95a8ebc58 100644
--- a/Android.bp
+++ b/Android.bp
@@ -687,6 +687,7 @@ java_defaults {
"core/java/com/android/server/DropboxLogTags.logtags",
"core/java/org/chromium/arc/EventLogTags.logtags",
+ ":apex-properties",
":platform-properties",
":framework-statslog-gen",
@@ -1834,4 +1835,4 @@ aidl_mapping {
name: "framework-aidl-mappings",
srcs: [":framework-defaults"],
output: "framework-aidl-mappings.txt"
-} \ No newline at end of file
+}
diff --git a/api/current.txt b/api/current.txt
index ec7df66b0e62..8b24826a267b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38820,8 +38820,7 @@ package android.provider {
field @Deprecated public static final String LOCATION_MODE = "location_mode";
field @Deprecated public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
field @Deprecated public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
- field @Deprecated public static final int LOCATION_MODE_OFF = 0; // 0x0
- field @Deprecated public static final int LOCATION_MODE_ON = 3; // 0x3
+ field public static final int LOCATION_MODE_OFF = 0; // 0x0
field @Deprecated public static final int LOCATION_MODE_SENSORS_ONLY = 1; // 0x1
field @Deprecated public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
field @Deprecated public static final String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
diff --git a/api/system-current.txt b/api/system-current.txt
index 2ce1ee15d98d..e479c4874375 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6046,6 +6046,7 @@ package android.provider {
field public static final String LAST_SETUP_SHOWN = "last_setup_shown";
field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
+ field public static final int LOCATION_MODE_ON = 3; // 0x3
field public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE = "location_permissions_upgrade_to_q_mode";
field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 7349f0cbe55d..8596af107ac1 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -40,6 +40,7 @@ import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -225,6 +226,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
mAuthenticationCallback = callback;
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
+ Trace.beginSection("FaceManager#authenticate");
mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
flags, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -236,6 +238,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */));
}
+ } finally {
+ Trace.endSection();
}
}
}
@@ -276,6 +280,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
if (mService != null) {
try {
mEnrollmentCallback = callback;
+ Trace.beginSection("FaceManager#enroll");
mService.enroll(mToken, token, mServiceReceiver,
mContext.getOpPackageName(), disabledFeatures);
} catch (RemoteException e) {
@@ -287,6 +292,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */));
}
+ } finally {
+ Trace.endSection();
}
}
}
@@ -965,6 +972,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
@Override
public void handleMessage(android.os.Message msg) {
+ Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what));
switch (msg.what) {
case MSG_ENROLL_RESULT:
sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
@@ -1000,6 +1008,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
default:
Log.w(TAG, "Unknown message: " + msg.what);
}
+ Trace.endSection();
}
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 9215de14c21f..774d4ae789b4 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -159,6 +159,14 @@ public final class DeviceConfig {
public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
/**
+ * Definitions for properties related to Content Suggestions.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS =
+ "intelligence_content_suggestions";
+
+ /**
* Namespace for all media native related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4da0d2da8ee9..8a2180611a07 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1791,6 +1791,58 @@ public final class Settings {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS";
+ /**
+ * Broadcast to trigger notification of asking user to enable MMS.
+ * Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ENABLE_MMS_DATA_REQUEST =
+ "android.settings.ENABLE_MMS_DATA_REQUEST";
+
+ /**
+ * Integer value that specifies the reason triggering enable MMS data notification.
+ * This must be passed as an extra field to the {@link #ACTION_ENABLE_MMS_DATA_REQUEST}.
+ * Extra with value of EnableMmsDataReason interface.
+ * @hide
+ */
+ public static final String EXTRA_ENABLE_MMS_DATA_REQUEST_REASON =
+ "android.settings.extra.ENABLE_MMS_DATA_REQUEST_REASON";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ENABLE_MMS_DATA_REQUEST_REASON_" }, value = {
+ ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS,
+ ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS,
+ })
+ public @interface EnableMmsDataReason{}
+
+ /**
+ * Requesting to enable MMS data because there's an incoming MMS.
+ * @hide
+ */
+ public static final int ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS = 0;
+
+ /**
+ * Requesting to enable MMS data because user is sending MMS.
+ * @hide
+ */
+ public static final int ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS = 1;
+
+ /**
+ * Activity Action: Show screen of a cellular subscription and highlight the
+ * "enable MMS" toggle.
+ * <p>
+ * Input: {@link #EXTRA_SUB_ID}: Sub ID of the subscription.
+ * <p>
+ * Output: Nothing
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MMS_MESSAGE_SETTING = "android.settings.MMS_MESSAGE_SETTING";
+
// End of Intent actions for Settings
/**
@@ -6053,9 +6105,8 @@ public final class Settings {
"unknown_sources_default_reversed";
/**
- * Comma-separated list of location providers that are accessible. Do not rely on
- * this value being present or correct, or on ContentObserver notifications on the
- * corresponding Uri.
+ * Comma-separated list of location providers that are enabled. Do not rely on this value
+ * being present or correct, or on ContentObserver notifications on the corresponding Uri.
*
* @deprecated The preferred methods for checking provider status and listening for changes
* are via {@link LocationManager#isProviderEnabled(String)} and
@@ -6098,17 +6149,14 @@ public final class Settings {
/**
* Location mode is off.
- *
- * @deprecated See {@link #LOCATION_MODE}.
*/
- @Deprecated
public static final int LOCATION_MODE_OFF = 0;
/**
* This mode no longer has any distinct meaning, but is interpreted as the location mode is
* on.
*
- * @deprecated See {@link #LOCATION_MODE_ON}.
+ * @deprecated See {@link #LOCATION_MODE}.
*/
@Deprecated
public static final int LOCATION_MODE_SENSORS_ONLY = 1;
@@ -6117,7 +6165,7 @@ public final class Settings {
* This mode no longer has any distinct meaning, but is interpreted as the location mode is
* on.
*
- * @deprecated See {@link #LOCATION_MODE_ON}.
+ * @deprecated See {@link #LOCATION_MODE}.
*/
@Deprecated
public static final int LOCATION_MODE_BATTERY_SAVING = 2;
@@ -6126,7 +6174,7 @@ public final class Settings {
* This mode no longer has any distinct meaning, but is interpreted as the location mode is
* on.
*
- * @deprecated See {@link #LOCATION_MODE_ON}.
+ * @deprecated See {@link #LOCATION_MODE}.
*/
@Deprecated
public static final int LOCATION_MODE_HIGH_ACCURACY = 3;
@@ -6134,9 +6182,9 @@ public final class Settings {
/**
* Location mode is on.
*
- * @deprecated See {@link #LOCATION_MODE}.
+ * @hide
*/
- @Deprecated
+ @SystemApi
public static final int LOCATION_MODE_ON = LOCATION_MODE_HIGH_ACCURACY;
/**
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 204012f04cba..1b805aced0dd 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2030,7 +2030,8 @@ public class ChooserActivity extends ResolverActivity {
public static final int TARGET_STANDARD_AZ = 3;
private static final int MAX_SUGGESTED_APP_TARGETS = 4;
- private static final int MAX_TARGETS_PER_SERVICE = 2;
+ private static final int MAX_CHOOSER_TARGETS_PER_APP = 2;
+ private static final int MAX_SHORTCUT_TARGETS_PER_APP = 8;
private static final int MAX_SERVICE_TARGETS = 8;
@@ -2356,9 +2357,11 @@ public class ChooserActivity extends ResolverActivity {
final float baseScore = getBaseScore(origTarget, isShortcutResult);
Collections.sort(targets, mBaseTargetComparator);
+ final int maxTargets = isShortcutResult ? MAX_SHORTCUT_TARGETS_PER_APP
+ : MAX_CHOOSER_TARGETS_PER_APP;
float lastScore = 0;
boolean shouldNotify = false;
- for (int i = 0, N = Math.min(targets.size(), MAX_TARGETS_PER_SERVICE); i < N; i++) {
+ for (int i = 0, count = Math.min(targets.size(), maxTargets); i < count; i++) {
final ChooserTarget target = targets.get(i);
float targetScore = target.getScore();
targetScore *= baseScore;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ad3203ed96eb..04ccb74dae3c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3863,10 +3863,6 @@
{@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
<string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
- <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is
- not set, secondary home launcher can be replaced by user. -->
- <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool>
-
<!-- If device supports corner radius on windows.
This should be turned off on low-end devices to improve animation performance. -->
<bool name="config_supportsRoundedCornersOnWindows">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 085ce56292c6..bb560d324809 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3678,7 +3678,6 @@
<!-- For Secondary Launcher -->
<java-symbol type="string" name="config_secondaryHomeComponent" />
- <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" />
<java-symbol type="string" name="battery_saver_notification_channel_name" />
<java-symbol type="string" name="battery_saver_sticky_disabled_notification_title" />
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 7f69cf4d239c..5b7e7e7d59a3 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -19,8 +19,8 @@
android:id="@+id/quick_qs_status_icons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_header_top_margin"
- android:layout_marginBottom="14dp"
+ android:paddingTop="@dimen/qs_header_top_padding"
+ android:paddingBottom="@dimen/qs_header_bottom_padding"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end"
android:layout_below="@id/quick_status_bar_system_icons"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 955cfb022125..84d3a38c41f1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -492,7 +492,8 @@
<dimen name="qs_footer_padding_end">16dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
<dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen>
- <dimen name="qs_header_top_margin">15dp</dimen>
+ <dimen name="qs_header_top_padding">15dp</dimen>
+ <dimen name="qs_header_bottom_padding">14dp</dimen>
<dimen name="qs_notif_collapsed_space">64dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 1feb63d884fd..4b6306ad8fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -35,6 +35,7 @@ import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -296,6 +297,7 @@ public class Dependency extends SystemUI {
@Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper;
@Inject Lazy<SensorPrivacyController> mSensorPrivacyController;
@Inject Lazy<DumpController> mDumpController;
+ @Inject Lazy<DockManager> mDockManager;
@Inject
public Dependency() {
@@ -470,6 +472,7 @@ public class Dependency extends SystemUI {
mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get);
mProviders.put(DumpController.class, mDumpController::get);
+ mProviders.put(DockManager.class, mDockManager::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index ffb5e810fb29..f9926f3550ec 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -34,6 +34,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -179,6 +180,13 @@ public class SystemUIFactory {
@Singleton
@Provides
+ @Nullable
+ public DockManager provideDockManager(Context context) {
+ return null;
+ }
+
+ @Singleton
+ @Provides
public NotificationEntryManager provideNotificationEntryManager(Context context) {
return new NotificationEntryManager(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 03324777e4ea..7094d28c29f5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,9 +16,12 @@
package com.android.systemui.bubbles;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
import android.os.UserHandle;
import android.view.LayoutInflater;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -40,15 +43,24 @@ class Bubble {
public NotificationEntry entry;
BubbleView iconView;
BubbleExpandedView expandedView;
+ private long mLastUpdated;
+ private long mLastAccessed;
private static String groupId(NotificationEntry entry) {
UserHandle user = entry.notification.getUser();
- return user.getIdentifier() + '|' + entry.notification.getPackageName();
+ return user.getIdentifier() + "|" + entry.notification.getPackageName();
+ }
+
+ /** Used in tests when no UI is required. */
+ @VisibleForTesting(visibility = PRIVATE)
+ Bubble(NotificationEntry e) {
+ this (e, null);
}
Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) {
entry = e;
mKey = e.key;
+ mLastUpdated = e.notification.getPostTime();
mGroupId = groupId(e);
mListener = listener;
}
@@ -101,12 +113,37 @@ class Bubble {
void setEntry(NotificationEntry entry) {
this.entry = entry;
+ mLastUpdated = entry.notification.getPostTime();
if (mInflated) {
iconView.update(entry);
expandedView.update(entry);
}
}
+ public long getLastActivity() {
+ return Math.max(mLastUpdated, mLastAccessed);
+ }
+
+ /**
+ * Should be invoked whenever a Bubble is accessed (selected while expanded).
+ */
+ void markAsAccessedAt(long lastAccessedMillis) {
+ mLastAccessed = lastAccessedMillis;
+ entry.setShowInShadeWhenBubble(false);
+ }
+
+ /**
+ * @return whether bubble is from a notification associated with a foreground service.
+ */
+ public boolean isOngoing() {
+ return entry.isForegroundService();
+ }
+
+ @Override
+ public String toString() {
+ return "Bubble{" + mKey + '}';
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index ef383add644e..d0713636c540 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -41,6 +41,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
+import android.util.Log;
import android.view.Display;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
@@ -83,6 +84,7 @@ import javax.inject.Singleton;
public class BubbleController implements ConfigurationController.ConfigurationListener {
private static final String TAG = "BubbleController";
+ private static final boolean DEBUG = true;
@Retention(SOURCE)
@IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
@@ -203,6 +205,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
configurationController.addCallback(this /* configurationListener */);
+ mBubbleData = data;
+ mBubbleData.setListener(mBubbleDataListener);
+
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -219,9 +224,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
} catch (RemoteException e) {
e.printStackTrace();
}
-
- mBubbleData = data;
- mBubbleData.setListener(mBubbleDataListener);
mSurfaceSynchronizer = synchronizer;
mBarService = IStatusBarService.Stub.asInterface(
@@ -482,7 +484,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
@Override
- public void onSelectionChanged(Bubble selectedBubble) {
+ public void onSelectionChanged(@Nullable Bubble selectedBubble) {
if (mStackView != null) {
mStackView.setSelectedBubble(selectedBubble);
}
@@ -506,6 +508,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
public void apply() {
mNotificationEntryManager.updateNotifications();
updateVisibility();
+
+ if (DEBUG) {
+ Log.d(TAG, "[BubbleData]");
+ Log.d(TAG, formatBubblesString(mBubbleData.getBubbles(),
+ mBubbleData.getSelectedBubble()));
+
+ if (mStackView != null) {
+ Log.d(TAG, "[BubbleStackView]");
+ Log.d(TAG, formatBubblesString(mStackView.getBubblesOnScreen(),
+ mStackView.getExpandedBubble()));
+ }
+ }
}
};
@@ -623,6 +637,23 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
entry.setShowInShadeWhenBubble(!suppressNotification);
}
+ static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
+ StringBuilder sb = new StringBuilder();
+ for (Bubble bubble : bubbles) {
+ if (bubble == null) {
+ sb.append(" <null> !!!!!\n");
+ } else {
+ boolean isSelected = (bubble == selected);
+ sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
+ ((isSelected) ? "->" : " "),
+ bubble.getLastActivity(),
+ (bubble.isOngoing() ? 1 : 0),
+ bubble.getKey()));
+ }
+ }
+ return sb.toString();
+ }
+
/**
* Return true if the applications with the package name is running in foreground.
*
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 259665dedf5b..38ba91e0d81d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -17,21 +17,26 @@ package com.android.systemui.bubbles;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-import android.app.ActivityManager;
+import static java.util.stream.Collectors.toList;
+
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import javax.inject.Inject;
@@ -44,6 +49,15 @@ import javax.inject.Singleton;
public class BubbleData {
private static final String TAG = "BubbleData";
+ private static final boolean DEBUG = false;
+
+ private static final int MAX_BUBBLES = 5;
+
+ private static final Comparator<Bubble> BUBBLES_BY_LAST_ACTIVITY_DESCENDING =
+ Comparator.comparing(Bubble::getLastActivity).reversed();
+
+ private static final Comparator<Map.Entry<String, Long>> GROUPS_BY_LAST_ACTIVITY_DESCENDING =
+ Comparator.<Map.Entry<String, Long>, Long>comparing(Map.Entry::getValue).reversed();
/**
* This interface reports changes to the state and appearance of bubbles which should be applied
@@ -83,7 +97,7 @@ public class BubbleData {
void onOrderChanged(List<Bubble> bubbles);
/** Indicates the selected bubble changed. */
- void onSelectionChanged(Bubble selectedBubble);
+ void onSelectionChanged(@Nullable Bubble selectedBubble);
/**
* The UI should transition to the given state, incorporating any pending changes during
@@ -98,16 +112,28 @@ public class BubbleData {
void apply();
}
+ interface TimeSource {
+ long currentTimeMillis();
+ }
+
private final Context mContext;
- private final List<Bubble> mBubbles = new ArrayList<>();
+ private List<Bubble> mBubbles;
private Bubble mSelectedBubble;
private boolean mExpanded;
+
+ // TODO: ensure this is invalidated at the appropriate time
+ private int mSelectedBubbleExpandedPosition = -1;
+
+ private TimeSource mTimeSource = System::currentTimeMillis;
+
+ @Nullable
private Listener mListener;
@VisibleForTesting
@Inject
public BubbleData(Context context) {
mContext = context;
+ mBubbles = new ArrayList<>();
}
public boolean hasBubbles() {
@@ -122,29 +148,41 @@ public class BubbleData {
return getBubbleWithKey(key) != null;
}
+ @Nullable
+ public Bubble getSelectedBubble() {
+ return mSelectedBubble;
+ }
+
public void setExpanded(boolean expanded) {
if (setExpandedInternal(expanded)) {
- mListener.apply();
+ dispatchApply();
}
}
public void setSelectedBubble(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "setSelectedBubble: " + bubble);
+ }
if (setSelectedBubbleInternal(bubble)) {
- mListener.apply();
+ dispatchApply();
}
}
public void notificationEntryUpdated(NotificationEntry entry) {
+ if (DEBUG) {
+ Log.d(TAG, "notificationEntryUpdated: " + entry);
+ }
Bubble bubble = getBubbleWithKey(entry.key);
if (bubble == null) {
// Create a new bubble
bubble = new Bubble(entry, this::onBubbleBlocked);
- mBubbles.add(0, bubble); // TODO: reorder/group
- mListener.onBubbleAdded(bubble);
+ doAdd(bubble);
+ dispatchOnBubbleAdded(bubble);
} else {
// Updates an existing bubble
bubble.setEntry(entry);
- mListener.onBubbleUpdated(bubble);
+ doUpdate(bubble);
+ dispatchOnBubbleUpdated(bubble);
}
if (shouldAutoExpand(entry)) {
setSelectedBubbleInternal(bubble);
@@ -154,46 +192,145 @@ public class BubbleData {
} else if (mSelectedBubble == null) {
setSelectedBubbleInternal(bubble);
}
- // TODO: reorder/group
- mListener.apply();
+ dispatchApply();
+ }
+
+ private void doAdd(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "doAdd: " + bubble);
+ }
+ int minInsertPoint = 0;
+ boolean newGroup = !hasBubbleWithGroupId(bubble.getGroupId());
+ if (isExpanded()) {
+ // first bubble of a group goes to the end, otherwise it goes within the existing group
+ minInsertPoint =
+ newGroup ? mBubbles.size() : findFirstIndexForGroup(bubble.getGroupId());
+ }
+ insertBubble(minInsertPoint, bubble);
+ if (!isExpanded()) {
+ packGroup(findFirstIndexForGroup(bubble.getGroupId()));
+ }
+ if (mBubbles.size() > MAX_BUBBLES) {
+ mBubbles.stream()
+ // sort oldest first (ascending lastActivity)
+ .sorted(Comparator.comparingLong(Bubble::getLastActivity))
+ // skip the selected bubble
+ .filter((b) -> !b.equals(mSelectedBubble))
+ .findFirst()
+ .ifPresent((b) -> {
+ doRemove(b.getKey(), BubbleController.DISMISS_AGED);
+ dispatchApply();
+ });
+ }
+ }
+
+ private void doUpdate(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "doUpdate: " + bubble);
+ }
+ if (!isExpanded()) {
+ // while collapsed, update causes re-sort
+ mBubbles.remove(bubble);
+ insertBubble(0, bubble);
+ packGroup(findFirstIndexForGroup(bubble.getGroupId()));
+ }
}
public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
- int indexToRemove = indexForKey(entry.key);
- if (indexToRemove >= 0) {
- Bubble removed = mBubbles.remove(indexToRemove);
- removed.setDismissed();
- mListener.onBubbleRemoved(removed, reason);
- maybeSendDeleteIntent(reason, removed.entry);
+ if (DEBUG) {
+ Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason);
+ }
+ doRemove(entry.key, reason);
+ dispatchApply();
+ }
- if (mBubbles.isEmpty()) {
+ private void doRemove(String key, @DismissReason int reason) {
+ int indexToRemove = indexForKey(key);
+ if (indexToRemove >= 0) {
+ Bubble bubbleToRemove = mBubbles.get(indexToRemove);
+ if (mBubbles.size() == 1) {
+ // Going to become empty, handle specially.
setExpandedInternal(false);
setSelectedBubbleInternal(null);
- } else if (removed == mSelectedBubble) {
+ }
+ mBubbles.remove(indexToRemove);
+ dispatchOnBubbleRemoved(bubbleToRemove, reason);
+
+ // Note: If mBubbles.isEmpty(), then mSelectedBubble is now null.
+ if (Objects.equals(mSelectedBubble, bubbleToRemove)) {
+ // Move selection to the new bubble at the same position.
int newIndex = Math.min(indexToRemove, mBubbles.size() - 1);
Bubble newSelected = mBubbles.get(newIndex);
setSelectedBubbleInternal(newSelected);
}
- // TODO: reorder/group
- mListener.apply();
+ bubbleToRemove.setDismissed();
+ maybeSendDeleteIntent(reason, bubbleToRemove.entry);
}
}
public void dismissAll(@DismissReason int reason) {
- boolean changed = setExpandedInternal(false);
+ if (DEBUG) {
+ Log.d(TAG, "dismissAll: reason=" + reason);
+ }
+ if (mBubbles.isEmpty()) {
+ return;
+ }
+ setExpandedInternal(false);
+ setSelectedBubbleInternal(null);
while (!mBubbles.isEmpty()) {
Bubble bubble = mBubbles.remove(0);
bubble.setDismissed();
maybeSendDeleteIntent(reason, bubble.entry);
+ dispatchOnBubbleRemoved(bubble, reason);
+ }
+ dispatchApply();
+ }
+
+ private void dispatchApply() {
+ if (mListener != null) {
+ mListener.apply();
+ }
+ }
+
+ private void dispatchOnBubbleAdded(Bubble bubble) {
+ if (mListener != null) {
+ mListener.onBubbleAdded(bubble);
+ }
+ }
+
+ private void dispatchOnBubbleRemoved(Bubble bubble, @DismissReason int reason) {
+ if (mListener != null) {
mListener.onBubbleRemoved(bubble, reason);
- changed = true;
}
- if (setSelectedBubbleInternal(null)) {
- changed = true;
+ }
+
+ private void dispatchOnExpandedChanged(boolean expanded) {
+ if (mListener != null) {
+ mListener.onExpandedChanged(expanded);
}
- if (changed) {
- // TODO: reorder/group
- mListener.apply();
+ }
+
+ private void dispatchOnSelectionChanged(@Nullable Bubble bubble) {
+ if (mListener != null) {
+ mListener.onSelectionChanged(bubble);
+ }
+ }
+
+ private void dispatchOnBubbleUpdated(Bubble bubble) {
+ if (mListener != null) {
+ mListener.onBubbleUpdated(bubble);
+ }
+ }
+
+ private void dispatchOnOrderChanged(List<Bubble> bubbles) {
+ if (mListener != null) {
+ mListener.onOrderChanged(bubbles);
+ }
+ }
+
+ private void dispatchShowFlyoutText(Bubble bubble, String text) {
+ if (mListener != null) {
+ mListener.showFlyoutText(bubble, text);
}
}
@@ -204,7 +341,10 @@ public class BubbleData {
* @param bubble the new selected bubble
* @return true if the state changed as a result
*/
- private boolean setSelectedBubbleInternal(Bubble bubble) {
+ private boolean setSelectedBubbleInternal(@Nullable Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "setSelectedBubbleInternal: " + bubble);
+ }
if (Objects.equals(bubble, mSelectedBubble)) {
return false;
}
@@ -213,16 +353,17 @@ public class BubbleData {
+ " (" + bubble + ") bubbles=" + mBubbles);
return false;
}
- if (mExpanded) {
- // TODO: bubble.markAsActive() ?
- bubble.entry.setShowInShadeWhenBubble(false);
+ if (mExpanded && bubble != null) {
+ mSelectedBubble.markAsAccessedAt(mTimeSource.currentTimeMillis());
}
mSelectedBubble = bubble;
- mListener.onSelectionChanged(mSelectedBubble);
+ dispatchOnSelectionChanged(mSelectedBubble);
+ if (!mExpanded || mSelectedBubble == null) {
+ mSelectedBubbleExpandedPosition = -1;
+ }
return true;
}
-
/**
* Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if
* the value changes.
@@ -231,9 +372,15 @@ public class BubbleData {
* @return true if the state changed as a result
*/
private boolean setExpandedInternal(boolean shouldExpand) {
+ if (DEBUG) {
+ Log.d(TAG, "setExpandedInternal: shouldExpand=" + shouldExpand);
+ }
if (mExpanded == shouldExpand) {
return false;
}
+ if (mSelectedBubble != null) {
+ mSelectedBubble.markAsAccessedAt(mTimeSource.currentTimeMillis());
+ }
if (shouldExpand) {
if (mBubbles.isEmpty()) {
Log.e(TAG, "Attempt to expand stack when empty!");
@@ -243,15 +390,126 @@ public class BubbleData {
Log.e(TAG, "Attempt to expand stack without selected bubble!");
return false;
}
- // TODO: bubble.markAsActive() ?
- mSelectedBubble.entry.setShowInShadeWhenBubble(false);
+ } else {
+ repackAll();
}
- // TODO: reorder/regroup
mExpanded = shouldExpand;
- mListener.onExpandedChanged(mExpanded);
+ dispatchOnExpandedChanged(mExpanded);
return true;
}
+ private static long sortKey(Bubble bubble) {
+ long key = bubble.getLastActivity();
+ if (bubble.isOngoing()) {
+ // Set 2nd highest bit (signed long int), to partition between ongoing and regular
+ key |= 0x4000000000000000L;
+ }
+ return key;
+ }
+
+ /**
+ * Locates and inserts the bubble into a sorted position. The is inserted
+ * based on sort key, groupId is not considered. A call to {@link #packGroup(int)} may be
+ * required to keep grouping intact.
+ *
+ * @param minPosition the first insert point to consider
+ * @param newBubble the bubble to insert
+ * @return the position where the bubble was inserted
+ */
+ private int insertBubble(int minPosition, Bubble newBubble) {
+ long newBubbleSortKey = sortKey(newBubble);
+ String previousGroupId = null;
+
+ for (int pos = minPosition; pos < mBubbles.size(); pos++) {
+ Bubble bubbleAtPos = mBubbles.get(pos);
+ String groupIdAtPos = bubbleAtPos.getGroupId();
+ boolean atStartOfGroup = !groupIdAtPos.equals(previousGroupId);
+
+ if (atStartOfGroup && newBubbleSortKey > sortKey(bubbleAtPos)) {
+ // Insert before the start of first group which has older bubbles.
+ mBubbles.add(pos, newBubble);
+ return pos;
+ }
+ previousGroupId = groupIdAtPos;
+ }
+ mBubbles.add(newBubble);
+ return mBubbles.size() - 1;
+ }
+
+ private boolean hasBubbleWithGroupId(String groupId) {
+ return mBubbles.stream().anyMatch(b -> b.getGroupId().equals(groupId));
+ }
+
+ private int findFirstIndexForGroup(String appId) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubbleAtPos = mBubbles.get(i);
+ if (bubbleAtPos.getGroupId().equals(appId)) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Starting at the given position, moves all bubbles with the same group id to follow. Bubbles
+ * at positions lower than {@code position} are unchanged. Relative order within the group
+ * unchanged. Relative order of any other bubbles are also unchanged.
+ *
+ * @param position the position of the first bubble for the group
+ */
+ private void packGroup(int position) {
+ if (DEBUG) {
+ Log.d(TAG, "packGroup: position=" + position);
+ }
+ Bubble groupStart = mBubbles.get(position);
+ final String groupAppId = groupStart.getGroupId();
+ List<Bubble> moving = new ArrayList<>();
+
+ // Walk backward, collect bubbles within the group
+ for (int i = mBubbles.size() - 1; i > position; i--) {
+ if (mBubbles.get(i).getGroupId().equals(groupAppId)) {
+ moving.add(0, mBubbles.get(i));
+ }
+ }
+ mBubbles.removeAll(moving);
+ mBubbles.addAll(position + 1, moving);
+ }
+
+ private void repackAll() {
+ if (DEBUG) {
+ Log.d(TAG, "repackAll()");
+ }
+ if (mBubbles.isEmpty()) {
+ return;
+ }
+ Map<String, Long> groupLastActivity = new HashMap<>();
+ for (Bubble bubble : mBubbles) {
+ long maxSortKeyForGroup = groupLastActivity.getOrDefault(bubble.getGroupId(), 0L);
+ long sortKeyForBubble = sortKey(bubble);
+ if (sortKeyForBubble > maxSortKeyForGroup) {
+ groupLastActivity.put(bubble.getGroupId(), sortKeyForBubble);
+ }
+ }
+
+ // Sort groups by their most recently active bubble
+ List<String> groupsByMostRecentActivity =
+ groupLastActivity.entrySet().stream()
+ .sorted(GROUPS_BY_LAST_ACTIVITY_DESCENDING)
+ .map(Map.Entry::getKey)
+ .collect(toList());
+
+ List<Bubble> repacked = new ArrayList<>(mBubbles.size());
+
+ // For each group, add bubbles, freshest to oldest
+ for (String appId : groupsByMostRecentActivity) {
+ mBubbles.stream()
+ .filter((b) -> b.getGroupId().equals(appId))
+ .sorted(BUBBLES_BY_LAST_ACTIVITY_DESCENDING)
+ .forEachOrdered(repacked::add);
+ }
+ mBubbles = repacked;
+ }
+
private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) {
if (reason == BubbleController.DISMISS_USER_GESTURE) {
Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
@@ -275,13 +533,14 @@ public class BubbleData {
Bubble bubble = i.next();
if (bubble.getPackageName().equals(blockedPackage)) {
i.remove();
- mListener.onBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED);
+ // TODO: handle removal of selected bubble, and collapse safely if emptied (see
+ // dismissAll)
+ dispatchOnBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED);
changed = true;
}
}
if (changed) {
- // TODO: reorder/group
- mListener.apply();
+ dispatchApply();
}
}
@@ -295,24 +554,11 @@ public class BubbleData {
return -1;
}
- private Bubble removeBubbleWithKey(String key) {
- for (int i = 0; i < mBubbles.size(); i++) {
- Bubble bubble = mBubbles.get(i);
- if (bubble.getKey().equals(key)) {
- mBubbles.remove(i);
- return bubble;
- }
- }
- return null;
- }
-
/**
* The set of bubbles.
- *
- * @deprecated
*/
- @Deprecated
- public Collection<Bubble> getBubbles() {
+ @VisibleForTesting(visibility = PRIVATE)
+ public List<Bubble> getBubbles() {
return Collections.unmodifiableList(mBubbles);
}
@@ -327,6 +573,11 @@ public class BubbleData {
return null;
}
+ @VisibleForTesting(visibility = PRIVATE)
+ void setTimeSource(TimeSource timeSource) {
+ mTimeSource = timeSource;
+ }
+
public void setListener(Listener listener) {
mListener = listener;
}
@@ -334,17 +585,6 @@ public class BubbleData {
boolean shouldAutoExpand(NotificationEntry entry) {
Notification.BubbleMetadata metadata = entry.getBubbleMetadata();
return metadata != null && metadata.getAutoExpandBubble()
- && isForegroundApp(entry.notification.getPackageName());
- }
-
- /**
- * Return true if the applications with the package name is running in foreground.
- *
- * @param pkgName application package name.
- */
- boolean isForegroundApp(String pkgName) {
- ActivityManager am = mContext.getSystemService(ActivityManager.class);
- List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */);
- return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+ && BubbleController.isForegroundApp(mContext, entry.notification.getPackageName());
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 7029931f72d6..d1bc9a91636c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -66,6 +66,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -227,7 +228,7 @@ public class BubbleStackView extends FrameLayout {
mBubbleData = data;
mInflater = LayoutInflater.from(context);
- mTouchHandler = new BubbleTouchHandler(context, this);
+ mTouchHandler = new BubbleTouchHandler(this, data, context);
setOnTouchListener(mTouchHandler);
mInflater = LayoutInflater.from(context);
@@ -503,6 +504,9 @@ public class BubbleStackView extends FrameLayout {
// via BubbleData.Listener
void addBubble(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "addBubble: " + bubble);
+ }
bubble.inflate(mInflater, this);
mBubbleContainer.addView(bubble.iconView, 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
@@ -513,10 +517,17 @@ public class BubbleStackView extends FrameLayout {
// via BubbleData.Listener
void removeBubble(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "removeBubble: " + bubble);
+ }
// Remove it from the views
int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView);
- mBubbleContainer.removeViewAt(removedIndex);
- logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ if (removedIndex >= 0) {
+ mBubbleContainer.removeViewAt(removedIndex);
+ logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ } else {
+ Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
+ }
}
// via BubbleData.Listener
@@ -531,7 +542,10 @@ public class BubbleStackView extends FrameLayout {
* position of any bubble.
*/
// via BubbleData.Listener
- public void setSelectedBubble(Bubble bubbleToSelect) {
+ public void setSelectedBubble(@Nullable Bubble bubbleToSelect) {
+ if (DEBUG) {
+ Log.d(TAG, "setSelectedBubble: " + bubbleToSelect);
+ }
if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) {
return;
}
@@ -562,6 +576,9 @@ public class BubbleStackView extends FrameLayout {
*/
// via BubbleData.Listener
public void setExpanded(boolean shouldExpand) {
+ if (DEBUG) {
+ Log.d(TAG, "setExpanded: " + shouldExpand);
+ }
boolean wasExpanded = mIsExpanded;
if (shouldExpand == wasExpanded) {
return;
@@ -586,6 +603,9 @@ public class BubbleStackView extends FrameLayout {
*/
@Deprecated
void stackDismissed(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "stackDismissed: reason=" + reason);
+ }
mBubbleData.dismissAll(reason);
logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
@@ -633,6 +653,9 @@ public class BubbleStackView extends FrameLayout {
@Deprecated
@MainThread
void collapseStack() {
+ if (DEBUG) {
+ Log.d(TAG, "collapseStack()");
+ }
mBubbleData.setExpanded(false);
}
@@ -642,6 +665,9 @@ public class BubbleStackView extends FrameLayout {
@Deprecated
@MainThread
void collapseStack(Runnable endRunnable) {
+ if (DEBUG) {
+ Log.d(TAG, "collapseStack(endRunnable)");
+ }
collapseStack();
// TODO - use the runnable at end of animation
endRunnable.run();
@@ -657,6 +683,9 @@ public class BubbleStackView extends FrameLayout {
@Deprecated
@MainThread
void expandStack() {
+ if (DEBUG) {
+ Log.d(TAG, "expandStack()");
+ }
mBubbleData.setExpanded(true);
}
@@ -664,6 +693,9 @@ public class BubbleStackView extends FrameLayout {
* Tell the stack to animate to collapsed or expanded state.
*/
private void animateExpansion(boolean shouldExpand) {
+ if (DEBUG) {
+ Log.d(TAG, "animateExpansion: shouldExpand=" + shouldExpand);
+ }
if (mIsExpanded != shouldExpand) {
hideFlyoutImmediate();
@@ -745,6 +777,9 @@ public class BubbleStackView extends FrameLayout {
/** Called when a drag operation on an individual bubble has started. */
public void onBubbleDragStart(View bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "onBubbleDragStart: bubble=" + bubble);
+ }
mExpandedAnimationController.prepareForBubbleDrag(bubble);
}
@@ -760,6 +795,9 @@ public class BubbleStackView extends FrameLayout {
/** Called when a drag operation on an individual bubble has finished. */
public void onBubbleDragFinish(
View bubble, float x, float y, float velX, float velY, boolean dismissed) {
+ if (DEBUG) {
+ Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble + ", dismissed=" + dismissed);
+ }
if (!mIsExpanded || mIsExpansionAnimating) {
return;
}
@@ -772,6 +810,9 @@ public class BubbleStackView extends FrameLayout {
}
void onDragStart() {
+ if (DEBUG) {
+ Log.d(TAG, "onDragStart()");
+ }
if (mIsExpanded || mIsExpansionAnimating) {
return;
}
@@ -792,6 +833,9 @@ public class BubbleStackView extends FrameLayout {
}
void onDragFinish(float x, float y, float velX, float velY) {
+ if (DEBUG) {
+ Log.d(TAG, "onDragFinish");
+ }
// TODO: Add fling to bottom to dismiss.
mIsDragging = false;
@@ -958,6 +1002,9 @@ public class BubbleStackView extends FrameLayout {
}
private void updateExpandedBubble() {
+ if (DEBUG) {
+ Log.d(TAG, "updateExpandedBubble()");
+ }
mExpandedViewContainer.removeAllViews();
if (mExpandedBubble != null && mIsExpanded) {
mExpandedViewContainer.addView(mExpandedBubble.expandedView);
@@ -1036,7 +1083,9 @@ public class BubbleStackView extends FrameLayout {
}
private void applyCurrentState() {
- Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
+ if (DEBUG) {
+ Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
+ }
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (mIsExpanded) {
// First update the view so that it calculates a new height (ensuring the y position
@@ -1075,10 +1124,14 @@ public class BubbleStackView extends FrameLayout {
}
private void updatePointerPosition() {
- if (mExpandedBubble != null) {
- float pointerPosition = mExpandedBubble.iconView.getTranslationX()
- + (mExpandedBubble.iconView.getWidth() / 2f);
- mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition);
+ if (DEBUG) {
+ Log.d(TAG, "updatePointerPosition()");
+ }
+ Bubble expandedBubble = getExpandedBubble();
+ if (expandedBubble != null) {
+ BubbleView iconView = expandedBubble.iconView;
+ float pointerPosition = iconView.getTranslationX() + (iconView.getWidth() / 2f);
+ expandedBubble.expandedView.setPointerPosition((int) pointerPosition);
}
}
@@ -1174,4 +1227,18 @@ public class BubbleStackView extends FrameLayout {
}
return mExpandedBubble.expandedView.performBackPressIfNeeded();
}
+
+ /** For debugging only */
+ List<Bubble> getBubblesOnScreen() {
+ List<Bubble> bubbles = new ArrayList<>();
+ for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ View child = mBubbleContainer.getChildAt(i);
+ if (child instanceof BubbleView) {
+ String key = ((BubbleView) child).getKey();
+ Bubble bubble = mBubbleData.getBubbleWithKey(key);
+ bubbles.add(bubble);
+ }
+ }
+ return bubbles;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index a51d46c0a848..82e6279772f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -37,9 +37,12 @@ class BubbleTouchHandler implements View.OnTouchListener {
/** Velocity required to dismiss a bubble without dragging it into the dismiss target. */
private static final float DISMISS_MIN_VELOCITY = 4000f;
+ private static final String TAG = "BubbleTouchHandler";
+
private final PointF mTouchDown = new PointF();
private final PointF mViewPositionOnTouchDown = new PointF();
private final BubbleStackView mStack;
+ private final BubbleData mBubbleData;
private BubbleController mController = Dependency.get(BubbleController.class);
private PipDismissViewController mDismissViewController;
@@ -60,10 +63,12 @@ class BubbleTouchHandler implements View.OnTouchListener {
/** View that was initially touched, when we received the first ACTION_DOWN event. */
private View mTouchedView;
- BubbleTouchHandler(Context context, BubbleStackView stackView) {
+ BubbleTouchHandler(BubbleStackView stackView,
+ BubbleData bubbleData, Context context) {
final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mTouchSlopSquared = touchSlop * touchSlop;
mDismissViewController = new PipDismissViewController(context);
+ mBubbleData = bubbleData;
mStack = stackView;
}
@@ -80,7 +85,7 @@ class BubbleTouchHandler implements View.OnTouchListener {
// If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching
// anything, collapse the stack.
if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
- mStack.collapseStack();
+ mBubbleData.setExpanded(false);
resetForNextGesture();
return false;
}
@@ -151,8 +156,8 @@ class BubbleTouchHandler implements View.OnTouchListener {
mStack.onDragFinishAsDismiss();
} else if (isFlyout) {
// TODO(b/129768381): Expand if tapped, dismiss if swiped away.
- if (!mStack.isExpanded() && !mMovedEnough) {
- mStack.expandStack();
+ if (!mBubbleData.isExpanded() && !mMovedEnough) {
+ mBubbleData.setExpanded(true);
}
} else if (mMovedEnough) {
mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
@@ -170,15 +175,13 @@ class BubbleTouchHandler implements View.OnTouchListener {
}
}
} else if (mTouchedView == mStack.getExpandedBubbleView()) {
- mStack.collapseStack();
+ mBubbleData.setExpanded(false);
} else if (isStack) {
- if (mStack.isExpanded()) {
- mStack.collapseStack();
- } else {
- mStack.expandStack();
- }
+ // Toggle expansion
+ mBubbleData.setExpanded(!mBubbleData.isExpanded());
} else {
- mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey());
+ final String key = ((BubbleView) mTouchedView).getKey();
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key));
}
resetForNextGesture();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 060765495f48..5196ec639453 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -27,7 +27,6 @@ import android.os.Handler;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.dock.DockManager;
@@ -46,7 +45,7 @@ public class DozeFactory {
Context context = dozeService;
SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
- DockManager dockManager = SysUiServiceProvider.getComponent(context, DockManager.class);
+ DockManager dockManager = Dependency.get(DockManager.class);
DozeHost host = getHost(dozeService);
AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 586e82c612c8..a831a5d29a79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -31,10 +31,13 @@ import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
+import androidx.annotation.Nullable;
+
import com.android.internal.graphics.ColorUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -62,6 +65,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private final UnlockMethodCache mUnlockMethodCache;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AccessibilityController mAccessibilityController;
+ private final DockManager mDockManager;
private int mLastState = 0;
private boolean mTransientBiometricsError;
@@ -72,13 +76,26 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private boolean mPulsing;
private boolean mDozing;
private boolean mBouncerVisible;
+ private boolean mDocked;
private boolean mLastDozing;
private boolean mLastPulsing;
private boolean mLastBouncerVisible;
private int mIconColor;
+ private float mDozeAmount;
private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
- private float mDozeAmount;
+ private final DockManager.DockEventListener mDockEventListener =
+ new DockManager.DockEventListener() {
+ @Override
+ public void onEvent(int event) {
+ boolean docked = event == DockManager.STATE_DOCKED
+ || event == DockManager.STATE_DOCKED_HIDE;
+ if (docked != mDocked) {
+ mDocked = docked;
+ update(true /* force */);
+ }
+ }
+ };
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@@ -115,7 +132,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
- AccessibilityController accessibilityController) {
+ AccessibilityController accessibilityController,
+ @Nullable DockManager dockManager) {
super(context, attrs);
mContext = context;
mUnlockMethodCache = UnlockMethodCache.getInstance(context);
@@ -123,6 +141,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
mAccessibilityController = accessibilityController;
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
+ mDockManager = dockManager;
}
@Override
@@ -132,6 +151,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
mConfigurationController.addCallback(this);
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
mUnlockMethodCache.addListener(this);
+ if (mDockManager != null) {
+ mDockManager.addListener(mDockEventListener);
+ }
onThemeChanged();
}
@@ -142,6 +164,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
mConfigurationController.removeCallback(this);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
mUnlockMethodCache.removeListener(this);
+ if (mDockManager != null) {
+ mDockManager.removeListener(mDockEventListener);
+ }
}
@Override
@@ -237,7 +262,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
mLastBouncerVisible = mBouncerVisible;
}
- setVisibility(mDozing && !mPulsing ? INVISIBLE : VISIBLE);
+ boolean invisible = mDozing && (!mPulsing || mDocked);
+ setVisibility(invisible ? INVISIBLE : VISIBLE);
updateClickability();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index 79c7ab1d2b8c..4603ba6af89f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -23,6 +23,7 @@ import android.graphics.Canvas;;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
+import android.os.SystemClock;
import android.os.VibrationEffect;
import android.util.MathUtils;
import android.view.ContextThemeWrapper;
@@ -51,6 +52,11 @@ public class NavigationBarEdgePanel extends View {
private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100;
/**
+ * The minimum time required since the first vibration effect to receive a second one
+ */
+ private static final int MIN_TIME_BETWEEN_EFFECTS_MS = 120;
+
+ /**
* The size of the protection of the arrow in px. Only used if this is not background protected
*/
private static final int PROTECTION_WIDTH_PX = 2;
@@ -182,6 +188,7 @@ public class NavigationBarEdgePanel extends View {
private int mArrowStartColor;
private int mCurrentArrowColor;
private float mDisappearAmount;
+ private long mVibrationTime;
private DynamicAnimation.OnAnimationEndListener mSetGoneEndListener
= new DynamicAnimation.OnAnimationEndListener() {
@@ -394,7 +401,7 @@ public class NavigationBarEdgePanel extends View {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
if (mTriggerBack) {
- triggerBackAnimation();
+ triggerBack();
} else {
if (mTranslationAnimation.isRunning()) {
mTranslationAnimation.addEndListener(mSetGoneEndListener);
@@ -521,8 +528,10 @@ public class NavigationBarEdgePanel extends View {
return mCurrentTranslation;
}
- private void triggerBackAnimation() {
-
+ private void triggerBack() {
+ if (SystemClock.uptimeMillis() - mVibrationTime >= MIN_TIME_BETWEEN_EFFECTS_MS) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK);
+ }
mVelocityTracker.computeCurrentVelocity(1000);
// Only do the extra translation if we're not already flinging
boolean doExtraTranslation = Math.abs(mVelocityTracker.getXVelocity()) < 1000;
@@ -577,6 +586,7 @@ public class NavigationBarEdgePanel extends View {
setCurrentTranslation(0);
mPreviousTouchTranslation = 0;
mTotalTouchDelta = 0;
+ mVibrationTime = 0;
setDesiredVerticalTransition(0, false /* animated */);
}
@@ -599,6 +609,7 @@ public class NavigationBarEdgePanel extends View {
if (!mDragSlopPassed && touchTranslation > mSwipeThreshold) {
mDragSlopPassed = true;
mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ mVibrationTime = SystemClock.uptimeMillis();
// Let's show the arrow and animate it in!
mDisappearAmount = 0.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 002313c227c6..7fe89069b438 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -25,8 +25,6 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.os.SystemClock;
-import android.transition.Fade;
-import android.transition.TransitionManager;
import android.util.StatsLog;
import android.view.KeyEvent;
import android.view.View;
@@ -40,10 +38,8 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.settingslib.animation.AppearAnimationUtils;
-import com.android.settingslib.animation.DisappearAnimationUtils;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.dock.DockManager;
@@ -51,6 +47,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
@@ -158,7 +155,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private boolean mLastPulsing;
private int mLastBiometricMode;
private boolean mGoingToSleepVisibleNotOccluded;
- private int mLastLockVisibility = -1;
+ private boolean mLastLockVisible;
private OnDismissAction mAfterKeyguardGoneAction;
private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
@@ -213,6 +210,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mStatusBar = statusBar;
mContainer = container;
mLockIconContainer = lockIconContainer;
+ if (mLockIconContainer != null) {
+ mLastLockVisible = mLockIconContainer.getVisibility() == View.VISIBLE;
+ }
mBiometricUnlockController = biometricUnlockController;
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
@@ -261,21 +261,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD
&& !mNotificationPanelView.isQsExpanded();
- int lockVisibility = (mBouncer.isShowing() || keyguardWithoutQs)
- && !mBouncer.isAnimatingAway() ? View.VISIBLE : View.INVISIBLE;
-
- if (mLastLockVisibility != lockVisibility) {
- mLastLockVisibility = lockVisibility;
-
- Fade transition = new Fade();
- boolean appearing = lockVisibility == View.VISIBLE;
- transition.setDuration(appearing ? AppearAnimationUtils.DEFAULT_APPEAR_DURATION
- : DisappearAnimationUtils.DEFAULT_APPEAR_DURATION / 2);
- transition.setInterpolator(appearing ? Interpolators.ALPHA_IN
- : Interpolators.ALPHA_OUT);
- TransitionManager.beginDelayedTransition((ViewGroup) mLockIconContainer.getParent(),
- transition);
- mLockIconContainer.setVisibility(lockVisibility);
+ boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs)
+ && !mBouncer.isAnimatingAway();
+
+ if (mLastLockVisible != lockVisible) {
+ mLastLockVisible = lockVisible;
+ if (lockVisible) {
+ CrossFadeHelper.fadeIn(mLockIconContainer,
+ AppearAnimationUtils.DEFAULT_APPEAR_DURATION /* duration */,
+ 0 /* delay */);
+ } else {
+ CrossFadeHelper.fadeOut(mLockIconContainer,
+ AppearAnimationUtils.DEFAULT_APPEAR_DURATION / 2 /* duration */,
+ 0 /* delay */, null /* runnable */);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
new file mode 100644
index 000000000000..d6dac2f36ba1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2019 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.bubbles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+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.app.Notification;
+import android.app.PendingIntent;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleData.TimeSource;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BubbleDataTest extends SysuiTestCase {
+
+ private NotificationEntry mEntryA1;
+ private NotificationEntry mEntryA2;
+ private NotificationEntry mEntryA3;
+ private NotificationEntry mEntryB1;
+ private NotificationEntry mEntryB2;
+ private NotificationEntry mEntryB3;
+ private NotificationEntry mEntryC1;
+
+ private Bubble mBubbleA1;
+ private Bubble mBubbleA2;
+ private Bubble mBubbleA3;
+ private Bubble mBubbleB1;
+ private Bubble mBubbleB2;
+ private Bubble mBubbleB3;
+ private Bubble mBubbleC1;
+
+ private BubbleData mBubbleData;
+
+ @Mock
+ private TimeSource mTimeSource;
+ @Mock
+ private BubbleData.Listener mListener;
+ @Mock
+ private PendingIntent mExpandIntent;
+ @Mock
+ private PendingIntent mDeleteIntent;
+
+ private NotificationTestHelper mNotificationTestHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ mNotificationTestHelper = new NotificationTestHelper(mContext);
+ MockitoAnnotations.initMocks(this);
+
+ mEntryA1 = createBubbleEntry(1, "a1", "package.a");
+ mEntryA2 = createBubbleEntry(1, "a2", "package.a");
+ mEntryA3 = createBubbleEntry(1, "a3", "package.a");
+ mEntryB1 = createBubbleEntry(1, "b1", "package.b");
+ mEntryB2 = createBubbleEntry(1, "b2", "package.b");
+ mEntryB3 = createBubbleEntry(1, "b3", "package.b");
+ mEntryC1 = createBubbleEntry(1, "c1", "package.c");
+
+ mBubbleA1 = new Bubble(mEntryA1);
+ mBubbleA2 = new Bubble(mEntryA2);
+ mBubbleA3 = new Bubble(mEntryA3);
+ mBubbleB1 = new Bubble(mEntryB1);
+ mBubbleB2 = new Bubble(mEntryB2);
+ mBubbleB3 = new Bubble(mEntryB3);
+ mBubbleC1 = new Bubble(mEntryC1);
+
+ mBubbleData = new BubbleData(getContext());
+
+ // Used by BubbleData to set lastAccessedTime
+ when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
+ mBubbleData.setTimeSource(mTimeSource);
+ }
+
+ private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
+ return createBubbleEntry(userId, notifKey, packageName, 1000);
+ }
+
+ private void setPostTime(NotificationEntry entry, long postTime) {
+ when(entry.notification.getPostTime()).thenReturn(postTime);
+ }
+
+ private void setOngoing(NotificationEntry entry, boolean ongoing) {
+ if (ongoing) {
+ entry.notification.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ } else {
+ entry.notification.getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
+ }
+ }
+
+ /**
+ * No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
+ * required for BubbleData functionality and verification. NotificationTestHelper is used only
+ * as a convenience to create a Notification w/BubbleMetadata.
+ */
+ private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName,
+ long postTime) {
+ // BubbleMetadata
+ Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder()
+ .setIntent(mExpandIntent)
+ .setDeleteIntent(mDeleteIntent)
+ .setIcon(Icon.createWithResource("", 0))
+ .build();
+ // Notification -> BubbleMetadata
+ Notification notification = mNotificationTestHelper.createNotification(false,
+ null /* groupKey */, bubbleMetadata);
+
+ // StatusBarNotification
+ StatusBarNotification sbn = mock(StatusBarNotification.class);
+ when(sbn.getKey()).thenReturn(notifKey);
+ when(sbn.getUser()).thenReturn(new UserHandle(userId));
+ when(sbn.getPackageName()).thenReturn(packageName);
+ when(sbn.getPostTime()).thenReturn(postTime);
+ when(sbn.getNotification()).thenReturn(notification);
+
+ // NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata
+ return new NotificationEntry(sbn);
+ }
+
+ private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
+ setPostTime(entry, postTime);
+ mBubbleData.notificationEntryUpdated(entry);
+ }
+
+ private void changeExpandedStateAtTime(boolean shouldBeExpanded, long time) {
+ when(mTimeSource.currentTimeMillis()).thenReturn(time);
+ mBubbleData.setExpanded(shouldBeExpanded);
+ }
+
+ @Test
+ public void testAddBubble() {
+ // Setup
+ mBubbleData.setListener(mListener);
+
+ // Test
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ // Verify
+ verify(mListener).onBubbleAdded(eq(mBubbleA1));
+ verify(mListener).onSelectionChanged(eq(mBubbleA1));
+ verify(mListener).apply();
+ }
+
+ @Test
+ public void testRemoveBubble() {
+ // Setup
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+ mBubbleData.notificationEntryUpdated(mEntryA3);
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+
+ // Verify
+ verify(mListener).onBubbleRemoved(eq(mBubbleA1), eq(BubbleController.DISMISS_USER_GESTURE));
+ verify(mListener).onSelectionChanged(eq(mBubbleA2));
+ verify(mListener).apply();
+ }
+
+ @Test
+ public void test_collapsed_addBubble_atMaxBubbles_expiresLeastActive() {
+ // Given
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryA3, 3000);
+ sendUpdatedEntryAtTime(mEntryB1, 4000);
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ // When
+ sendUpdatedEntryAtTime(mEntryC1, 6000);
+
+ // Then
+ // A2 is removed. A1 is oldest but is the selected bubble.
+ assertThat(mBubbleData.getBubbles()).doesNotContain(mBubbleA2);
+ }
+
+ @Test
+ public void test_collapsed_expand_whenEmpty_doesNothing() {
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ changeExpandedStateAtTime(true, 2000L);
+
+ verify(mListener, never()).onExpandedChanged(anyBoolean());
+ verify(mListener, never()).apply();
+ }
+
+ // New bubble while stack is collapsed
+ @Test
+ public void test_collapsed_addBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ // When
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ // Then
+ // New bubbles move to front when collapsed, bringing bubbles from the same app along
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+ }
+
+ // New bubble while collapsed with ongoing bubble present
+ @Test
+ public void test_collapsed_addBubble_withOngoing() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ // When
+ setOngoing(mEntryA1, true);
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ // Then
+ // New bubbles move to front, but stay behind any ongoing bubbles.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB2, mBubbleB1));
+ }
+
+ // Remove the selected bubble (middle bubble), while the stack is collapsed.
+ @Test
+ public void test_collapsed_removeBubble_selected() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ mBubbleData.setSelectedBubble(mBubbleB2);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
+
+ // Then
+ // (Selection remains in the same position)
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
+ }
+
+ // Remove the selected bubble (last bubble), while the stack is collapsed.
+ @Test
+ public void test_collapsed_removeSelectedBubble_inLastPosition() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ mBubbleData.setSelectedBubble(mBubbleB1);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
+
+ // Then
+ // (Selection is forced to move to previous)
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB2);
+ }
+
+ @Test
+ public void test_collapsed_addBubble_ongoing() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ // When
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ setOngoing(mEntryB2, true);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ // Then
+ // New bubbles move to front, but stay behind any ongoing bubbles.
+ // Does not break grouping. (A2 is inserted after B1, even though it's newer).
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+ }
+
+ @Test
+ public void test_collapsed_removeBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ // When
+ mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
+
+ // Then
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB1));
+ }
+
+ @Test
+ public void test_collapsed_updateBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+
+ // Then
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+ }
+
+ @Test
+ public void test_collapsed_updateBubble_withOngoing() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+
+ setOngoing(mEntryA2, true);
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ setPostTime(mEntryB1, 5000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ // Then
+ // A2 remains in first position, due to being ongoing. B1 moves before B2, Group A
+ // remains before group B.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB1, mBubbleB2));
+ }
+
+ @Test
+ public void test_collapse_afterUpdateWhileExpanded() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ changeExpandedStateAtTime(true, 5000L);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ sendUpdatedEntryAtTime(mEntryB1, 6000);
+
+ // (No reordering while expanded)
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ changeExpandedStateAtTime(false, 7000L);
+
+ // Then
+ // A1 moves to front on collapse, since it is the selected bubble (and most recently
+ // accessed).
+ // A2 moves next to A1 to maintain grouping.
+ // B1 moves in front of B2, since it received an update while expanded
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB1, mBubbleB2));
+ }
+
+ @Test
+ public void test_collapse_afterUpdateWhileExpanded_withOngoing() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+
+ setOngoing(mEntryB2, true);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ changeExpandedStateAtTime(true, 5000L);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+
+ sendUpdatedEntryAtTime(mEntryA1, 6000);
+
+ // No reordering if expanded
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+
+ // When
+ changeExpandedStateAtTime(false, 7000L);
+
+ // Then
+ // B2 remains in first position because it is ongoing.
+ // B1 remains grouped with B2
+ // A1 moves in front of A2, since it is more recently updated (and is selected).
+ // B1 moves in front of B2, since it has more recent activity.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA1, mBubbleA2));
+ }
+
+ @Test
+ public void test_collapsed_removeLastBubble_clearsSelectedBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
+
+ assertThat(mBubbleData.getSelectedBubble()).isNull();
+ }
+
+ @Test
+ public void test_expanded_addBubble_atMaxBubbles_expiresLeastActive() {
+ // Given
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ changeExpandedStateAtTime(true, 2000L);
+ assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(2000);
+
+ sendUpdatedEntryAtTime(mEntryA2, 3000);
+ sendUpdatedEntryAtTime(mEntryA3, 4000);
+ sendUpdatedEntryAtTime(mEntryB1, 5000);
+ sendUpdatedEntryAtTime(mEntryB2, 6000);
+ sendUpdatedEntryAtTime(mEntryB3, 7000);
+
+
+ // Then
+ // A1 would be removed, but it is selected and expanded, so it should not go away.
+ // Instead, fall through to removing A2 (the next oldest).
+ assertThat(mBubbleData.getBubbles()).doesNotContain(mEntryA2);
+ }
+
+ @Test
+ public void test_expanded_removeLastBubble_collapsesStack() {
+ // Given
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryC1);
+
+ mBubbleData.setExpanded(true);
+
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryC1, BubbleController.DISMISS_USER_GESTURE);
+
+ assertThat(mBubbleData.isExpanded()).isFalse();
+ assertThat(mBubbleData.getSelectedBubble()).isNull();
+ }
+
+ // Bubbles do not reorder while expanded
+ @Test
+ public void test_expanded_selection_collapseToTop() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryB1, 3000);
+
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB1, mBubbleA2, mBubbleA1));
+
+ changeExpandedStateAtTime(true, 4000L);
+
+ // regrouping only happens when collapsed (after new or update) or expanded->collapsed
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB1, mBubbleA2, mBubbleA1));
+
+ changeExpandedStateAtTime(false, 6000L);
+
+ // A1 is still selected and it's lastAccessed time has been updated
+ // on collapse, sorting is applied, keeping the selected bubble at the front
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB1));
+ }
+
+ // New bubble from new app while stack is expanded
+ @Test
+ public void test_expanded_addBubble_newApp() {
+ // Given
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryA3, 3000);
+ sendUpdatedEntryAtTime(mEntryB1, 4000);
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ changeExpandedStateAtTime(true, 6000L);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+ assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(6000L);
+
+ // regrouping only happens when collapsed (after new or update) or expanded->collapsed
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2, mBubbleA1));
+
+ // When
+ sendUpdatedEntryAtTime(mEntryC1, 7000);
+
+ // Then
+ // A2 is expired. A1 was oldest, but lastActivityTime is reset when expanded, since A1 is
+ // selected.
+ // C1 is added at the end since bubbles are expanded.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA1, mBubbleC1));
+ }
+
+ // New bubble from existing app while stack is expanded
+ @Test
+ public void test_expanded_addBubble_existingApp() {
+ // Given
+ sendUpdatedEntryAtTime(mEntryB1, 1000);
+ sendUpdatedEntryAtTime(mEntryB2, 2000);
+ sendUpdatedEntryAtTime(mEntryA1, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+ sendUpdatedEntryAtTime(mEntryA3, 5000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
+
+ changeExpandedStateAtTime(true, 6000L);
+
+ // B1 is first (newest, since it's just been expanded and is selected)
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
+ assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(6000L);
+
+ // regrouping only happens when collapsed (after new or update) or while collapsing
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA3, mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ sendUpdatedEntryAtTime(mEntryB3, 7000);
+
+ // Then
+ // (B2 is expired, B1 was oldest, but it's lastActivityTime is updated at the point when
+ // the stack was expanded, since it is the selected bubble.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA3, mBubbleA2, mBubbleA1, mBubbleB3, mBubbleB1));
+ }
+
+ // Updated bubble from existing app while stack is expanded
+ @Test
+ public void test_expanded_updateBubble_existingApp() {
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryB1, 3000);
+ sendUpdatedEntryAtTime(mEntryB2, 4000);
+
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+ mBubbleData.setExpanded(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 5000);
+
+ // Does not reorder while expanded (for an update).
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+ }
+
+ @Test
+ public void test_expanded_updateBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ mBubbleData.setExpanded(true);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ setPostTime(mEntryB1, 5000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ // Then
+ // B1 remains in the same place due to being expanded
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index e4b90c54d5b3..028fd7afd945 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -234,7 +234,7 @@ public class NotificationTestHelper {
* @param bubbleMetadata the bubble metadata to use for this notification if it exists.
* @return a notification that is in the group specified or standalone if unspecified
*/
- private Notification createNotification(boolean isGroupSummary,
+ public Notification createNotification(boolean isGroupSummary,
@Nullable String groupKey, @Nullable BubbleMetadata bubbleMetadata) {
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index d09cea59b9c5..1c6e3b09df44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.View;
import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
@@ -39,7 +38,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
import org.junit.Test;
@@ -198,29 +196,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
verify(mBouncer, never()).setExpansion(anyFloat());
}
- @Test
- public void onQsExpansionChanged_lockVisibleOnlyWhenCollapsed() {
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- mStatusBarKeyguardViewManager.onQsExpansionChanged(0);
- verify(mLockIconContainer).setVisibility(eq(View.VISIBLE));
-
- reset(mNotificationPanelView);
- when(mNotificationPanelView.isQsExpanded()).thenReturn(true);
- mStatusBarKeyguardViewManager.onQsExpansionChanged(1f);
- verify(mLockIconContainer).setVisibility(eq(View.INVISIBLE));
- }
-
- @Test
- public void onQsExpansionChanged_lockInvisibleWhenAnimatingAway() {
- when(mBouncer.isShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.onQsExpansionChanged(0);
- verify(mLockIconContainer).setVisibility(eq(View.VISIBLE));
-
- when(mBouncer.isAnimatingAway()).thenReturn(true);
- mStatusBarKeyguardViewManager.onQsExpansionChanged(0f);
- verify(mLockIconContainer).setVisibility(eq(View.INVISIBLE));
- }
-
private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
public TestableStatusBarKeyguardViewManager(Context context,
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
index b81ae5bbe3fa..a689defe5b6b 100644
--- a/packages/overlays/AccentColorBlackOverlay/Android.mk
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorBlack
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorCinnamonOverlay/Android.mk b/packages/overlays/AccentColorCinnamonOverlay/Android.mk
index d53c1143cd33..3a6cbe3a182a 100644
--- a/packages/overlays/AccentColorCinnamonOverlay/Android.mk
+++ b/packages/overlays/AccentColorCinnamonOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorCinnamon
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
index db92157c8fdf..d96dbe17b103 100644
--- a/packages/overlays/AccentColorGreenOverlay/Android.mk
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorGreen
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorOceanOverlay/Android.mk b/packages/overlays/AccentColorOceanOverlay/Android.mk
index a28fc7227a8c..cf0c6b310f02 100644
--- a/packages/overlays/AccentColorOceanOverlay/Android.mk
+++ b/packages/overlays/AccentColorOceanOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorOcean
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorOrchidOverlay/Android.mk b/packages/overlays/AccentColorOrchidOverlay/Android.mk
index c635890d02cc..fc55befef71e 100644
--- a/packages/overlays/AccentColorOrchidOverlay/Android.mk
+++ b/packages/overlays/AccentColorOrchidOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorOrchid
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
index d7dc4978e2ca..3a28efa2f820 100644
--- a/packages/overlays/AccentColorPurpleOverlay/Android.mk
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorPurple
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorSpaceOverlay/Android.mk b/packages/overlays/AccentColorSpaceOverlay/Android.mk
index a0edb96ac23e..78cbf7325dee 100644
--- a/packages/overlays/AccentColorSpaceOverlay/Android.mk
+++ b/packages/overlays/AccentColorSpaceOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorSpace
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
index bf2b6312d68f..b73aea320d60 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationCorner
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
index 70429064ec2b..8ca2dad25f3f 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationDouble
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index ae69e1137e60..7458cb5db9fb 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index 7dcadfbd4708..1a405e2275c6 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationTall
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index 3f7be73aa5fa..3ebc540852c8 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationWide
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/FontNotoSerifSourceOverlay/Android.mk b/packages/overlays/FontNotoSerifSourceOverlay/Android.mk
index 6f3c4f7cfebd..f4eedaf377f9 100644
--- a/packages/overlays/FontNotoSerifSourceOverlay/Android.mk
+++ b/packages/overlays/FontNotoSerifSourceOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := FontNotoSerifSource
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/Android.mk b/packages/overlays/IconPackCircularAndroidOverlay/Android.mk
index 60f525b65128..8f3baa5962dc 100644
--- a/packages/overlays/IconPackCircularAndroidOverlay/Android.mk
+++ b/packages/overlays/IconPackCircularAndroidOverlay/Android.mk
@@ -17,7 +17,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackCircularAndroid
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackCircularLauncherOverlay/Android.mk b/packages/overlays/IconPackCircularLauncherOverlay/Android.mk
index a5277fa17be0..310bdef44b48 100644
--- a/packages/overlays/IconPackCircularLauncherOverlay/Android.mk
+++ b/packages/overlays/IconPackCircularLauncherOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackCircularLauncher
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/Android.mk b/packages/overlays/IconPackCircularSettingsOverlay/Android.mk
index ad7324dec699..d06732228b82 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/Android.mk
+++ b/packages/overlays/IconPackCircularSettingsOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackCircularSettings
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk b/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk
index 711063dbf6ad..5e0dcbee8118 100644
--- a/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk
+++ b/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackCircularSystemUI
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/Android.mk b/packages/overlays/IconPackFilledAndroidOverlay/Android.mk
index e0db3a27cdbe..3036f7df9f1e 100644
--- a/packages/overlays/IconPackFilledAndroidOverlay/Android.mk
+++ b/packages/overlays/IconPackFilledAndroidOverlay/Android.mk
@@ -17,7 +17,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackFilledAndroid
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackFilledLauncherOverlay/Android.mk b/packages/overlays/IconPackFilledLauncherOverlay/Android.mk
index d2e5b605b6b0..2460fa4675ff 100644
--- a/packages/overlays/IconPackFilledLauncherOverlay/Android.mk
+++ b/packages/overlays/IconPackFilledLauncherOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackFilledLauncher
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/Android.mk b/packages/overlays/IconPackFilledSettingsOverlay/Android.mk
index 0443560a573e..3cc071d732e1 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/Android.mk
+++ b/packages/overlays/IconPackFilledSettingsOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackFilledSettings
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk b/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk
index 2506132e3058..f0276927b8e2 100644
--- a/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk
+++ b/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackFilledSystemUI
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk b/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk
index 2937fb862618..c6ad4ac04092 100644
--- a/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk
@@ -17,7 +17,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackRoundedAndroid
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk b/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk
index 7adfe3b88eb6..713e2819bb65 100644
--- a/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk
+++ b/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackRoundedLauncher
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk b/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk
index 44ac6dd939dd..6c775190f548 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackRoundedSettings
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk
index 2d34a54fb4de..4e21b41828c8 100644
--- a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk
+++ b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackRoundedSystemUI
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
index 08428d192fae..21cd011ef83c 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeRoundedRect
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
index ceb745ae1429..c8728838303c 100644
--- a/packages/overlays/IconShapeSquareOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeSquare
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
index 34edc3b78b09..fa5fe6906dce 100644
--- a/packages/overlays/IconShapeSquircleOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeSquircle
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
index 834a1c357c61..d5f01f39965b 100644
--- a/packages/overlays/IconShapeTeardropOverlay/Android.mk
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeTeardrop
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk
index 410d6d87c61d..be86ef2d3ff4 100644
--- a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk
+++ b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := NavigationBarMode2Button
-LOCAL_CERTIFICATE := platform
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk
index 2bc9a6aea9eb..f44a362b266d 100644
--- a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk
+++ b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := NavigationBarMode3Button
-LOCAL_CERTIFICATE := platform
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk b/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk
index 5f7e0eb62a46..02e2074accb7 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := NavigationBarModeGestural
-LOCAL_CERTIFICATE := platform
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9e73684d5f93..9eda9db1910f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -33,7 +33,6 @@ import static com.android.server.autofill.Helper.getNumericValue;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
-import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -564,7 +563,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Reads a new structure and then request a new fill response from the fill service.
*/
@GuardedBy("mLock")
- private void requestNewFillResponseLocked(int flags) {
+ private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
+ int flags) {
if (mForAugmentedAutofillOnly || (flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) {
// TODO(b/122858578): log metrics
if (sVerbose) {
@@ -575,6 +575,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
triggerAugmentedAutofillLocked();
return;
}
+ viewState.setState(newState);
int requestId;
@@ -2165,19 +2166,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@NonNull ViewState viewState, int flags) {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
- viewState.setState(STATE_RESTARTED_SESSION);
- requestNewFillResponseLocked(flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags);
return;
}
// If it's not, then check if it it should start a partition.
if (shouldStartNewPartitionLocked(id)) {
if (sDebug) {
- Slog.d(TAG, "Starting partition for view id " + id + ": "
+ Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
+ viewState.getStateAsString());
}
- viewState.setState(ViewState.STATE_STARTED_PARTITION);
- requestNewFillResponseLocked(flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags);
} else {
if (sVerbose) {
Slog.v(TAG, "Not starting new partition for view " + id + ": "
@@ -2283,8 +2282,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// View is triggering autofill.
mCurrentViewId = viewState.id;
viewState.update(value, virtualBounds, flags);
- viewState.setState(ViewState.STATE_STARTED_SESSION);
- requestNewFillResponseLocked(flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
break;
case ACTION_VALUE_CHANGED:
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
@@ -2386,6 +2384,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
}
+ // Update the view states first...
+ mCurrentViewId = viewState.id;
+ viewState.setCurrentValue(value);
+
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")");
return;
@@ -2397,10 +2399,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) Slog.d(TAG, "updateLocked(" + id + "): augmented-autofillable");
- // Update the view states first...
- mCurrentViewId = viewState.id;
- viewState.setCurrentValue(value);
-
// ...then trigger the augmented autofill UI
triggerAugmentedAutofillLocked();
return;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 1fa62cec6e30..1220e82485e7 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -760,7 +760,7 @@ class AlarmManagerService extends SystemService {
if (predicate.test(alarm)) {
alarms.remove(i);
if (!reOrdering) {
- decrementAlarmCount(alarm.uid);
+ decrementAlarmCount(alarm.uid, 1);
}
didRemove = true;
if (alarm.alarmClock != null) {
@@ -1764,7 +1764,7 @@ class AlarmManagerService extends SystemService {
+ ", callingPackage: " + callingPackage;
// STOPSHIP (b/128866264): Just to catch breakages. Remove before final release.
Slog.wtf(TAG, errorMsg);
- // TODO b/129995049: Resume throwing once issue is resolved.
+ // TODO b/129995049: Resume throwing after some soak time without errors
// throw new UnsupportedOperationException(errorMsg);
}
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
@@ -3113,17 +3113,21 @@ class AlarmManagerService extends SystemService {
}
}
for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
- if (mPendingWhileIdleAlarms.get(i).matches(operation, directReceiver)) {
+ final Alarm alarm = mPendingWhileIdleAlarms.get(i);
+ if (alarm.matches(operation, directReceiver)) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
mPendingWhileIdleAlarms.remove(i);
+ decrementAlarmCount(alarm.uid, 1);
}
}
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
- if (alarmsForUid.get(j).matches(operation, directReceiver)) {
+ final Alarm alarm = alarmsForUid.get(j);
+ if (alarm.matches(operation, directReceiver)) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
alarmsForUid.remove(j);
+ decrementAlarmCount(alarm.uid, 1);
}
}
if (alarmsForUid.size() == 0) {
@@ -3169,6 +3173,7 @@ class AlarmManagerService extends SystemService {
if (a.uid == uid) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
mPendingWhileIdleAlarms.remove(i);
+ decrementAlarmCount(uid, 1);
}
}
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) {
@@ -3176,6 +3181,7 @@ class AlarmManagerService extends SystemService {
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
if (alarmsForUid.get(j).uid == uid) {
alarmsForUid.remove(j);
+ decrementAlarmCount(uid, 1);
}
}
if (alarmsForUid.size() == 0) {
@@ -3221,13 +3227,16 @@ class AlarmManagerService extends SystemService {
if (a.matches(packageName)) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
mPendingWhileIdleAlarms.remove(i);
+ decrementAlarmCount(a.uid, 1);
}
}
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) {
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
- if (alarmsForUid.get(j).matches(packageName)) {
+ final Alarm alarm = alarmsForUid.get(j);
+ if (alarm.matches(packageName)) {
alarmsForUid.remove(j);
+ decrementAlarmCount(alarm.uid, 1);
}
}
if (alarmsForUid.size() == 0) {
@@ -3272,10 +3281,15 @@ class AlarmManagerService extends SystemService {
if (a.uid == uid) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
mPendingWhileIdleAlarms.remove(i);
+ decrementAlarmCount(uid, 1);
}
}
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
if (mPendingBackgroundAlarms.keyAt(i) == uid) {
+ final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i);
+ if (toRemove != null) {
+ decrementAlarmCount(uid, toRemove.size());
+ }
mPendingBackgroundAlarms.removeAt(i);
}
}
@@ -3308,11 +3322,18 @@ class AlarmManagerService extends SystemService {
if (UserHandle.getUserId(mPendingWhileIdleAlarms.get(i).creatorUid)
== userHandle) {
// Don't set didRemove, since this doesn't impact the scheduled alarms.
- mPendingWhileIdleAlarms.remove(i);
+ final Alarm removed = mPendingWhileIdleAlarms.remove(i);
+ decrementAlarmCount(removed.uid, 1);
}
}
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
if (UserHandle.getUserId(mPendingBackgroundAlarms.keyAt(i)) == userHandle) {
+ final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i);
+ if (toRemove != null) {
+ for (int j = 0; j < toRemove.size(); j++) {
+ decrementAlarmCount(toRemove.get(j).uid, 1);
+ }
+ }
mPendingBackgroundAlarms.removeAt(i);
}
}
@@ -3844,7 +3865,7 @@ class AlarmManagerService extends SystemService {
Slog.w(TAG, "Failure sending alarm.", e);
}
Trace.traceEnd(Trace.TRACE_TAG_POWER);
- decrementAlarmCount(alarm.uid);
+ decrementAlarmCount(alarm.uid, 1);
}
}
@@ -4198,7 +4219,7 @@ class AlarmManagerService extends SystemService {
removeImpl(alarm.operation, null);
}
}
- decrementAlarmCount(alarm.uid);
+ decrementAlarmCount(alarm.uid, 1);
}
break;
}
@@ -4828,16 +4849,21 @@ class AlarmManagerService extends SystemService {
}
}
- private void decrementAlarmCount(int uid) {
+ private void decrementAlarmCount(int uid, int decrement) {
+ int oldCount = 0;
final int uidIndex = mAlarmsPerUid.indexOfKey(uid);
if (uidIndex >= 0) {
- final int newCount = mAlarmsPerUid.valueAt(uidIndex) - 1;
- if (newCount > 0) {
- mAlarmsPerUid.setValueAt(uidIndex, newCount);
+ oldCount = mAlarmsPerUid.valueAt(uidIndex);
+ if (oldCount > decrement) {
+ mAlarmsPerUid.setValueAt(uidIndex, oldCount - decrement);
} else {
mAlarmsPerUid.removeAt(uidIndex);
}
}
+ if (oldCount < decrement) {
+ Slog.wtf(TAG, "Attempt to decrement existing alarm count " + oldCount + " by "
+ + decrement + " for uid " + uid);
+ }
}
private class ShellCmd extends ShellCommand {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index da91187c053f..28bc34859e6c 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -66,6 +66,7 @@ import com.android.internal.telephony.PhoneConstantConversions;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.am.BatteryStatsService;
@@ -1164,36 +1165,33 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
@Override
public void notifyCarrierNetworkChange(boolean active) {
- // only CarrierService with carrier privilege rule should have the permission.
- int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- try {
- subId = Arrays.stream(SubscriptionManager.from(mContext)
+ // only CarrierService with carrier privilege rule should have the permission
+ int[] subIds = Arrays.stream(SubscriptionManager.from(mContext)
.getActiveSubscriptionIdList())
- .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i))
- .findFirst().getAsInt();
- } catch (NoSuchElementException ex) {
+ .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i)).toArray();
+ if (ArrayUtils.isEmpty(subIds)) {
loge("notifyCarrierNetworkChange without carrier privilege");
- }
- // the active subId does not have carrier privilege.
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ // the active subId does not have carrier privilege.
throw new SecurityException("notifyCarrierNetworkChange without carrier privilege");
}
- int phoneId = SubscriptionManager.getPhoneId(subId);
-
- if (VDBG) {
- log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
- }
synchronized (mRecords) {
mCarrierNetworkChangeState = active;
- for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) &&
- idMatch(r.subId, subId, phoneId)) {
- try {
- r.callback.onCarrierNetworkChange(active);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
+ for (int subId : subIds) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+
+ if (VDBG) {
+ log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
+ }
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) &&
+ idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 19c3a71810fb..64f4a35032c0 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -81,6 +81,7 @@ public class SettingsToPropertiesMapper {
static final String[] sDeviceConfigScopes = new String[] {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
DeviceConfig.NAMESPACE_NETD_NATIVE,
DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c6f6c50a308d..19ff2c11d14c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1164,6 +1164,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// Carrier might want to manage notifications themselves
final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+ if (!CarrierConfigManager.isConfigForIdentifiedCarrier(config)) {
+ if (LOGV) Slog.v(TAG, "isConfigForIdentifiedCarrier returned false");
+ // Don't show notifications until we confirm that the loaded config is from an
+ // identified carrier, which may want to manage their own notifications. This method
+ // should be called every time the carrier config changes anyways, and there's no
+ // reason to alert if there isn't a carrier.
+ return;
+ }
+
final boolean notifyWarning = getBooleanDefeatingNullable(config,
KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
final boolean notifyLimit = getBooleanDefeatingNullable(config,
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 21b6f12b6f8b..497385fef39c 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -22,21 +22,21 @@ import android.apex.ApexInfo;
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
import android.apex.IApexService;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
+import android.os.HandlerThread;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemClock;
+import android.sysprop.ApexProperties;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.SystemService;
import java.io.File;
import java.io.PrintWriter;
@@ -45,75 +45,108 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* ApexManager class handles communications with the apex service to perform operation and queries,
* as well as providing caching to avoid unnecessary calls to the service.
+ *
+ * @hide
*/
-class ApexManager {
- static final String TAG = "ApexManager";
- private final IApexService mApexService;
- private final Context mContext;
- private final Object mLock = new Object();
- @GuardedBy("mLock")
+public final class ApexManager extends SystemService {
+ private static final String TAG = "ApexManager";
+ private IApexService mApexService;
+
+ private final CountDownLatch mActivePackagesCacheLatch = new CountDownLatch(1);
private Map<String, PackageInfo> mActivePackagesCache;
- ApexManager(Context context) {
+ private final CountDownLatch mApexFilesCacheLatch = new CountDownLatch(1);
+ private ApexInfo[] mApexFiles;
+
+ public ApexManager(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
try {
mApexService = IApexService.Stub.asInterface(
- ServiceManager.getServiceOrThrow("apexservice"));
+ ServiceManager.getServiceOrThrow("apexservice"));
} catch (ServiceNotFoundException e) {
throw new IllegalStateException("Required service apexservice not available");
}
- mContext = context;
+ publishLocalService(ApexManager.class, this);
+ HandlerThread oneShotThread = new HandlerThread("ApexManagerOneShotHandler");
+ oneShotThread.start();
+ oneShotThread.getThreadHandler().post(this::initSequence);
+ oneShotThread.quitSafely();
}
- void systemReady() {
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onBootCompleted();
- mContext.unregisterReceiver(this);
- }
- }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+ private void initSequence() {
+ populateApexFilesCache();
+ parseApexFiles();
}
- private void populateActivePackagesCacheIfNeeded() {
- synchronized (mLock) {
- if (mActivePackagesCache != null) {
- return;
- }
+ private void populateApexFilesCache() {
+ if (mApexFiles != null) {
+ return;
+ }
+ long startTimeMicros = SystemClock.currentTimeMicro();
+ Slog.i(TAG, "Starting to populate apex files cache");
+ try {
+ mApexFiles = mApexService.getActivePackages();
+ Slog.i(TAG, "IPC to apexd finished in " + (SystemClock.currentTimeMicro()
+ - startTimeMicros) + " μs");
+ } catch (RemoteException re) {
+ // TODO: make sure this error is propagated to system server.
+ Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
+ re.rethrowAsRuntimeException();
+ }
+ mApexFilesCacheLatch.countDown();
+ Slog.i(TAG, "Finished populating apex files cache in " + (SystemClock.currentTimeMicro()
+ - startTimeMicros) + " μs");
+ }
+
+ private void parseApexFiles() {
+ waitForLatch(mApexFilesCacheLatch);
+ if (mApexFiles == null) {
+ throw new IllegalStateException("mApexFiles must be populated");
+ }
+ long startTimeMicros = SystemClock.currentTimeMicro();
+ Slog.i(TAG, "Starting to parse apex files");
+ List<PackageInfo> list = new ArrayList<>();
+ // TODO: this can be parallelized.
+ for (ApexInfo ai : mApexFiles) {
try {
- List<PackageInfo> list = new ArrayList<>();
- final ApexInfo[] activePkgs = mApexService.getActivePackages();
- for (ApexInfo ai : activePkgs) {
- // If the device is using flattened APEX, don't report any APEX
- // packages since they won't be managed or updated by PackageManager.
- if ((new File(ai.packagePath)).isDirectory()) {
- break;
- }
- try {
- list.add(PackageParser.generatePackageInfoFromApex(
- new File(ai.packagePath), PackageManager.GET_META_DATA
- | PackageManager.GET_SIGNING_CERTIFICATES));
- } catch (PackageParserException pe) {
- throw new IllegalStateException("Unable to parse: " + ai, pe);
- }
+ // If the device is using flattened APEX, don't report any APEX
+ // packages since they won't be managed or updated by PackageManager.
+ if ((new File(ai.packagePath)).isDirectory()) {
+ break;
}
- mActivePackagesCache = list.stream().collect(
- Collectors.toMap(p -> p.packageName, Function.identity()));
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
- throw new RuntimeException(re);
+ list.add(PackageParser.generatePackageInfoFromApex(
+ new File(ai.packagePath), PackageManager.GET_META_DATA
+ | PackageManager.GET_SIGNING_CERTIFICATES));
+ } catch (PackageParserException pe) {
+ // TODO: make sure this error is propagated to system server.
+ throw new IllegalStateException("Unable to parse: " + ai, pe);
}
}
+ mActivePackagesCache = list.stream().collect(
+ Collectors.toMap(p -> p.packageName, Function.identity()));
+ mActivePackagesCacheLatch.countDown();
+ Slog.i(TAG, "Finished parsing apex files in " + (SystemClock.currentTimeMicro()
+ - startTimeMicros) + " μs");
}
/**
* Retrieves information about an active APEX package.
*
+ * <p>This method blocks caller thread until {@link #parseApexFiles()} succeeds. Note that in
+ * case {@link #parseApexFiles()}} throws an exception this method will never finish
+ * essentially putting device into a boot loop.
+ *
* @param packageName the package name to look for. Note that this is the package name reported
* in the APK container manifest (i.e. AndroidManifest.xml), which might
* differ from the one reported in the APEX manifest (i.e.
@@ -122,30 +155,43 @@ class ApexManager {
* is not found.
*/
@Nullable PackageInfo getActivePackage(String packageName) {
- populateActivePackagesCacheIfNeeded();
+ waitForLatch(mActivePackagesCacheLatch);
return mActivePackagesCache.get(packageName);
}
/**
* Retrieves information about all active APEX packages.
*
+ * <p>This method blocks caller thread until {@link #parseApexFiles()} succeeds. Note that in
+ * case {@link #parseApexFiles()}} throws an exception this method will never finish
+ * essentially putting device into a boot loop.
+ *
* @return a Collection of PackageInfo object, each one containing information about a different
* active package.
*/
Collection<PackageInfo> getActivePackages() {
- populateActivePackagesCacheIfNeeded();
+ waitForLatch(mActivePackagesCacheLatch);
return mActivePackagesCache.values();
}
/**
* Checks if {@code packageName} is an apex package.
*
+ * <p>This method blocks caller thread until {@link #populateApexFilesCache()} succeeds. Note
+ * that in case {@link #populateApexFilesCache()} throws an exception this method will never
+ * finish essentially putting device into a boot loop.
+ *
* @param packageName package to check.
* @return {@code true} if {@code packageName} is an apex package.
*/
boolean isApexPackage(String packageName) {
- populateActivePackagesCacheIfNeeded();
- return mActivePackagesCache.containsKey(packageName);
+ waitForLatch(mApexFilesCacheLatch);
+ for (ApexInfo ai : mApexFiles) {
+ if (ai.packageName.equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -237,11 +283,7 @@ class ApexManager {
* @return true if APEX packages can be managed on this device, false otherwise.
*/
boolean isApexSupported() {
- populateActivePackagesCacheIfNeeded();
- // There is no system-wide property available to check if APEX are flattened and hence can't
- // be updated. In absence of such property, we assume that if we didn't index APEX packages
- // since they were flattened, no APEX management should be possible.
- return !mActivePackagesCache.isEmpty();
+ return ApexProperties.updatable().orElse(false);
}
/**
@@ -277,6 +319,19 @@ class ApexManager {
}
/**
+ * Blocks current thread until {@code latch} has counted down to zero.
+ *
+ * @throws RuntimeException if thread was interrupted while waiting.
+ */
+ private void waitForLatch(CountDownLatch latch) {
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted waiting for cache to be populated", e);
+ }
+ }
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -289,7 +344,7 @@ class ApexManager {
ipw.println("Active APEX packages:");
ipw.increaseIndent();
try {
- populateActivePackagesCacheIfNeeded();
+ waitForLatch(mActivePackagesCacheLatch);
for (PackageInfo pi : mActivePackagesCache.values()) {
if (packageName != null && !packageName.equals(pi.packageName)) {
continue;
@@ -334,8 +389,4 @@ class ApexManager {
ipw.println("Couldn't communicate with apexd.");
}
}
-
- public void onBootCompleted() {
- populateActivePackagesCacheIfNeeded();
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e08af6f3521f..e62402e065cd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2374,6 +2374,8 @@ public class PackageManagerService extends IPackageManager.Stub
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
+ mApexManager = LocalServices.getService(ApexManager.class);
+
LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
@@ -2470,7 +2472,6 @@ public class PackageManagerService extends IPackageManager.Stub
mProtectedPackages = new ProtectedPackages(mContext);
- mApexManager = new ApexManager(context);
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
@@ -21462,7 +21463,6 @@ public class PackageManagerService extends IPackageManager.Stub
storage.registerListener(mStorageListener);
mInstallerService.systemReady();
- mApexManager.systemReady();
mPackageDexOptimizer.systemReady();
getStorageManagerInternal().addExternalStoragePolicy(
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index ead09b424d61..e85989315417 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -185,16 +185,18 @@ public final class PackageSetting extends PackageSettingBase {
proto.write(PackageProto.NAME, (realName != null ? realName : name));
proto.write(PackageProto.UID, appId);
proto.write(PackageProto.VERSION_CODE, versionCode);
- proto.write(PackageProto.VERSION_STRING, pkg.mVersionName);
proto.write(PackageProto.INSTALL_TIME_MS, firstInstallTime);
proto.write(PackageProto.UPDATE_TIME_MS, lastUpdateTime);
proto.write(PackageProto.INSTALLER_NAME, installerPackageName);
if (pkg != null) {
+ proto.write(PackageProto.VERSION_STRING, pkg.mVersionName);
+
long splitToken = proto.start(PackageProto.SPLITS);
proto.write(PackageProto.SplitProto.NAME, "base");
proto.write(PackageProto.SplitProto.REVISION_CODE, pkg.baseRevisionCode);
proto.end(splitToken);
+
if (pkg.splitNames != null) {
for (int i = 0; i < pkg.splitNames.length; i++) {
splitToken = proto.start(PackageProto.SPLITS);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a55ee5ff7f1f..7bc9600f50b4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5810,10 +5810,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
Intent getSecondaryHomeIntent(String preferredPackage) {
final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
- final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
- if (preferredPackage == null || useSystemProvidedLauncher) {
- // Using the component stored in config if no package name or forced.
+ if (preferredPackage == null) {
+ // Using the component stored in config if no package name.
final String secondaryHomeComponent = mContext.getResources().getString(
com.android.internal.R.string.config_secondaryHomeComponent);
intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 7bc6776803a3..4d37f1ace9c0 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1860,13 +1860,14 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject
* Fail if the main interface fails to initialize
*/
if (gnssHal == nullptr) {
- ALOGE("Unable to Initialize GNSS HAL\n");
+ ALOGE("Unable to initialize GNSS HAL.");
return JNI_FALSE;
}
- sp<IGnssCallback> gnssCbIface = new GnssCallback();
-
Return<bool> result = false;
+
+ // Set top level IGnss.hal callback.
+ sp<IGnssCallback> gnssCbIface = new GnssCallback();
if (gnssHal_V2_0 != nullptr) {
result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
} else if (gnssHal_V1_1 != nullptr) {
@@ -1876,62 +1877,89 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject
}
if (!result.isOk() || !result) {
- ALOGE("SetCallback for Gnss Interface fails\n");
+ ALOGE("SetCallback for IGnss interface failed.");
return JNI_FALSE;
}
- sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
+ // Set IGnssXtra.hal callback.
if (gnssXtraIface == nullptr) {
- ALOGI("Unable to initialize GNSS Xtra interface\n");
+ ALOGI("Unable to initialize IGnssXtra interface.");
} else {
+ sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
result = gnssXtraIface->setCallback(gnssXtraCbIface);
if (!result.isOk() || !result) {
gnssXtraIface = nullptr;
- ALOGI("SetCallback for Gnss Xtra Interface fails\n");
+ ALOGI("SetCallback for IGnssXtra interface failed.");
}
}
+ // Set IAGnss.hal callback.
+ Return<void> agnssStatus;
if (agnssIface_V2_0 != nullptr) {
sp<IAGnssCallback_V2_0> aGnssCbIface = new AGnssCallback_V2_0();
- agnssIface_V2_0->setCallback(aGnssCbIface);
+ agnssStatus = agnssIface_V2_0->setCallback(aGnssCbIface);
} else if (agnssIface != nullptr) {
sp<IAGnssCallback_V1_0> aGnssCbIface = new AGnssCallback_V1_0();
- agnssIface->setCallback(aGnssCbIface);
+ agnssStatus = agnssIface->setCallback(aGnssCbIface);
} else {
- ALOGI("Unable to initialize AGnss interface\n");
+ ALOGI("Unable to initialize IAGnss interface.");
}
+ if (!agnssStatus.isOk()) {
+ ALOGI("SetCallback for IAGnss interface failed.");
+ }
+
+ // Set IGnssGeofencing.hal callback.
sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
if (gnssGeofencingIface != nullptr) {
- gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
+ auto status = gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
+ if (!status.isOk()) {
+ ALOGI("SetCallback for IGnssGeofencing interface failed.");
+ }
} else {
- ALOGI("Unable to initialize GNSS Geofencing interface\n");
+ ALOGI("Unable to initialize IGnssGeofencing interface.");
}
+ // Set IGnssNi.hal callback.
sp<IGnssNiCallback> gnssNiCbIface = new GnssNiCallback();
if (gnssNiIface != nullptr) {
- gnssNiIface->setCallback(gnssNiCbIface);
+ auto status = gnssNiIface->setCallback(gnssNiCbIface);
+ if (!status.isOk()) {
+ ALOGI("SetCallback for IGnssNi interface failed.");
+ }
} else {
- ALOGI("Unable to initialize GNSS NI interface\n");
+ ALOGI("Unable to initialize IGnssNi interface.");
}
+ // Set IAGnssRil.hal callback.
sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
if (agnssRilIface != nullptr) {
- agnssRilIface->setCallback(aGnssRilCbIface);
+ auto status = agnssRilIface->setCallback(aGnssRilCbIface);
+ if (!status.isOk()) {
+ ALOGI("SetCallback for IAGnssRil interface failed.");
+ }
} else {
- ALOGI("Unable to initialize AGnss Ril interface\n");
+ ALOGI("Unable to initialize IAGnssRil interface.");
}
+ // Set IGnssVisibilityControl.hal callback.
if (gnssVisibilityControlIface != nullptr) {
sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface =
new GnssVisibilityControlCallback();
- gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
+ result = gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
+ if (!result.isOk() || !result) {
+ ALOGI("SetCallback for IGnssVisibilityControl interface failed.");
+ }
}
+ // Set IMeasurementCorrections.hal callback.
if (gnssCorrectionsIface != nullptr) {
sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
new MeasurementCorrectionsCallback();
- gnssCorrectionsIface->setCallback(gnssCorrectionsIfaceCbIface);
+ result = gnssCorrectionsIface->setCallback(gnssCorrectionsIfaceCbIface);
+ if (!result.isOk() || !result) {
+ ALOGI("SetCallback for IMeasurementCorrections interface failed.");
+ }
}
return JNI_TRUE;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index be7dd31380ba..4ac8342e6e60 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -115,6 +115,7 @@ import com.android.server.om.OverlayManagerService;
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.SchedulingPolicyService;
+import com.android.server.pm.ApexManager;
import com.android.server.pm.BackgroundDexOptService;
import com.android.server.pm.CrossProfileAppsService;
import com.android.server.pm.DynamicCodeLoggingService;
@@ -627,6 +628,12 @@ public final class SystemServer {
watchdog.start();
traceEnd();
+ // Start ApexManager as early as we can to give it enough time to call apexd and populate
+ // cache of known apex packages. Note that calling apexd will happen asynchronously.
+ traceBeginAndSlog("StartApexManager");
+ mSystemServiceManager.startService(ApexManager.class);
+ traceEnd();
+
Slog.i(TAG, "Reading configuration...");
final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
traceBeginAndSlog(TAG_SYSTEM_CONFIG);
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index c0a11b27c65c..6517303842ed 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -263,6 +263,9 @@ public class AlarmManagerServiceTest {
mService.onStart();
spyOn(mService.mHandler);
+ // Stubbing the handler. Test should simulate any handling of messages synchronously.
+ doReturn(true).when(mService.mHandler).sendMessageAtTime(any(Message.class), anyLong());
+
assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID);
assertEquals(mService.mClockReceiver, mClockReceiver);
assertEquals(mService.mWakeLock, mWakeLock);
@@ -617,11 +620,9 @@ public class AlarmManagerServiceTest {
testQuotasNoDeferral(STANDBY_BUCKET_RARE);
}
- private void sendAndHandleBucketChanged(int bucket) {
+ private void assertAndHandleBucketChanged(int bucket) {
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(bucket);
- // Stubbing the handler call to simulate it synchronously here.
- doReturn(true).when(mService.mHandler).sendMessage(any(Message.class));
mAppStandbyListener.onAppIdleStateChanged(TEST_CALLING_PACKAGE,
UserHandle.getUserId(TEST_CALLING_UID), false, bucket, 0);
final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
@@ -652,7 +653,7 @@ public class AlarmManagerServiceTest {
// The next upcoming alarm in queue should also be set as expected.
assertEquals(firstTrigger + workingQuota - 1, mTestTimer.getElapsed());
// Downgrading the bucket now
- sendAndHandleBucketChanged(STANDBY_BUCKET_RARE);
+ assertAndHandleBucketChanged(STANDBY_BUCKET_RARE);
final int rareQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_RARE);
// The last alarm should now be deferred.
final long expectedNextTrigger = (firstTrigger + workingQuota - 1 - rareQuota)
@@ -680,15 +681,13 @@ public class AlarmManagerServiceTest {
assertEquals(deferredTrigger, mTestTimer.getElapsed());
// Upgrading the bucket now
- sendAndHandleBucketChanged(STANDBY_BUCKET_ACTIVE);
+ assertAndHandleBucketChanged(STANDBY_BUCKET_ACTIVE);
// The last alarm should now be rescheduled to go as per original expectations
final long originalTrigger = firstTrigger + frequentQuota;
assertEquals("Incorrect next alarm trigger", originalTrigger, mTestTimer.getElapsed());
}
- private void sendAndHandleParoleChanged(boolean parole) {
- // Stubbing the handler call to simulate it synchronously here.
- doReturn(true).when(mService.mHandler).sendMessage(any(Message.class));
+ private void assertAndHandleParoleChanged(boolean parole) {
mAppStandbyListener.onParoleStateChanged(parole);
final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture());
@@ -719,7 +718,7 @@ public class AlarmManagerServiceTest {
// Any subsequent alarms in queue should all be deferred
assertEquals(firstTrigger + mAppStandbyWindow + 1, mTestTimer.getElapsed());
// Paroling now
- sendAndHandleParoleChanged(true);
+ assertAndHandleParoleChanged(true);
// Subsequent alarms should now go off as per original expectations.
for (int i = 0; i < 5; i++) {
@@ -728,7 +727,7 @@ public class AlarmManagerServiceTest {
mTestTimer.expire();
}
// Come out of parole
- sendAndHandleParoleChanged(false);
+ assertAndHandleParoleChanged(false);
// Subsequent alarms should again get deferred
final long expectedNextTrigger = (firstTrigger + 5) + 1 + mAppStandbyWindow;
@@ -938,6 +937,26 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void alarmCountOnRemoveFromPendingWhileIdle() {
+ mService.mPendingIdleUntil = mock(AlarmManagerService.Alarm.class);
+ final int numAlarms = 15;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ pis[i] = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
+ }
+ assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(numAlarms, mService.mPendingWhileIdleAlarms.size());
+ final int toRemove = 8;
+ for (int i = 0; i < toRemove; i++) {
+ mService.removeLocked(pis[i], null);
+ assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
+ }
+ mService.removeLocked(TEST_CALLING_UID);
+ assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
+ }
+
+ @Test
public void alarmCountOnAlarmRemoved() {
final int numAlarms = 10;
final PendingIntent[] pis = new PendingIntent[numAlarms];
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 99b827c11853..bdc46ec808c1 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -263,6 +263,7 @@ public class NetworkPolicyManagerServiceTest {
private static final int INVALID_CARRIER_CONFIG_VALUE = -9999;
private long mDefaultWarningBytes; // filled in with the actual default before tests are run
private long mDefaultLimitBytes; // filled in with the actual default before tests are run
+ private PersistableBundle mCarrierConfig = CarrierConfigManager.getDefaultConfig();
private static final int APP_ID_A = android.os.Process.FIRST_APPLICATION_UID + 4;
private static final int APP_ID_B = android.os.Process.FIRST_APPLICATION_UID + 8;
@@ -409,6 +410,9 @@ public class NetworkPolicyManagerServiceTest {
doNothing().when(mConnectivityManager)
.registerNetworkCallback(any(), mNetworkCallbackCaptor.capture());
+ // Create the expected carrier config
+ mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+
// Prepare NPMS.
mService.systemReady(mService.networkScoreAndNetworkManagementServiceReady());
@@ -1086,6 +1090,25 @@ public class NetworkPolicyManagerServiceTest {
isA(Notification.class), eq(UserHandle.ALL));
}
+ // Push over warning, but with a config that isn't from an identified carrier
+ {
+ history.clear();
+ history.recordData(start, end,
+ new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+
+ reset(mTelephonyManager, mNetworkManager, mNotifManager);
+ expectMobileDefaults();
+ expectDefaultCarrierConfig();
+
+ mService.updateNetworks();
+
+ verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+ DataUnit.MEGABYTES.toBytes(1800 - 1799));
+ // Since this isn't from the identified carrier, there should be no notifications
+ verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+ }
+
// Push over limit
{
history.clear();
@@ -1812,7 +1835,7 @@ public class NetworkPolicyManagerServiceTest {
private void expectNetworkState(boolean roaming) throws Exception {
when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
- .thenReturn(CarrierConfigManager.getDefaultConfig());
+ .thenReturn(mCarrierConfig);
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
new NetworkState(buildNetworkInfo(),
buildLinkProperties(TEST_IFACE),
@@ -1821,10 +1844,16 @@ public class NetworkPolicyManagerServiceTest {
});
}
+ private void expectDefaultCarrierConfig() throws Exception {
+ when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
+ .thenReturn(CarrierConfigManager.getDefaultConfig());
+ }
+
private void expectMobileDefaults() throws Exception {
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
new int[] { TEST_SUB_ID });
when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
+ doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt());
expectNetworkState(false /* roaming */);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 6d27e6d0852c..bac1ecd5ec31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -31,7 +31,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
@@ -56,7 +55,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
@@ -539,71 +537,6 @@ public class RootActivityContainerTests extends ActivityTestsBase {
}
/**
- * Tests that the default secondary home activity is always picked when it is in forced by
- * config_useSystemProvidedLauncherForSecondary.
- */
- @Test
- public void testResolveSecondaryHomeActivityForced() {
- Resources resources = mContext.getResources();
- spyOn(resources);
- final String defaultSecondaryHome =
- "com.android.test/com.android.test.TestDefaultSecondaryHome";
- final ComponentName secondaryComp = ComponentName.unflattenFromString(defaultSecondaryHome);
- doReturn(defaultSecondaryHome).when(resources).getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- doReturn(true).when(resources).getBoolean(
- com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
-
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- assertEquals(secondaryComp, secondaryHomeIntent.getComponent());
-
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = secondaryComp.getClassName();
- aInfoSecondary.applicationInfo = new ApplicationInfo();
- aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName();
- doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
- refEq(secondaryHomeIntent));
-
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
-
- // Let resolveActivities call to validate both main launcher and second launcher so that
- // resolveActivities call does not work as enabler for secondary.
- final List<ResolveInfo> resolutions1 = new ArrayList<>();
- final ResolveInfo resolveInfo1 = new ResolveInfo();
- resolveInfo1.activityInfo = new ActivityInfo();
- resolveInfo1.activityInfo.name = aInfoDefault.name;
- resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo;
- resolutions1.add(resolveInfo1);
- doReturn(resolutions1).when(mRootActivityContainer).resolveActivities(anyInt(),
- refEq(homeIntent));
- final List<ResolveInfo> resolutions2 = new ArrayList<>();
- final ResolveInfo resolveInfo2 = new ResolveInfo();
- resolveInfo2.activityInfo = new ActivityInfo();
- resolveInfo2.activityInfo.name = aInfoSecondary.name;
- resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo;
- resolutions2.add(resolveInfo2);
- doReturn(resolutions2).when(mRootActivityContainer).resolveActivities(anyInt(),
- refEq(secondaryHomeIntent));
-
- doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
-
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
- .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name);
- assertEquals(secondaryComp.getPackageName(),
- resolvedInfo.first.applicationInfo.packageName);
- assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
- }
-
- /**
* Tests that secondary home should be selected if default home not support secondary displays
* or there is no matched activity in the same package as selected default home.
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 638733430ede..69500d77fd6f 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3313,7 +3313,7 @@ public class CarrierConfigManager {
});
sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_USE_USIM_BOOL, false);
- sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, true);
+ sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false);
sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false);
sDefaults.putString(KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING, "");
sDefaults.putBoolean(KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN,
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ccf49c919db8..f0cad6a35b16 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3321,7 +3321,7 @@ public class TelephonyManager {
}
/**
- * Gets information about currently inserted UICCs and enabled eUICCs.
+ * Gets information about currently inserted UICCs and eUICCs.
* <p>
* Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
* <p>
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 2c98c4d40347..b438920362ff 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -101,7 +101,7 @@ public final class UiccCardInfo implements Parcelable {
/**
* Get the embedded ID (EID) of the eUICC. If the UiccCardInfo is not an eUICC
- * (see {@link #isEuicc()}), returns null.
+ * (see {@link #isEuicc()}), or the EID is not available, returns null.
* <p>
* Note that this field may be omitted if the caller does not have the correct permissions
* (see {@link TelephonyManager#getUiccCardsInfo()}).
@@ -115,7 +115,7 @@ public final class UiccCardInfo implements Parcelable {
}
/**
- * Get the ICCID of the UICC.
+ * Get the ICCID of the UICC. If the ICCID is not availble, returns null.
* <p>
* Note that this field may be omitted if the caller does not have the correct permissions
* (see {@link TelephonyManager#getUiccCardsInfo()}).
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0169c26d393c..f226bb1fa588 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1594,7 +1594,7 @@ interface ITelephony {
int getCardIdForDefaultEuicc(int subId, String callingPackage);
/**
- * Gets information about currently inserted UICCs and enabled eUICCs.
+ * Gets information about currently inserted UICCs and eUICCs.
* <p>
* Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
* <p>