diff options
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> |