Merge "Add missing android.location.flags-aconfig-java to framework-location" into main
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6ff1bfc5..e39928b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3090,6 +3090,25 @@
     }
 
     /**
+     * @hide
+     */
+    public int loadHeaderAppIconRes(Context context) {
+        ApplicationInfo info = null;
+        if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
+            info = extras.getParcelable(
+                    EXTRA_BUILDER_APPLICATION_INFO,
+                    ApplicationInfo.class);
+        }
+        if (info == null) {
+            info = context.getApplicationInfo();
+        }
+        if (info != null) {
+            return info.icon;
+        }
+        return 0;
+    }
+
+    /**
      * Removes heavyweight parts of the Notification object for archival or for sending to
      * listeners when the full contents are not necessary.
      * @hide
@@ -5963,12 +5982,21 @@
         }
 
         private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
-            if (mN.mSmallIcon == null && mN.icon != 0) {
+            if (Flags.notificationsUseAppIcon()) {
+                // Override small icon with app icon
+                mN.mSmallIcon = Icon.createWithResource(mContext,
+                        mN.loadHeaderAppIconRes(mContext));
+            } else if (mN.mSmallIcon == null && mN.icon != 0) {
                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
             }
+
             contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
             contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
-            processSmallIconColor(mN.mSmallIcon, contentView, p);
+
+            // Don't change color if we're using the app icon.
+            if (!Flags.notificationsUseAppIcon()) {
+                processSmallIconColor(mN.mSmallIcon, contentView, p);
+            }
         }
 
         /**
@@ -6804,7 +6832,8 @@
          */
         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
                 StandardTemplateParams p) {
-            boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
+            boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext,
+                    smallIcon);
             int color = getSmallIconColor(p);
             contentView.setInt(R.id.icon, "setBackgroundColor",
                     getBackgroundColor(p));
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 250953e..28afd5f 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -1,5 +1,9 @@
 package: "android.app"
 
+# Note: When adding a new flag here, consider including the word "notification(s)" in the flag name
+# when appropriate, as it's not currently part of the namespace so it may not be obvious what the
+# flag relates to.
+
 flag {
   name: "modes_api"
   is_exported: true
@@ -41,6 +45,13 @@
 }
 
 flag {
+  name: "notifications_use_app_icon"
+  namespace: "systemui"
+  description: "Experiment to replace the small icon in a notification with the app icon."
+  bug: "335211019"
+}
+
+flag {
   name: "keyguard_private_notifications"
   namespace: "systemui"
   description: "Fixes the behavior of KeyguardManager#setPrivateNotificationsAllowed()"
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4c0da7c..f5bff9d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4844,6 +4844,16 @@
     public static final String FEATURE_ROTARY_ENCODER_LOW_RES =
             "android.hardware.rotaryencoder.lowres";
 
+  /**
+   * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+   * support for contextual search helper.
+   *
+   * @hide
+   */
+  @SdkConstant(SdkConstantType.FEATURE)
+  public static final String FEATURE_CONTEXTUAL_SEARCH_HELPER =
+      "android.software.contextualsearch";
+
     /** @hide */
     public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
index 2284436..bc5904c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
@@ -40,6 +40,7 @@
     singleLine: Boolean = true,
     enabled: Boolean = true,
     shape: Shape = OutlinedTextFieldDefaults.shape,
+    placeholder: @Composable (() -> Unit)? = null,
     modifier: Modifier = Modifier
         .fillMaxWidth()
         .padding(SettingsDimension.textFieldPadding),
@@ -60,6 +61,7 @@
                 Text(text = errorMessage)
             }
         },
+        placeholder = placeholder,
         shape = shape
     )
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index 5ed5999..2da6221 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.utils;
 
+import static java.lang.Math.abs;
+
 import android.content.Context;
 import android.icu.text.DateFormat;
 import android.icu.text.MeasureFormat;
@@ -212,8 +214,8 @@
      * @return The rounded value as a long
      */
     public static long roundTimeToNearestThreshold(long drainTime, long threshold) {
-        long time = Math.abs(drainTime);
-        long multiple = Math.abs(threshold);
+        long time = abs(drainTime);
+        long multiple = abs(threshold);
         final long remainder = time % multiple;
         if (remainder < multiple / 2) {
             return time - remainder;
@@ -222,18 +224,24 @@
         }
     }
 
