Merge "Use dark theme for alerts if device is in dark mode"
diff --git a/src/com/android/phone/SimPhonebookProvider.java b/src/com/android/phone/SimPhonebookProvider.java
index 04c4f48..8952865 100644
--- a/src/com/android/phone/SimPhonebookProvider.java
+++ b/src/com/android/phone/SimPhonebookProvider.java
@@ -43,7 +43,7 @@
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
-import android.util.Pair;
+import android.util.SparseArray;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -55,10 +55,10 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.MoreExecutors;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -333,19 +333,25 @@
         if (recordsSize == null || getRecordCount(recordsSize) == 0) {
             return;
         }
+        int efid = efIdForEfType(efType);
+        // Have to load the existing records to get the size because there may be more than one
+        // phonebook set in which case the total capacity is the sum of the capacity of EF_ADN for
+        // all the phonebook sets whereas the recordsSize is just the size for a single EF.
+        List<AdnRecord> existingRecords = mIccPhoneBookSupplier.get()
+                .getAdnRecordsInEfForSubscriber(subscriptionInfo.getSubscriptionId(), efid);
+        if (existingRecords == null) {
+            existingRecords = ImmutableList.of();
+        }
         MatrixCursor.RowBuilder row = result.newRow()
                 .add(ElementaryFiles.SLOT_INDEX, subscriptionInfo.getSimSlotIndex())
                 .add(ElementaryFiles.SUBSCRIPTION_ID, subscriptionInfo.getSubscriptionId())
                 .add(ElementaryFiles.EF_TYPE, efType)
