Merge "Add DataFailCause for IWLAN_CONGESTION."
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
index 5b0ae92..8129d99 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -17,6 +17,7 @@
 #include "idmap2/CommandLineOptions.h"
 
 #include <algorithm>
+#include <cassert>
 #include <iomanip>
 #include <iostream>
 #include <memory>
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 6515d55..06650f6 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -17,6 +17,7 @@
 #include "idmap2/Idmap.h"
 
 #include <algorithm>
+#include <cassert>
 #include <iostream>
 #include <iterator>
 #include <limits>
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 53c6db0..f9b3a8c 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -51,6 +51,49 @@
 
 using uirenderer::PaintUtils;
 
+class SkiaCanvas::Clip {
+public:
+    Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
+            : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {}
+    Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m)
+            : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
+    Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
+            : mType(Type::Path), mOp(op), mMatrix(m), mPath(std::in_place, path) {}
+
+    void apply(SkCanvas* canvas) const {
+        canvas->setMatrix(mMatrix);
+        switch (mType) {
+            case Type::Rect:
+                // Don't anti-alias rectangular clips
+                canvas->clipRect(mRRect.rect(), mOp, false);
+                break;
+            case Type::RRect:
+                // Ensure rounded rectangular clips are anti-aliased
+                canvas->clipRRect(mRRect, mOp, true);
+                break;
+            case Type::Path:
+                // Ensure path clips are anti-aliased
+                canvas->clipPath(mPath.value(), mOp, true);
+                break;
+        }
+    }
+
+private:
+    enum class Type {
+        Rect,
+        RRect,
+        Path,
+    };
+
+    Type mType;
+    SkClipOp mOp;
+    SkMatrix mMatrix;
+
+    // These are logically a union (tracked separately due to non-POD path).
+    std::optional<SkPath> mPath;
+    SkRRect mRRect;
+};
+
 Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
     return new SkiaCanvas(bitmap);
 }
@@ -194,49 +237,6 @@
     }
 }
 