-    /** Gets the rounded target time string in a short format. */
+    /** Gets the target time string in a short format. */
     public static String getTargetTimeShortString(
             Context context, long targetTimeOffsetMs, long currentTimeMs) {
-        final long roundedTimeOfDayMs =
-                roundTimeToNearestThreshold(
-                        currentTimeMs + targetTimeOffsetMs, FIFTEEN_MINUTES_MILLIS);
+        long targetTimeMs = currentTimeMs + targetTimeOffsetMs;
+        if (targetTimeOffsetMs >= FIFTEEN_MINUTES_MILLIS) {
+            targetTimeMs = roundUpTimeToNextThreshold(targetTimeMs, FIFTEEN_MINUTES_MILLIS);
+        }
 
         // convert the time to a properly formatted string.
         String skeleton = android.text.format.DateFormat.getTimeFormatString(context);
         DateFormat fmt = DateFormat.getInstanceForSkeleton(skeleton);
-        Date date = Date.from(Instant.ofEpochMilli(roundedTimeOfDayMs));
+        Date date = Date.from(Instant.ofEpochMilli(targetTimeMs));
         return fmt.format(date);
     }
-}
 
+    private static long roundUpTimeToNextThreshold(long timeMs, long threshold) {
+        var time = abs(timeMs);
+        var multiple = abs(threshold);
+        return ((time + multiple - 1) / multiple) * multiple;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index cbc382b..4f3b200 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -87,15 +87,31 @@
     }
 
     @Test
