Merge "Adds ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G to PhoneInterfaceManager." into sc-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index a7137d9..25a4909 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -9,6 +9,8 @@
 
     key: "com.android.telephony.key",
     certificate: ":com.android.telephony.certificate",
+
+    updatable: false,
 }
 
 apex {
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 37a0618..831b537 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -278,6 +278,9 @@
                                     obtainMessage(EVENT_BIND_DEFAULT_TIMEOUT, phoneId, -1),
                                     BIND_TIMEOUT_MILLIS);
                         } else {
+                            // Put a stub bundle in place so that the rest of the logic continues
+                            // smoothly.
+                            mConfigFromDefaultApp[phoneId] = new PersistableBundle();
                             // Send broadcast if bind fails.
                             notifySubscriptionInfoUpdater(phoneId);
                             // TODO: We *must* call unbindService even if bindService returns false.
@@ -359,6 +362,8 @@
                         unbindIfBound(mContext, mServiceConnection[phoneId], phoneId);
                         broadcastConfigChangedIntent(phoneId);
                     }
+                    // Put a stub bundle in place so that the rest of the logic continues smoothly.
+                    mConfigFromDefaultApp[phoneId] = new PersistableBundle();
                     notifySubscriptionInfoUpdater(phoneId);
                     break;
                 }
@@ -402,6 +407,9 @@
                                     obtainMessage(EVENT_BIND_CARRIER_TIMEOUT, phoneId, -1),
                                     BIND_TIMEOUT_MILLIS);
                         } else {
+                            // Put a stub bundle in place so that the rest of the logic continues
+                            // smoothly.
+                            mConfigFromCarrierApp[phoneId] = new PersistableBundle();
                             // Send broadcast if bind fails.
                             broadcastConfigChangedIntent(phoneId);
                             loge("Bind to carrier app: " + carrierPackageName + " fails");
@@ -471,7 +479,8 @@
 
                 case EVENT_BIND_CARRIER_TIMEOUT:
                 case EVENT_FETCH_CARRIER_TIMEOUT: {
-                    loge("Bind/fetch from carrier app timeout");
+                    loge("Bind/fetch from carrier app timeout, package="
+                            + getCarrierPackageForPhoneId(phoneId));
                     removeMessages(EVENT_FETCH_CARRIER_TIMEOUT);
                     // If we attempted to bind to the app, but the service connection is null due to
                     // the race condition that clear config event happens before bind/fetch complete
@@ -482,6 +491,8 @@
                         unbindIfBound(mContext, mServiceConnection[phoneId], phoneId);
                         broadcastConfigChangedIntent(phoneId);
                     }
+                    // Put a stub bundle in place so that the rest of the logic continues smoothly.
+                    mConfigFromCarrierApp[phoneId] = new PersistableBundle();
                     notifySubscriptionInfoUpdater(phoneId);
                     break;
                 }
@@ -848,6 +859,7 @@
     }
 
     /** Returns the package name of a priveleged carrier app, or null if there is none. */