-class SkiaCanvas::Clip {
-public:
-    Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
-            : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {}
-    Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m)
-            : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
-    Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
-            : mType(Type::Path), mOp(op), mMatrix(m), mPath(std::in_place, path) {}
-
-    void apply(SkCanvas* canvas) const {
-        canvas->setMatrix(mMatrix);
-        switch (mType) {
-            case Type::Rect:
-                // Don't anti-alias rectangular clips
-                canvas->clipRect(mRRect.rect(), mOp, false);
-                break;
-            case Type::RRect:
-                // Ensure rounded rectangular clips are anti-aliased
-                canvas->clipRRect(mRRect, mOp, true);
-                break;
-            case Type::Path:
-                // Ensure path clips are anti-aliased
-                canvas->clipPath(mPath.value(), mOp, true);
-                break;
-        }
-    }
-
-private:
-    enum class Type {
-        Rect,
-        RRect,
-        Path,
-    };
-
-    Type mType;
-    SkClipOp mOp;
-    SkMatrix mMatrix;
-
-    // These are logically a union (tracked separately due to non-POD path).
-    std::optional<SkPath> mPath;
-    SkRRect mRRect;
-};
-
 const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const {
     const SaveRec* rec = mSaveStack ? static_cast<const SaveRec*>(mSaveStack->back()) : nullptr;
     int currentSaveCount = mCanvas->getSaveCount();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 3d3ac83..83168cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -305,7 +305,7 @@
         // Then we save an update with the current time
         verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
         componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
-                ?.dropLastWhile { it.isEmpty() }.forEach {
+                .dropLastWhile { it.isEmpty() }.forEach {
             val result = it.split("/")
             assertThat(result.size).isEqualTo(3)
             assertThat(result[2].toLong()).isEqualTo(currentTime)
@@ -392,7 +392,7 @@
         // Then we store the new lastPlayed time
         verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
         componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
-                ?.dropLastWhile { it.isEmpty() }.forEach {
+                .dropLastWhile { it.isEmpty() }.forEach {
                     val result = it.split("/")
                     assertThat(result.size).isEqualTo(3)
                     assertThat(result[2].toLong()).isEqualTo(currentTime)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8856147..bc065d1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17794,6 +17794,13 @@
     }
 
     /**
+     * Reset the dropbox rate limiter
+     */
+    void resetDropboxRateLimiter() {
+        mDropboxRateLimiter.reset();
+    }
+
+    /**
      * Kill processes for the user with id userId and that depend on the package named packageName
      */
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 570a157..82d0b67 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -350,6 +350,8 @@
                     return runSetBgAbusiveUids(pw);
                 case "list-bg-exemptions-config":
                     return runListBgExemptionsConfig(pw);
+                case "reset-dropbox-rate-limiter":
+                    return runResetDropboxRateLimiter();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -3374,6 +3376,11 @@
         return 0;
     }
 
+    int runResetDropboxRateLimiter() throws RemoteException {
+        mInternal.resetDropboxRateLimiter();
+        return 0;
+    }
+
     private Resources getResources(PrintWriter pw) throws RemoteException {
         // system resources does not contain all the device configuration, construct it manually.
         Configuration config = mInterface.getConfiguration();
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index baf062d..6087f76 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -19,11 +19,13 @@
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 
 /** Rate limiter for adding errors into dropbox. */
 public class DropboxRateLimiter {
+    private static final String TAG = "DropboxRateLimiter";
     // After RATE_LIMIT_ALLOWED_ENTRIES have been collected (for a single breakdown of
     // process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has
     // elapsed, after which the current count for this breakdown will be reset.
@@ -105,6 +107,15 @@
         mLastMapCleanUp = now;
     }
 
+    /** Resets the rate limiter memory. */
+    void reset() {
+        synchronized (mErrorClusterRecords) {
+            mErrorClusterRecords.clear();
+        }
+        mLastMapCleanUp = 0L;
+        Slog.i(TAG, "Rate limiter reset.");
+    }
+
     String errorKey(String eventType, String processName) {
         return eventType + processName;
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c73c5a0..e2c4cbd 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -123,6 +123,7 @@
 import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_NOTIFICATION_BOOL;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -3158,7 +3159,8 @@
      * active merge set [A,B], we'd return a new template that primarily matches
      * A, but also matches B.
      */
-    private static NetworkTemplate normalizeTemplate(@NonNull NetworkTemplate template,
+    @VisibleForTesting(visibility = PRIVATE)
+    static NetworkTemplate normalizeTemplate(@NonNull NetworkTemplate template,
             @NonNull List<String[]> mergedList) {
         // Now there are several types of network which uses Subscriber Id to store network
         // information. For instance:
@@ -3168,6 +3170,12 @@
         if (template.getSubscriberIds().isEmpty()) return template;
 
         for (final String[] merged : mergedList) {
+            // In some rare cases (e.g. b/243015487), merged subscriberId list might contain
+            // duplicated items. Deduplication for better error handling.
+            final ArraySet mergedSet = new ArraySet(merged);
+            if (mergedSet.size() != merged.length) {
+                Log.wtf(TAG, "Duplicated merged list detected: " + Arrays.toString(merged));
+            }
             // TODO: Handle incompatible subscriberIds if that happens in practice.
             for (final String subscriberId : template.getSubscriberIds()) {
                 if (com.android.net.module.util.CollectionUtils.contains(merged, subscriberId)) {
@@ -3175,7 +3183,7 @@
                     // a template that matches all merged subscribers.
                     return new NetworkTemplate.Builder(template.getMatchRule())
                             .setWifiNetworkKeys(template.getWifiNetworkKeys())
-                            .setSubscriberIds(Set.of(merged))
+                            .setSubscriberIds(mergedSet)
                             .setMeteredness(template.getMeteredness())
                             .build();
                 }
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index d322290..3c68662 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -205,6 +205,7 @@
                     .setRequiresDeviceIdle(true)
                     .setRequiresCharging(true)
                     .setPeriodic(BG_PROCESS_PERIOD)
+                    .setPriority(JobInfo.PRIORITY_MIN)
                     .build());
         }
 
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
index 45e0d09..4ef6875 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -628,6 +628,7 @@
             CodePath.SAME, CodePath.DIFFERENT ->
                 throw AssertionError("secondDataPath cannot be a data path")
             CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
+            else -> {}
         }
     }
 
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 0f2fe44..821ce5e 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -75,6 +75,7 @@
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
 import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
+import static com.android.server.net.NetworkPolicyManagerService.normalizeTemplate;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -2030,6 +2031,18 @@
                 METERED_NO, actualPolicy.template.getMeteredness());
     }
 
+    @Test
+    public void testNormalizeTemplate_duplicatedMergedImsiList() {
+        final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_CARRIER)
+                .setSubscriberIds(Set.of(TEST_IMSI)).build();
+        final String[] mergedImsiGroup = new String[] {TEST_IMSI, TEST_IMSI};
+        final ArrayList<String[]> mergedList = new ArrayList<>();
+        mergedList.add(mergedImsiGroup);
+        // Verify the duplicated items in the merged IMSI list won't crash the system.
+        final NetworkTemplate result = normalizeTemplate(template, mergedList);
+        assertEquals(template, result);
+    }
+
     private String formatBlockedStateError(int uid, int rule, boolean metered,
             boolean backgroundRestricted) {
         return String.format(
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 0f034ad..b003f59 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -88,8 +88,8 @@
     public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
 
     /**
-     * Reason code (returned via {@link #getReason()}), which indicates that the video telephony
-     * call was disconnected because IMS access is blocked.
+     * Reason code (returned via {@link #getReason()}), which indicates that the call was
+     * disconnected because IMS access is blocked.
      */
     public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
 
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
index b2c5f4a..8e62388 100644
--- a/tools/xmlpersistence/src/main/kotlin/Generator.kt
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -149,6 +149,7 @@
                 when (field) {
                     is ClassFieldInfo -> this += field.allClassFields
                     is ListFieldInfo -> this += field.element.allClassFields
+                    else -> {}
                 }
             }
         }