-                .add(ElementaryFiles.MAX_RECORDS, getRecordCount(recordsSize))
+                .add(ElementaryFiles.MAX_RECORDS, existingRecords.size())
                 .add(ElementaryFiles.NAME_MAX_LENGTH,
                         AdnRecord.getMaxAlphaTagBytes(getRecordSize(recordsSize)))
                 .add(ElementaryFiles.PHONE_NUMBER_MAX_LENGTH,
                         AdnRecord.getMaxPhoneNumberDigits());
         if (result.getColumnIndex(ElementaryFiles.RECORD_COUNT) != -1) {
-            int efid = efIdForEfType(efType);
-            List<AdnRecord> existingRecords = mIccPhoneBookSupplier.get()
-                    .getAdnRecordsInEfForSubscriber(subscriptionInfo.getSubscriptionId(), efid);
             int nonEmptyCount = 0;
             for (AdnRecord record : existingRecords) {
                 if (!record.isEmpty()) {
@@ -368,39 +374,46 @@
             return new MatrixCursor(projection, 0);
         }
         MatrixCursor result = new MatrixCursor(projection, records.size());
-        List<Pair<AdnRecord, MatrixCursor.RowBuilder>> rowBuilders = new ArrayList<>(
-                records.size());
-        for (AdnRecord record : records) {
+        SparseArray<MatrixCursor.RowBuilder> rowBuilders = new SparseArray<>(records.size());
+        for (int i = 0; i < records.size(); i++) {
+            AdnRecord record = records.get(i);
             if (!record.isEmpty()) {
-                rowBuilders.add(Pair.create(record, result.newRow()));
+                rowBuilders.put(i, result.newRow());
             }
         }
         // This is kind of ugly but avoids looking up columns in an inner loop.
         for (String column : projection) {
             switch (column) {
                 case SimRecords.SUBSCRIPTION_ID:
-                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
-                        row.second.add(args.subscriptionId);
+                    for (int i = 0; i < rowBuilders.size(); i++) {
+                        rowBuilders.valueAt(i).add(args.subscriptionId);
                     }
                     break;
                 case SimRecords.ELEMENTARY_FILE_TYPE:
-                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
-                        row.second.add(args.efType);
+                    for (int i = 0; i < rowBuilders.size(); i++) {
+                        rowBuilders.valueAt(i).add(args.efType);
                     }
                     break;
                 case SimRecords.RECORD_NUMBER:
-                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
-                        row.second.add(row.first.getRecId());
+                    for (int i = 0; i < rowBuilders.size(); i++) {
+                        int index = rowBuilders.keyAt(i);
+                        MatrixCursor.RowBuilder rowBuilder = rowBuilders.valueAt(i);
+                        // See b/201685690. The logical record number, i.e. the 1-based index in the
+                        // list, is used the rather than AdnRecord.getRecId() because getRecId is
+                        // not offset when a single logical EF is made up of multiple physical EFs.
+                        rowBuilder.add(index + 1);
                     }
                     break;
                 case SimRecords.NAME:
-                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
-                        row.second.add(row.first.getAlphaTag());
+                    for (int i = 0; i < rowBuilders.size(); i++) {
+                        AdnRecord record = records.get(rowBuilders.keyAt(i));
+                        rowBuilders.valueAt(i).add(record.getAlphaTag());
                     }
                     break;
                 case SimRecords.PHONE_NUMBER:
-                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
-                        row.second.add(row.first.getNumber());
+                    for (int i = 0; i < rowBuilders.size(); i++) {
+                        AdnRecord record = records.get(rowBuilders.keyAt(i));
+                        rowBuilders.valueAt(i).add(record.getNumber());
                     }
                     break;
                 default:
@@ -765,20 +778,9 @@
         if (records == null || args.recordNumber > records.size()) {
             return null;
         }
-        AdnRecord result = records.get(args.recordNumber - 1);
-        // This should be true but the service could have a different implementation.
-        if (result.getRecId() == args.recordNumber) {
-            return result;
-        }
-        for (AdnRecord record : records) {
-            if (record.getRecId() == args.recordNumber) {
-                return result;
-            }
-        }
-        return null;
+        return records.get(args.recordNumber - 1);
     }
 
-
     private int[] getRecordsSizeForEf(PhonebookArgs args) {
         try {
             return mIccPhoneBookSupplier.get().getAdnRecordsSizeForSubscriber(
diff --git a/tests/src/com/android/phone/SimPhonebookProviderTest.java b/tests/src/com/android/phone/SimPhonebookProviderTest.java
index 848ba42..d8518f8 100644
--- a/tests/src/com/android/phone/SimPhonebookProviderTest.java
+++ b/tests/src/com/android/phone/SimPhonebookProviderTest.java
@@ -181,16 +181,16 @@
     public void query_entityFiles_multiSim_returnsCursorWithRowForEachSimEf() {
         setupSimsWithSubscriptionIds(2, 3, 7);
 
-        mIccPhoneBook.setRecordsSize(2, IccConstants.EF_ADN, 10, 25);
-        mIccPhoneBook.setRecordsSize(2, IccConstants.EF_FDN, 5, 20);
-        mIccPhoneBook.setRecordsSize(2, IccConstants.EF_SDN, 15, 20);
-        mIccPhoneBook.setRecordsSize(3, IccConstants.EF_ADN, 100, 30);
+        mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_ADN, 10, 25);
+        mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_FDN, 5, 20);
+        mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_SDN, 15, 20);
+        mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_ADN, 100, 30);
         // These Will be omitted from results because zero size indicates the EF is not supported.
-        mIccPhoneBook.setRecordsSize(3, IccConstants.EF_FDN, 0, 0);
-        mIccPhoneBook.setRecordsSize(3, IccConstants.EF_SDN, 0, 0);
-        mIccPhoneBook.setRecordsSize(7, IccConstants.EF_ADN, 0, 0);
-        mIccPhoneBook.setRecordsSize(7, IccConstants.EF_FDN, 0, 0);
-        mIccPhoneBook.setRecordsSize(7, IccConstants.EF_SDN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_FDN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_SDN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_ADN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_FDN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_SDN, 0, 0);
 
         String[] projection = {
                 ElementaryFiles.SLOT_INDEX, ElementaryFiles.SUBSCRIPTION_ID,
@@ -212,15 +212,44 @@
     public void query_entityFiles_simWithZeroSizes_returnsEmptyCursor() {
         setupSimsWithSubscriptionIds(1);
 
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 0, 0);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 0, 0);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 0, 0);
 
         try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null, null)) {
             assertThat(cursor).hasCount(0);
         }
     }
 