-    public void getTargetTimeShortString_returnsTimeShortString() {
+    public void getTargetTimeShortString_lessThan15Minutes_returnsTimeShortStringWithoutRounded() {
         mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
         mContext.getResources().getConfiguration().setLocale(Locale.US);
         var currentTimeMs = Instant.parse("2024-06-06T15:00:00Z").toEpochMilli();
-        var remainingTimeMs = Duration.ofMinutes(30).toMillis();
+        var remainingTimeMs = Duration.ofMinutes(15).toMillis() - 1;
 
         var actualTimeString =
                 PowerUtil.getTargetTimeShortString(mContext, remainingTimeMs, currentTimeMs);
 
-        assertThat(actualTimeString).isEqualTo("3:30 PM");
+        // due to timezone issue in test case, focus on rounded minutes, remove hours part.
+        assertThat(actualTimeString).endsWith("14 PM");
+    }
+
+    @Test
+    public void getTargetTimeShortString_moreThan15Minutes_returnsTimeShortStringWithRounded() {
+        mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
+        mContext.getResources().getConfiguration().setLocale(Locale.US);
+        var currentTimeMs = Instant.parse("2024-06-06T15:00:00Z").toEpochMilli();
+        var remainingTimeMs = Duration.ofMinutes(15).toMillis() + 1;
+
+        var actualTimeString =
+                PowerUtil.getTargetTimeShortString(mContext, remainingTimeMs, currentTimeMs);
+
+        // due to timezone issue in test case, focus on rounded minutes, remove hours part.
+        assertThat(actualTimeString).endsWith("30 PM");
+
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 4ee2db7..042fb63f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -75,6 +75,10 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        // set layer to make alpha animation of brightness slider nicer - otherwise elements
+        // of slider are animated separately and it doesn't look good. See b/329244723
+        setLayerType(LAYER_TYPE_HARDWARE, null);
+
         mQSPanelContainer = findViewById(R.id.expanded_qs_scroll_view);
         mQSPanel = findViewById(R.id.quick_settings_panel);
         mHeader = findViewById(R.id.header);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index 92006a4..e051dab 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -63,7 +63,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        setLayerType(LAYER_TYPE_HARDWARE, null);
 
         mSlider = requireViewById(R.id.slider);
         mSlider.setAccessibilityLabel(getContentDescription().toString());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 4ebb699..271b0a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -39,13 +39,13 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import java.util.concurrent.ConcurrentHashMap
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
-import java.util.concurrent.ConcurrentHashMap
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
 
 /**
  * Inflates and updates icons associated with notifications
@@ -239,8 +239,8 @@
 
         val sbi = icon.toStatusBarIcon(entry)
 
-        // Cache if important conversation.
-        if (isImportantConversation(entry)) {
+        // Cache if important conversation or app icon.
+        if (isImportantConversation(entry) || android.app.Flags.notificationsUseAppIcon()) {
             if (showPeopleAvatar) {
                 entry.icons.peopleAvatarDescriptor = sbi
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
index bfeaced..2fdd2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
@@ -20,6 +20,7 @@
 import android.view.View
 import com.android.app.tracing.traceSection
 import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.Flags
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.StatusBarIconView.NO_COLOR
@@ -34,9 +35,12 @@
     //  view-model (which, at the time of this writing, does not yet exist).
 
     suspend fun bindColor(view: StatusBarIconView, color: Flow<Int>) {
-        color.collectTracingEach("SBIV#bindColor") { color ->
-            view.staticDrawableColor = color
-            view.setDecorColor(color)
+        // Don't change the icon color if an app icon experiment is enabled.
+        if (!android.app.Flags.notificationsUseAppIcon()) {
+            color.collectTracingEach("SBIV#bindColor") { color ->
+                view.staticDrawableColor = color
+                view.setDecorColor(color)
+            }
         }
     }
 
@@ -53,12 +57,15 @@
         iconColors: Flow<NotificationIconColors>,
         contrastColorUtil: ContrastColorUtil,
     ) {
-        iconColors.collectTracingEach("SBIV#bindIconColors") { colors ->
-            val isPreL = java.lang.Boolean.TRUE == view.getTag(R.id.icon_is_pre_L)
-            val isColorized = !isPreL || NotificationUtils.isGrayscale(view, contrastColorUtil)
-            view.staticDrawableColor =
-                if (isColorized) colors.staticDrawableColor(view.viewBounds) else NO_COLOR
-            view.setDecorColor(colors.tint)
+        // Don't change the icon color if an app icon experiment is enabled.
+        if (!android.app.Flags.notificationsUseAppIcon()) {
+            iconColors.collectTracingEach("SBIV#bindIconColors") { colors ->
+                val isPreL = java.lang.Boolean.TRUE == view.getTag(R.id.icon_is_pre_L)
+                val isColorized = !isPreL || NotificationUtils.isGrayscale(view, contrastColorUtil)
+                view.staticDrawableColor =
+                    if (isColorized) colors.staticDrawableColor(view.viewBounds) else NO_COLOR
+                view.setDecorColor(colors.tint)
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
index 6acb12a..52cb48b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
@@ -16,13 +16,12 @@
 
 package com.android.systemui.statusbar.notification.ui.viewbinder
 
-import android.util.Log
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
 import com.android.systemui.util.kotlin.sample
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.coroutineScope
@@ -77,8 +76,7 @@
 }
 
 private val NotificationStackScrollLayout.isHeadsUpAnimatingAway: Flow<Boolean>
-    get() =
-        ConflatedCallbackFlow.conflatedCallbackFlow {
-            setHeadsUpAnimatingAwayListener { animatingAway -> trySend(animatingAway) }
-            awaitClose { setHeadsUpAnimatingAwayListener(null) }
-        }
+    get() = conflatedCallbackFlow {
+        setHeadsUpAnimatingAwayListener { animatingAway -> trySend(animatingAway) }
+        awaitClose { setHeadsUpAnimatingAwayListener(null) }
+    }
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 32a0ef4..8002300 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -18,6 +18,7 @@
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 
+import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.location.flags.Flags;
 import android.net.ConnectivityManager;
@@ -32,6 +33,7 @@
 import android.os.PowerManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.PreciseCallState;
+import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -572,6 +574,10 @@
         }
     }
 
+    @RequiresPermission(allOf = {
+        android.Manifest.permission.ACCESS_COARSE_LOCATION,
+        android.Manifest.permission.READ_PHONE_STATE
+    })
     private void handleRequestSuplConnection(int agpsType, byte[] suplIpAddr) {
         mAGpsDataConnectionIpAddr = null;
         mAGpsType = agpsType;
@@ -605,6 +611,19 @@
         NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
         networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType));
         networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+        if (com.android.internal.telephony.flags.Flags.satelliteInternet()) {
+            // Add transport type NetworkCapabilities.TRANSPORT_SATELLITE on satellite network.
+            TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+            if (telephonyManager != null) {
+                ServiceState state = telephonyManager.getServiceState();
+                if (state != null && state.isUsingNonTerrestrialNetwork()) {
+                    networkRequestBuilder.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+                    networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE);
+                }
+            }
+        }
+
         // During an emergency call, and when we have cached the Active Sub Id, we set the
         // Network Specifier so that the network request goes to the correct Sub Id
         if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {