Merge "Explicitly set apex updatable to false."
diff --git a/src/com/android/phone/SimPhonebookProvider.java b/src/com/android/phone/SimPhonebookProvider.java
index 8307672..7a1e93c 100644
--- a/src/com/android/phone/SimPhonebookProvider.java
+++ b/src/com/android/phone/SimPhonebookProvider.java
@@ -59,7 +59,6 @@
 import java.util.Arrays;
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
@@ -106,7 +105,6 @@
     private static final int ELEMENTARY_FILES_ITEM = 101;
     private static final int SIM_RECORDS = 200;
     private static final int SIM_RECORDS_ITEM = 201;
-    private static final int VALIDATE_NAME = 300;
 
     static {
         URI_MATCHER.addURI(SimPhonebookContract.AUTHORITY,
@@ -120,10 +118,6 @@
                 SimPhonebookContract.SUBSCRIPTION_ID_PATH_SEGMENT + "/#/*", SIM_RECORDS);
         URI_MATCHER.addURI(SimPhonebookContract.AUTHORITY,
                 SimPhonebookContract.SUBSCRIPTION_ID_PATH_SEGMENT + "/#/*/#", SIM_RECORDS_ITEM);
-        URI_MATCHER.addURI(SimPhonebookContract.AUTHORITY,
-                SimPhonebookContract.SUBSCRIPTION_ID_PATH_SEGMENT + "/#/*/"
-                        + SimRecords.VALIDATE_NAME_PATH_SEGMENT,
-                VALIDATE_NAME);
     }
 
     // Only allow 1 write at a time to prevent races; the mutations are based on reads of the
@@ -209,6 +203,31 @@
         return true;
     }
 
+    @Nullable
+    @Override
+    public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+        if (SimRecords.GET_ENCODED_NAME_LENGTH_METHOD_NAME.equals(method)) {
+            // No permissions checks needed. This isn't leaking any sensitive information since the
+            // name we are checking is provided by the caller.
+            return callForEncodedNameLength(arg);
+        }
+        return super.call(method, arg, extras);
+    }
+
+    private Bundle callForEncodedNameLength(String name) {
+        Bundle result = new Bundle();
+        result.putInt(SimRecords.EXTRA_ENCODED_NAME_LENGTH, getEncodedNameLength(name));
+        return result;
+    }
+
+    private int getEncodedNameLength(String name) {
+        if (Strings.isNullOrEmpty(name)) {
+            return 0;
+        } else {
+            byte[] encoded = AdnRecord.encodeAlphaTag(name);
+            return encoded.length;
+        }
+    }
 
     @Nullable
     @Override
@@ -231,8 +250,6 @@
             case SIM_RECORDS_ITEM:
                 return querySimRecordsItem(PhonebookArgs.forSimRecordsItem(uri, queryArgs),
                         projection);
-            case VALIDATE_NAME:
-                return queryValidateName(PhonebookArgs.forValidateName(uri, queryArgs), queryArgs);
             default:
                 throw new IllegalArgumentException("Unsupported Uri " + uri);
         }
@@ -402,21 +419,6 @@
         return result;
     }
 
-    private Cursor queryValidateName(PhonebookArgs args, @Nullable Bundle queryArgs) {
-        if (queryArgs == null) {
-            throw new IllegalArgumentException(SimRecords.NAME + " is required.");
-        }
-        validateSubscriptionAndEf(args);
-        String name = queryArgs.getString(SimRecords.NAME);
-
-        // Cursor extras are used to return the result.
-        Cursor result = new MatrixCursor(new String[0], 0);
-        Bundle extras = new Bundle();
-        extras.putParcelable(SimRecords.EXTRA_NAME_VALIDATION_RESULT, validateName(args, name));
-        result.setExtras(extras);
-        return result;
-    }
-
     @Nullable
     @Override
     public String getType(@NonNull Uri uri) {
@@ -665,23 +667,6 @@
         }
     }
 
-    private SimRecords.NameValidationResult validateName(
-            PhonebookArgs args, @Nullable String name) {
-        name = Strings.nullToEmpty(name);
-        int recordSize = getRecordSize(getRecordsSizeForEf(args));
-        // Validating the name consists of encoding the record in the binary format that it is
-        // stored on the SIM then decoding it and checking whether the decoded name is the same.
-        // The AOSP implementation of AdnRecord replaces unsupported characters with spaces during
-        // encoding.
-        // TODO: It would be good to update AdnRecord to support UCS-2 on the encode path (it
-        //  supports it on the decode path). Right now it's not supported and so any non-latin
-        //  characters will not be valid (at least in the AOSP implementation).
-        byte[] encodedName = AdnRecord.encodeAlphaTag(name);
-        String sanitizedName = AdnRecord.decodeAlphaTag(encodedName, 0, encodedName.length);
-        return new SimRecords.NameValidationResult(name, sanitizedName,
-                encodedName.length, AdnRecord.getMaxAlphaTagBytes(recordSize));
-    }
-
     private void validatePhoneNumber(@Nullable String phoneNumber) {
         if (phoneNumber == null || phoneNumber.isEmpty()) {
             throw new IllegalArgumentException(SimRecords.PHONE_NUMBER + " is required.");
@@ -715,13 +700,11 @@
         validatePhoneNumber(phoneNumber);
 
         String name = values.getAsString(SimRecords.NAME);
-        SimRecords.NameValidationResult result = validateName(args, name);
+        int length = getEncodedNameLength(name);
+        int maxLength = AdnRecord.getMaxAlphaTagBytes(getRecordSize(getRecordsSizeForEf(args)));
 
-        if (result.getEncodedLength() > result.getMaxEncodedLength()) {
+        if (length > maxLength) {
             throw new IllegalArgumentException(SimRecords.NAME + " is too long.");
-        } else if (!Objects.equals(result.getName(), result.getSanitizedName())) {
-            throw new IllegalArgumentException(
-                    SimRecords.NAME + " contains unsupported characters.");
         }
     }
 
@@ -895,19 +878,6 @@
                     queryArgs);
         }
 
-        /**
-         * Pattern: subid/${subscriptionId}/${efName}/validate_name
-         *
-         * @see SimRecords#validateName(ContentResolver, int, int, String)
-         * @see #VALIDATE_NAME
-         */
-        static PhonebookArgs forValidateName(Uri uri, Bundle queryArgs) {
-            int subscriptionId = parseSubscriptionIdFromUri(uri, 1);
-            String efName = uri.getPathSegments().get(2);
-            return PhonebookArgs.createFromEfName(
-                    uri, subscriptionId, efName, -1, queryArgs);
-        }
-
         private static int parseSubscriptionIdFromUri(Uri uri, int pathIndex) {
             if (pathIndex == -1) {
                 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
diff --git a/tests/src/com/android/phone/SimPhonebookProviderTest.java b/tests/src/com/android/phone/SimPhonebookProviderTest.java
index 1d48694..8778529 100644
--- a/tests/src/com/android/phone/SimPhonebookProviderTest.java
+++ b/tests/src/com/android/phone/SimPhonebookProviderTest.java
@@ -78,10 +78,7 @@
 @RunWith(AndroidJUnit4.class)
 public final class SimPhonebookProviderTest {
 
-    // Emojis aren't currently supported for the ADN record label.
     private static final String EMOJI = new String(Character.toChars(0x1F642));
-    private static final String UNSUPPORTED_NAME = ":)=" + EMOJI + ";ni=日;hon=本;";
-    private static final String UNSUPPORTED_NAME2 = "日本" + EMOJI;
     private static final Correspondence<AdnRecord, AdnRecord> ADN_RECORD_IS_EQUAL =
             Correspondence.from(AdnRecord::isEqual, "isEqual");
 
@@ -674,6 +671,30 @@
     }
 
     @Test
+    public void insert_nameWithNonGsmCharacters_addsAdnRecord() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        String name = "abc日本" + EMOJI;
+        values.put(SimRecords.NAME, name);
+        values.put(SimRecords.PHONE_NUMBER, "8005550101");
+
+        Uri uri = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+
+        List<AdnRecord> records = mIccPhoneBook.getAdnRecordsInEfForSubscriber(
+                1, IccConstants.EF_ADN).stream()
+                .filter(((Predicate<AdnRecord>) AdnRecord::isEmpty).negate())
+                .collect(Collectors.toList());
+
+        assertThat(records)
+                .comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, name, "8005550101"));
+
+        assertThat(uri).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1));
+    }
+
+    @Test
     public void insert_nullValues_returnsNull() {
         setupSimsWithSubscriptionIds(1);
         mIccPhoneBook.makeAllEfsSupported(1);
@@ -753,7 +774,7 @@
         mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
 
         ContentValues values = new ContentValues();
-        // Name is limited to 11 characters
+        // Name is limited to 11 characters when the max record size is 25
         values.put(SimRecords.NAME, "1234567890ab");
         values.put(SimRecords.PHONE_NUMBER, "8005550102");
 
@@ -761,6 +782,13 @@
                 () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
 
         assertThat(e).hasMessageThat().isEqualTo(SimRecords.NAME + " is too long.");
+
+        // 2 bytes per character and 4 for the emoji. So this is 14 characters long.
+        values.put(SimRecords.NAME, "abc日本" + EMOJI);
+        e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
+
+        assertThat(e).hasMessageThat().isEqualTo(SimRecords.NAME + " is too long.");
     }
 
     @Test