+    @Nullable
     private String getCarrierPackageForPhoneId(int phoneId) {
         List<String> carrierPackageNames = TelephonyManager.from(mContext)
                 .getCarrierPackageNamesForIntentAndPhone(
@@ -1167,25 +1179,27 @@
             PersistableBundle config = mConfigFromDefaultApp[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
-                if (getCarrierPackageForPhoneId(phoneId) == null) {
-                    retConfig.putBoolean(
-                            CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
-                }
             }
             config = mConfigFromCarrierApp[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
-                retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
             }
             config = mPersistentOverrideConfigs[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
-                retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
             }
             config = mOverrideConfigs[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
             }
+            // Ignore the theoretical case of the default app not being present since that won't
+            // work in CarrierConfigLoader today.
+            final boolean allConfigsApplied =
+                    (mConfigFromCarrierApp[phoneId] != null
+                        || getCarrierPackageForPhoneId(phoneId) == null)
+                    && mConfigFromDefaultApp[phoneId] != null;
+            retConfig.putBoolean(
+                    CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, allConfigsApplied);
         } else {
             if (mNoSimConfig != null) {
                 retConfig.putAll(mNoSimConfig);
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 9334078..701a759 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -16,6 +16,7 @@
 
 package com.android.phone;
 
+import android.app.ActivityManager;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Binder;
@@ -270,7 +271,11 @@
     @Override
     public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
             List<Uri> contactNumbers, IRcsUceControllerCallback c) {
-        enforceReadPrivilegedPermission("requestCapabilities");
+        enforceAccessUserCapabilityExchangePermission("requestCapabilities");
+        enforceReadContactsPermission("requestCapabilities");
+        if (!isCallingProcessInForeground(Binder.getCallingUid())) {
+            throw new SecurityException("The caller is not in the foreground.");
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -290,7 +295,11 @@
     @Override
     public void requestAvailability(int subId, String callingPackage,
             String callingFeatureId, Uri contactNumber, IRcsUceControllerCallback c) {
-        enforceReadPrivilegedPermission("requestAvailability");
+        enforceAccessUserCapabilityExchangePermission("requestAvailability");
+        enforceReadContactsPermission("requestAvailability");
+        if (!isCallingProcessInForeground(Binder.getCallingUid())) {
+            throw new SecurityException("The caller is not in the foreground.");
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -548,6 +557,39 @@
     }
 
     /**
+     * Make sure the caller has the ACCESS_RCS_USER_CAPABILITY_EXCHANGE permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission.
+     */
+    private void enforceAccessUserCapabilityExchangePermission(String message) {
+        mApp.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, message);
+    }
+
+    /**
+     * Make sure the caller has the READ_CONTACTS permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission.
+     */
+    private void enforceReadContactsPermission(String message) {
+        mApp.enforceCallingOrSelfPermission(
+                android.Manifest.permission.READ_CONTACTS, message);
+    }
+
+    /**
+     * Check if the calling process is in the foreground.
+     *
+     * @return true if the caller is in the foreground.
+     */
+    private boolean isCallingProcessInForeground(int uid) {
+        ActivityManager am = mApp.getSystemService(ActivityManager.class);
+        boolean isCallingProcessForeground = am != null
+                && am.getUidImportance(uid)
+                        == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+        return isCallingProcessForeground;
+    }
+
+    /**
      * Retrieve ImsPhone instance.
      *
      * @param subId the subscription ID
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/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 1e97a6c..7a59908 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -31,6 +31,7 @@
 import android.os.PersistableBundle;
 import android.telecom.BluetoothCallQualityReport;
 import android.telecom.CallAudioState;
+import android.telecom.CallScreeningService;
 import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.ConnectionService;
@@ -1138,12 +1139,24 @@
     }
 
     @Override
-    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) {
+    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
+            CallScreeningService.CallResponse callScreeningResponse,
+            boolean isResponseFromSystemDialer) {
+        // Check what the call screening service has to say, if it's a system dialer.
+        boolean isAllowedToDisplayPicture;
+        if (isResponseFromSystemDialer && callScreeningResponse != null
+                && callScreeningResponse.getCallComposerAttachmentsToShow() >= 0) {
+            isAllowedToDisplayPicture = (callScreeningResponse.getCallComposerAttachmentsToShow()
+                    & CallScreeningService.CallResponse.CALL_COMPOSER_ATTACHMENT_PICTURE) != 0;
+        } else {
+            isAllowedToDisplayPicture = isInContacts;
+        }
+
         if (isImsConnection()) {
             ImsPhone imsPhone = (getPhone() instanceof ImsPhone) ? (ImsPhone) getPhone() : null;
             if (imsPhone != null
                     && imsPhone.getCallComposerStatus() == TelephonyManager.CALL_COMPOSER_STATUS_ON
-                    && !isBlocked && isInContacts) {
+                    && !isBlocked && isAllowedToDisplayPicture) {
                 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
                 ImsCallProfile profile = originalConnection.getImsCall().getCallProfile();
                 String serverUrl = CallComposerPictureManager.sTestMode
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
index 2ff1df0..6e52949 100644
--- a/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -19,8 +19,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.google.android.sample.rcsclient"
-    android:versionCode="2"
-    android:versionName="1.0.1">
+    android:versionCode="3"
+    android:versionName="1.0.2_UP1.0">
 
     <uses-sdk
         android:minSdkVersion="30"
diff --git a/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml
index 374db9b..5dbf6b0 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml
@@ -21,8 +21,9 @@
             android:id="@+id/destNum"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:inputType="number"
-            android:text="16504483120" />
+            android:inputType="phone"
+            android:digits="0123456789+"
+            android:hint="+15555551212" />
     </LinearLayout>
 
 
diff --git a/testapps/TestRcsApp/TestApp/res/layout/number_to_chat.xml b/testapps/TestRcsApp/TestApp/res/layout/number_to_chat.xml
index 5d71cd1..0390d51 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/number_to_chat.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/number_to_chat.xml
@@ -20,8 +20,9 @@
             android:id="@+id/destNum"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:inputType="number"
-            android:text="16504396583" />
+            android:inputType="phone"
+            android:digits="0123456789+"
+            android:hint="+15555551212" />
     </LinearLayout>
 
     <Button
diff --git a/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml
index 0174d71..305c88e 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml
@@ -34,8 +34,9 @@
                 android:id="@+id/number_list"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:inputType="number"
-                android:text="16504483123, 16504489023" />
+                android:inputType="phone"
+                android:digits="0123456789+,"
+                android:hint="+16505551212,+16505551213" />
         </LinearLayout>
 
         <Button
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
index 1619f14..1db4af7 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
@@ -40,6 +40,7 @@
 
 import com.google.android.sample.rcsclient.util.ChatManager;
 import com.google.android.sample.rcsclient.util.ChatProvider;
+import com.google.android.sample.rcsclient.util.NumberUtils;
 
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -58,6 +59,7 @@
     private boolean mSessionInitResult = false;
     private Button mSend;
     private String mDestNumber;
+    private TextView mDestNumberView;
     private EditText mNewMessage;
     private ChatObserver mChatObserver;
     private Handler mHandler;
@@ -89,6 +91,7 @@
 
             }
         };
+        mDestNumberView = findViewById(R.id.destNum);
         initDestNumber();
         mChatObserver = new ChatObserver(mHandler);
     }
@@ -96,8 +99,7 @@
     private void initDestNumber() {
         Intent intent = getIntent();
         mDestNumber = intent.getStringExtra(EXTRA_REMOTE_PHONE_NUMBER);
-        TextView destNumber = findViewById(R.id.destNum);
-        destNumber.setText(mDestNumber);
+        mDestNumberView.setText(mDestNumber);
     }
 
     @Override
@@ -119,7 +121,12 @@
             return;
         }
         try {
-
+            // Reformat so that the number matches the one sent to the network.
+            String formattedNumber = NumberUtils.formatNumber(this, mDestNumber);
+            if (formattedNumber != null) {
+                mDestNumber = formattedNumber;
+            }
+            mDestNumberView.setText(mDestNumber);
             ChatManager.getInstance(getApplicationContext(), subId).initChatSession(
                     TELURI_PREFIX + mDestNumber, new SessionStateCallback() {
                         @Override
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/PhoneNumberActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/PhoneNumberActivity.java
index a277994..b432979 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/PhoneNumberActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/PhoneNumberActivity.java
@@ -22,10 +22,13 @@
 import android.view.MenuItem;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.Toast;
 
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
 
+import com.google.android.sample.rcsclient.util.NumberUtils;
+
 /** An activity to let user input phone number to chat. */
 public class PhoneNumberActivity extends AppCompatActivity {
 
@@ -43,11 +46,16 @@
         mChatButton = this.findViewById(R.id.launch_chat_btn);
         mPhoneNumber = findViewById(R.id.destNum);
         mChatButton.setOnClickListener(view -> {
-            Intent intent = new Intent(PhoneNumberActivity.this, ChatActivity.class);
-            intent.putExtra(ChatActivity.EXTRA_REMOTE_PHONE_NUMBER,
+            String formattedNumber = NumberUtils.formatNumber(PhoneNumberActivity.this,
                     mPhoneNumber.getText().toString());
-            PhoneNumberActivity.this.startActivity(intent);
-
+            if (formattedNumber != null) {
+                Intent intent = new Intent(PhoneNumberActivity.this, ChatActivity.class);
+                intent.putExtra(ChatActivity.EXTRA_REMOTE_PHONE_NUMBER, formattedNumber);
+                PhoneNumberActivity.this.startActivity(intent);
+            } else {
+                Toast.makeText(this, "Invalid Number format!",
+                        Toast.LENGTH_LONG).show();
+            }
         });
     }
 
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java
index da0cf39..aa90487 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java
@@ -88,7 +88,7 @@
     private static RcsClientConfiguration getDefaultClientConfiguration() {
         return new RcsClientConfiguration(
                 /*rcsVersion=*/ "6.0",
-                /*rcsProfile=*/ "UP_2.3",
+                /*rcsProfile=*/ "UP_1.0",
                 /*clientVendor=*/ "Goog",
                 /*clientVersion=*/ "RCSAndrd-1.0");
     }
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
index 9edb817..10f588c 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
@@ -35,16 +35,15 @@
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
 
+import com.google.android.sample.rcsclient.util.NumberUtils;
+
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 
 /** An activity to verify UCE. */
 public class UceActivity extends AppCompatActivity {
 
     private static final String TAG = "TestRcsApp.UceActivity";
-    private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
     private Button mCapabilityButton;
     private Button mAvailabilityButton;
     private TextView mCapabilityResult;
@@ -72,16 +71,16 @@
         mCapabilityResult = findViewById(R.id.capability_result);
         mAvailabilityResult = findViewById(R.id.capability_result);
 
-        List<Uri> contactList = getContectList();
         mImsRcsManager = getImsRcsManager(mDefaultSmsSubId);
         mCapabilityButton.setOnClickListener(view -> {
+            List<Uri> contactList = getContectList();
             if (contactList.size() == 0) {
                 Log.i(TAG, "empty contact list");
                 return;
             }
             mCapabilityResult.setText("pending...\n");
             try {
-                mImsRcsManager.getUceAdapter().requestCapabilities(contactList, mExecutorService,
+                mImsRcsManager.getUceAdapter().requestCapabilities(contactList, getMainExecutor(),
                         new RcsUceAdapter.CapabilitiesCallback() {
                             public void onCapabilitiesReceived(
                                     List<RcsContactUceCapability> contactCapabilities) {
@@ -113,6 +112,7 @@
         });
 
         mAvailabilityButton.setOnClickListener(view -> {
+            List<Uri> contactList = getContectList();
             if (contactList.size() == 0) {
                 Log.i(TAG, "empty contact list");
                 return;
@@ -120,7 +120,7 @@
             mAvailabilityResult.setText("pending...\n");
             try {
                 mImsRcsManager.getUceAdapter().requestAvailability(contactList.get(0),
-                        mExecutorService, new RcsUceAdapter.CapabilitiesCallback() {
+                        getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {
                             public void onCapabilitiesReceived(
                                     List<RcsContactUceCapability> contactCapabilities) {
                                 Log.i(TAG, "onCapabilitiesReceived()");
@@ -153,13 +153,18 @@
 
     private List<Uri> getContectList() {
         mNumbers = findViewById(R.id.number_list);
-        String []numbers;
+        String[] numbers;
         ArrayList<Uri> contactList = new ArrayList<>();
         if (!TextUtils.isEmpty(mNumbers.getText().toString())) {
             String numberList = mNumbers.getText().toString().trim();
             numbers = numberList.split(",");
             for (String number : numbers) {
-                contactList.add(Uri.parse(ChatActivity.TELURI_PREFIX + number));
+                String formattedNumber = NumberUtils.formatNumber(this, number);
+                if (formattedNumber != null) {
+                    contactList.add(Uri.parse(ChatActivity.TELURI_PREFIX + formattedNumber));
+                } else {
+                    Log.w(TAG, "number formatted improperly, skipping: " + number);
+                }
             }
         }
 
@@ -206,6 +211,10 @@
                 if (t.getServiceCapabilities() != null) {
                     RcsContactPresenceTuple.ServiceCapabilities servCaps =
                             t.getServiceCapabilities();
+                    b.append(", servCaps=(audio=");
+                    b.append(servCaps.isAudioCapable());
+                    b.append(", video=");
+                    b.append(servCaps.isVideoCapable());
                     b.append(", servCaps=(supported=");
                     b.append(servCaps.getSupportedDuplexModes());
                     b.append("), servCaps=(unsupported=");
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java
new file mode 100644
index 0000000..72cbf3f
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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.google.android.sample.rcsclient.util;
+
+import android.content.Context;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+
+public class NumberUtils {
+
+    /**
+     * Format a number in E164 format.
+     * <p>
+     * Note: if the number can not be formatted, this method will return null.
+     */
+    public static String formatNumber(Context context, String number) {
+        TelephonyManager manager = context.getSystemService(TelephonyManager.class);
+        String simCountryIso = manager.getSimCountryIso().toUpperCase();
+        return PhoneNumberUtils.formatNumberToE164(number, simCountryIso);
+    }
+}
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) {