+    /**
+     * USIM cards support more than 255 records by having multiple files for one EF type but
+     * IIccPhoneBook.getAdnRecordsSizeForSubscriber returns the size for a single file and so is
+     * inaccurate for such SIMs.
+     *
+     * <p>See b/201385523#comment4 and b/201685690
+     */
+    @Test
+    public void query_entityFiles_adnRecordCountExceedsSize_returnsAdnRecordCountAsMaxRecords() {
+        setupSimsWithSubscriptionIds(1);
+
+        // There are 400 records returned by getAdnRecordsInEfForSubscriber but the count returned
+        // by getAdnRecordsSizeForSubscriber is only 200.
+        AdnRecord[] records = mIccPhoneBook.createEmptyRecords(IccConstants.EF_ADN, 400);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 200, 20);
+        mIccPhoneBook.setRecords(1, IccConstants.EF_ADN, records);
+
+        String[] projection = {
+                ElementaryFiles.SUBSCRIPTION_ID, ElementaryFiles.EF_TYPE,
+                ElementaryFiles.MAX_RECORDS
+        };
+        try (Cursor cursor = mResolver.query(
+                ElementaryFiles.CONTENT_URI, projection, null, null)) {
+            assertThat(cursor).hasCount(1);
+            assertThat(cursor)
+                    .atRow(0).hasRowValues(1, ElementaryFiles.EF_ADN, 400);
+        }
+    }
+
     @Test
     public void query_entityFilesItem_nullProjection_returnsCursorWithCorrectProjection() {
         setupSimsWithSubscriptionIds(1);
@@ -549,7 +578,7 @@
     @Test
     public void query_adnRecords_zeroSizeEf_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 0, 0);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 0, 0);
 
         IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
                 () -> mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null, null));
@@ -618,9 +647,9 @@
     @Test
     public void query_itemUriEmptyRecord_returnsEmptyCursor() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 1, 30);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 1, 30);
 
         try (Cursor adnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1),
                 null, null, null);
@@ -638,9 +667,9 @@
     @Test
     public void query_itemUriIndexExceedsMax_returnsEmptyCursor() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 1, 30);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 1, 30);
 
         try (Cursor adnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2),
                 null, null, null);
@@ -741,7 +770,7 @@
     @Test
     public void insert_efFull_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
         mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Existing", "8005550101");
 
         ContentValues values = new ContentValues();
@@ -843,7 +872,7 @@
     @Test
     public void insert_phoneNumberOmitted_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
 
         ContentValues values = new ContentValues();
         values.put(SimRecords.NAME, "Name");
@@ -856,7 +885,7 @@
     @Test
     public void insert_nameTooLong_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
 
         ContentValues values = new ContentValues();
         // Name is limited to 11 characters when the max record size is 25
@@ -879,7 +908,7 @@
     @Test
     public void insert_phoneNumberTooLong_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
 
         ContentValues values = new ContentValues();
         values.put(SimRecords.NAME, "Name");
@@ -895,7 +924,7 @@
     @Test
     public void insert_numberWithInvalidCharacters_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 32);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 32);
 
         ContentValues values = new ContentValues();
         values.put(SimRecords.NAME, "Name");
@@ -915,7 +944,7 @@
     @Test
     public void insert_unsupportedColumn_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
 
         ContentValues values = new ContentValues();
         values.put(SimRecords.NAME, "Name");
@@ -1007,7 +1036,7 @@
     @Test
     public void update_indexExceedingMax_returnsZero() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
 
         ContentValues values = new ContentValues();
         values.put(SimRecords.NAME, "name");
@@ -1046,7 +1075,7 @@
     public void delete_indexExceedingMax_returnsZero() {
         setupSimsWithSubscriptionIds(1);
         mIccPhoneBook.makeAllEfsSupported(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30);
 
         int result = mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), null);
 
@@ -1067,7 +1096,7 @@
     @Test
     public void update_nameOrNumberTooLong_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25);
         mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101");
 
         ContentValues values = new ContentValues();
@@ -1097,7 +1126,7 @@
     @Test
     public void update_numberWithInvalidCharacters_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 32);
+        mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 32);
         mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101");
 
         ContentValues values = new ContentValues();