@@ -780,36 +808,22 @@
     }
 
     @Test
-    public void insert_illegalCharacters_throwsCorrectException() {
+    public void insert_numberWithInvalidCharacters_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
         mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 32);
 
         ContentValues values = new ContentValues();
         values.put(SimRecords.NAME, "Name");
-        values.put(SimRecords.PHONE_NUMBER, "1800J550A0B");
+        values.put(SimRecords.PHONE_NUMBER, "(800)555-0190 x7777");
 
         IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
+                () -> mResolver.insert(SimRecords.getContentUri(1, ElementaryFiles.EF_ADN),
+                        values,
+                        null));
         assertThat(e).hasMessageThat().isEqualTo(
                 SimRecords.PHONE_NUMBER + " contains unsupported characters.");
 
-        values.put(SimRecords.NAME, UNSUPPORTED_NAME);
-        values.put(SimRecords.PHONE_NUMBER, "18005550101");
-
-        e = assertThrows(IllegalArgumentException.class,
-                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
-        assertThat(e).hasMessageThat().isEqualTo(
-                SimRecords.NAME + " contains unsupported characters.");
-
-        values.put(SimRecords.NAME, UNSUPPORTED_NAME2);
-        values.put(SimRecords.PHONE_NUMBER, "18005550101");
-
-        e = assertThrows(IllegalArgumentException.class,
-                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
-        assertThat(e).hasMessageThat().isEqualTo(
-                SimRecords.NAME + " contains unsupported characters.");
-
-        // The inserts didn't actually add any data.
+        // The insert didn't actually change the data.
         assertThat(mIccPhoneBook.getAllValidRecords()).isEmpty();
     }
 
@@ -996,7 +1010,7 @@
     }
 
     @Test
-    public void update_nameOrNumberWithInvalidCharacters_throwsCorrectException() {
+    public void update_numberWithInvalidCharacters_throwsCorrectException() {
         setupSimsWithSubscriptionIds(1);
         mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 32);
         mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101");
@@ -1012,18 +1026,7 @@
         assertThat(e).hasMessageThat().isEqualTo(
                 SimRecords.PHONE_NUMBER + " contains unsupported characters.");
 
-        // Unicode fffe is a unicode non-character
-        values.put(SimRecords.NAME, UNSUPPORTED_NAME);
-        values.put(SimRecords.PHONE_NUMBER, "18005550102");
-
-        e = assertThrows(IllegalArgumentException.class,
-                () -> mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1),
-                        values,
-                        null));
-        assertThat(e).hasMessageThat().isEqualTo(
-                SimRecords.NAME + " contains unsupported characters.");
-
-        // The updates didn't actually change the data.
+        // The update didn't actually change the data.
         assertThat(mIccPhoneBook.getAllValidRecords())
                 .comparingElementsUsing(Correspondence.from(AdnRecord::isEqual, "isEqual"))
                 .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, "Initial", "8005550101"));
@@ -1179,76 +1182,26 @@
     }
 
     @Test
-    public void validateName_validName_returnsValueIsCorrect() {
-        setupSimsWithSubscriptionIds(1);
-        String validName = "First Last";
-        // See AdnRecord#FOOTER_SIZE_BYTES
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 10, validName.length() + 14);
-        SimRecords.NameValidationResult validationResult = SimRecords.validateName(mResolver, 1,
-                EF_ADN, validName);
+    public void getEncodedNameLength_returnsValueIsCorrect() {
+        String name = "";
+        int length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(0);
 
-        assertThat(validationResult.isValid()).isTrue();
-        assertThat(validationResult.getName()).isEqualTo(validName);
-        assertThat(validationResult.getSanitizedName()).isEqualTo(validName);
-        assertThat(validationResult.getEncodedLength()).isEqualTo(validName.length());
-        assertThat(validationResult.getMaxEncodedLength()).isEqualTo(validName.length());
+        name = "First Last";
+        length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(name.length());
 
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 10, 40);
-        validationResult = SimRecords.validateName(mResolver, 1, EF_ADN, validName);
-        assertThat(validationResult.getMaxEncodedLength()).isEqualTo(40 - 14);
-    }
+        name = "日本";
+        length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(name.length() * 2 + 1);
 
-    @Test
-    public void validateName_nameTooLong_returnsValueIsCorrect() {
-        setupSimsWithSubscriptionIds(1);
-        String tooLongName = "First Last";
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 10, tooLongName.length() + 14 - 1);
-        SimRecords.NameValidationResult validationResult = SimRecords.validateName(mResolver, 1,
-                EF_ADN, tooLongName);
+        name = EMOJI;
+        length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(name.length() * 2 + 1);
 
-        assertThat(validationResult.isValid()).isFalse();
-        assertThat(validationResult.getName()).isEqualTo(tooLongName);
-        assertThat(validationResult.getSanitizedName()).isEqualTo(tooLongName);
-        assertThat(validationResult.getEncodedLength()).isEqualTo(tooLongName.length());
-        assertThat(validationResult.getMaxEncodedLength()).isEqualTo(tooLongName.length() - 1);
-    }
-
-    @Test
-    public void validateName_nameWithUnsupportedCharacters_returnsValueIsCorrect() {
-        setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 10, 40);
-        SimRecords.NameValidationResult validationResult = SimRecords.validateName(mResolver, 1,
-                EF_ADN, UNSUPPORTED_NAME);
-
-        assertThat(validationResult.isValid()).isFalse();
-        assertThat(validationResult.getName()).isEqualTo(UNSUPPORTED_NAME);
-        assertThat(validationResult.getSanitizedName()).isEqualTo(":)=  ;ni= ;hon= ;");
-        assertThat(validationResult.getEncodedLength()).isEqualTo(UNSUPPORTED_NAME.length());
-        assertThat(validationResult.getMaxEncodedLength()).isEqualTo(
-                AdnRecord.getMaxAlphaTagBytes(40));
-    }
-
-    @Test
-    public void validateName_emptyString_returnsValueIsCorrect() {
-        setupSimsWithSubscriptionIds(1);
-        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 10, 40);
-        SimRecords.NameValidationResult validationResult = SimRecords.validateName(mResolver, 1,
-                EF_ADN, "");
-
-        assertThat(validationResult.isValid()).isTrue();
-        assertThat(validationResult.getName()).isEqualTo("");
-        assertThat(validationResult.getSanitizedName()).isEqualTo("");
-        assertThat(validationResult.getEncodedLength()).isEqualTo(0);
-        assertThat(validationResult.getMaxEncodedLength()).isEqualTo(
-                AdnRecord.getMaxAlphaTagBytes(40));
-
-        // Null is equivalent to empty
-        validationResult = SimRecords.validateName(mResolver, 1, EF_ADN, null);
-        assertThat(validationResult.getName()).isEqualTo("");
-        assertThat(validationResult.getSanitizedName()).isEqualTo("");
-        assertThat(validationResult.getEncodedLength()).isEqualTo(0);
-        assertThat(validationResult.getMaxEncodedLength()).isEqualTo(
-                AdnRecord.getMaxAlphaTagBytes(40));
+        name = "abc日本" + EMOJI;
+        length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(name.length() * 2 + 1);
     }
 
     private void setupSimsWithSubscriptionIds(int... subscriptionIds) {