@@ -1324,7 +1353,7 @@
         // The key for both maps is the (subscription ID, efid)
         private Map<Pair<Integer, Integer>, AdnRecord[]> mRecords = new HashMap<>();
         // The value is the single record size
-        private Map<Pair<Integer, Integer>, Integer> mRecordSizes = new HashMap<>();
+        private Map<Pair<Integer, Integer>, int[]> mRecordSizes = new HashMap<>();
 
         private int mDefaultSubscriptionId = 101;
 
@@ -1332,7 +1361,7 @@
             // Assume that if records are being added then the test wants it to be a valid
             // elementary file so set sizes as well.
             if (!mRecordSizes.containsKey(key)) {
-                setRecordsSize(key.first, key.second,
+                setupEfWithSizes(key.first, key.second,
                         Math.max(record.getRecId(), DEFAULT_RECORDS_COUNT), DEFAULT_RECORD_SIZE);
             }
             mRecords.get(key)[record.getRecId() - 1] = record;
@@ -1402,18 +1431,33 @@
          * subscription IDs.
          */
         public void makeAllEfsSupported(int subscriptionId) {
-            setRecordsSize(subscriptionId, IccConstants.EF_ADN, DEFAULT_RECORDS_COUNT,
+            setupEfWithSizes(subscriptionId, IccConstants.EF_ADN, DEFAULT_RECORDS_COUNT,
                     DEFAULT_RECORD_SIZE);
-            setRecordsSize(subscriptionId, IccConstants.EF_FDN, DEFAULT_RECORDS_COUNT,
+            setupEfWithSizes(subscriptionId, IccConstants.EF_FDN, DEFAULT_RECORDS_COUNT,
                     DEFAULT_RECORD_SIZE);
-            setRecordsSize(subscriptionId, IccConstants.EF_SDN, DEFAULT_RECORDS_COUNT,
+            setupEfWithSizes(subscriptionId, IccConstants.EF_SDN, DEFAULT_RECORDS_COUNT,
                     DEFAULT_RECORD_SIZE);
         }
 
+        public void setRecords(int subscriptionId, int efid, AdnRecord[] records) {
+            mRecords.put(Pair.create(subscriptionId, efid), records);
+        }
+
         public void setRecordsSize(int subscriptionId, int efid, int maxRecordCount,
                 int maxRecordSize) {
+            setRecordsSize(Pair.create(subscriptionId, efid), maxRecordCount, maxRecordSize);
+        }
+
+        private void setRecordsSize(Pair<Integer, Integer> key, int maxRecordCount,
+                int maxRecordSize) {
+            int[] sizes = { maxRecordSize, maxRecordSize * maxRecordCount, maxRecordCount };
+            mRecordSizes.put(key, sizes);
+        }
+
+        public void setupEfWithSizes(int subscriptionId, int efid, int maxRecordCount,
+                int maxRecordSize) {
             Pair<Integer, Integer> key = Pair.create(subscriptionId, efid);
-            mRecordSizes.put(key, maxRecordSize);
+            setRecordsSize(key, maxRecordCount, maxRecordSize);
             AdnRecord[] records = mRecords.computeIfAbsent(key, unused ->
                     createEmptyRecords(efid, maxRecordCount));
             if (records.length < maxRecordCount) {
@@ -1421,7 +1465,7 @@
             }
         }
 
-        private AdnRecord[] createEmptyRecords(int efid, int count) {
+        AdnRecord[] createEmptyRecords(int efid, int count) {
             AdnRecord[] records = new AdnRecord[count];
             for (int i = 0; i < records.length; i++) {
                 if (records[i] == null) {
@@ -1499,12 +1543,11 @@
         @Override
         public int[] getAdnRecordsSizeForSubscriber(int subId, int efid) {
             Pair<Integer, Integer> key = Pair.create(subId, efid);
-            Integer recordSize = mRecordSizes.get(key);
-            if (recordSize == null) {
+            int[] recordsSize = mRecordSizes.get(key);
+            if (recordsSize == null) {
                 return new int[]{0, 0, 0};
             }
-            int count = mRecords.get(key).length;
-            return new int[]{recordSize, recordSize * count, count};
+            return recordsSize;
         }
 
         @Override