Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-dev
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index c41bf1a..1e97a6c 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Icon;
@@ -65,8 +66,11 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.d2d.Communicator;
+import com.android.internal.telephony.d2d.DtmfAdapter;
+import com.android.internal.telephony.d2d.DtmfTransport;
 import com.android.internal.telephony.d2d.RtpAdapter;
 import com.android.internal.telephony.d2d.RtpTransport;
+import com.android.internal.telephony.d2d.Timeouts;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
@@ -89,6 +93,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
 
 /**
  * Base class for CDMA and GSM connections.
@@ -846,6 +851,8 @@
 
     private RtpTransport mRtpTransport;
 
+    private DtmfTransport mDtmfTransport;
+
     /**
      * Facilitates device to device communication.
      */
@@ -3297,7 +3304,20 @@
             }
         };
         mRtpTransport = new RtpTransport(rtpAdapter, null /* TODO: not needed yet */, mHandler);
-        mCommunicator = new Communicator(List.of(mRtpTransport), this);
+
+        DtmfAdapter dtmfAdapter = digit -> {
+            if (!isImsConnection()) {
+                Log.w(TelephonyConnection.this, "sendDtmf: not an ims conn.");
+            }
+            Log.d(TelephonyConnection.this, "sendDtmf: send digit %c", digit);
+            ImsPhoneConnection originalConnection =
+                    (ImsPhoneConnection) mOriginalConnection;
+            originalConnection.getImsCall().sendDtmf(digit, null /* result msg not needed */);
+        };
+        ContentResolver cr = getPhone().getContext().getContentResolver();
+        mDtmfTransport = new DtmfTransport(dtmfAdapter, new Timeouts.Adapter(cr),
+                Executors.newSingleThreadScheduledExecutor());
+        mCommunicator = new Communicator(List.of(mRtpTransport, mDtmfTransport), this);
         mD2DCallStateAdapter = new D2DCallStateAdapter(mCommunicator);
         addTelephonyConnectionListener(mD2DCallStateAdapter);
     }
diff --git a/testapps/SmsManagerTestApp/Android.bp b/testapps/SmsManagerTestApp/Android.bp
index 5333eab..0f52d1c 100644
--- a/testapps/SmsManagerTestApp/Android.bp
+++ b/testapps/SmsManagerTestApp/Android.bp
@@ -1,5 +1,7 @@
 android_app {
     name: "SmsManagerTestApp",
     srcs: ["src/**/*.java"],
-    sdk_version: "current",
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
 }
diff --git a/testapps/SmsManagerTestApp/AndroidManifest.xml b/testapps/SmsManagerTestApp/AndroidManifest.xml
index 7dc717f..1be2b2d 100644
--- a/testapps/SmsManagerTestApp/AndroidManifest.xml
+++ b/testapps/SmsManagerTestApp/AndroidManifest.xml
@@ -21,6 +21,7 @@
          android:targetSdkVersion="29"/>
     <uses-permission android:name="android.permission.SEND_SMS"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION"/>
     <application android:label="SmsManagerTestApp">
         <activity android:name=".SmsManagerTestApp"
              android:label="SmsManagerTestApp"
@@ -54,6 +55,7 @@
 
         <!-- Fake BroadcastReceiver that listens for incoming SMS messages -->
         <receiver android:name=".SmsReceiver"
+                  android:exported="true"
                   android:permission="android.permission.BROADCAST_SMS">
             <intent-filter>
                 <action android:name="android.provider.Telephony.SMS_DELIVER" />
@@ -62,7 +64,8 @@
 
         <!-- Fake BroadcastReceiver that listens for incoming MMS messages -->
         <receiver android:name=".MmsReceiver"
-                  android:permission="android.permission.BROADCAST_WAP_PUSH">
+                  android:permission="android.permission.BROADCAST_WAP_PUSH"
+                  android:exported="true">
             <intent-filter>
                 <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                 <data android:mimeType="application/vnd.wap.mms-message" />
@@ -70,7 +73,7 @@
         </receiver>
 
         <!-- Fake Activity that allows the user to send new SMS/MMS messages -->
-        <activity android:name=".ComposeSmsActivity" >
+        <activity android:name=".ComposeSmsActivity" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.SEND" />
                 <action android:name="android.intent.action.SENDTO" />
diff --git a/testapps/SmsManagerTestApp/res/layout/activity_main.xml b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
index 185e0e2..ddaf3a6 100644
--- a/testapps/SmsManagerTestApp/res/layout/activity_main.xml
+++ b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
@@ -75,5 +75,11 @@
             android:layout_height="wrap_content"
             android:paddingRight="4dp"
             android:text="@string/disable_persistent_service"/>
+        <Button
+            android:id="@+id/check_single_reg_permission"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/check_single_reg_permission"/>
     </LinearLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
index c8b9f91..ca07c54 100644
--- a/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
+++ b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
@@ -21,4 +21,5 @@
     <string name="get_sub_for_result_button">Ask user for sub id.</string>
     <string name="enable_persistent_service">Enable Persistent Service</string>
     <string name="disable_persistent_service">Disable Persistent Service</string>
+    <string name="check_single_reg_permission">Check RCS Single Reg Perm</string>
 </resources>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
index 785fe3f..cc3769e 100644
--- a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
@@ -44,6 +44,10 @@
     private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName(
             "com.android.settings", "com.android.settings.sim.SimDialogActivity");
 
+    // Can't import PERFORM_IMS_SINGLE_REGISTRATION const directly beause it's a @SystemApi
+    private static final String PERFORM_IMS_SINGLE_REGISTRATION =
+            "android.permission.PERFORM_IMS_SINGLE_REGISTRATION";
+
     /*
      * Forwarded constants from SimDialogActivity.
      */
@@ -70,6 +74,8 @@
                 .setOnClickListener(this::setPersistentServiceComponentEnabled);
         findViewById(R.id.disable_persistent_service)
                 .setOnClickListener(this::setPersistentServiceComponentDisabled);
+        findViewById(R.id.check_single_reg_permission)
+                .setOnClickListener(this::checkSingleRegPermission);
         mPhoneNumber = (EditText) findViewById(R.id.phone_number_text);
     }
 
@@ -201,6 +207,17 @@
                 PackageManager.DONT_KILL_APP);
     }
 
+    private void checkSingleRegPermission(View view) {
+        if (checkSelfPermission(PERFORM_IMS_SINGLE_REGISTRATION)
+                == PackageManager.PERMISSION_GRANTED) {
+            Toast.makeText(this, "Single Reg permission granted",
+                    Toast.LENGTH_SHORT).show();
+        } else {
+            Toast.makeText(this, "Single Reg permission NOT granted",
+                    Toast.LENGTH_SHORT).show();
+        }
+
+    }
 
     private Intent getSendStatusIntent() {
         // Encode requestId in intent data
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
index 9801faf..2ff1df0 100644
--- a/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -18,7 +18,9 @@
 */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.google.android.sample.rcsclient">
+    package="com.google.android.sample.rcsclient"
+    android:versionCode="2"
+    android:versionName="1.0.1">
 
     <uses-sdk
         android:minSdkVersion="30"
diff --git a/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml b/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
index c406972..db7ea33 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
@@ -49,6 +49,14 @@
             android:text="@string/test_msg_client"
             android:textAlignment="center"
             android:textAllCaps="false" />
+
+        <TextView
+            android:id="@+id/version_info"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/version_info"
+            android:textAlignment="center"
+            android:paddingTop="7dp"/>
     </LinearLayout>
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/values/strings.xml b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
similarity index 98%
rename from testapps/TestRcsApp/TestApp/res/values/strings.xml
rename to testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
index 2369449..3528add 100644
--- a/testapps/TestRcsApp/TestApp/res/values/strings.xml
+++ b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
@@ -41,6 +41,7 @@
     <string name="chatbot_version">Chatbot Version</string>
     <string name="provisioning_done">Provisioning Done</string>
     <string name="registration_done">Registration Done</string>
+    <string name="version_info">Version: %s</string>
 
     <string-array name="organization">
         <item>NONE</item>
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
index 04fdb5b..62302fe 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
@@ -18,9 +18,14 @@
 package com.google.android.sample.rcsclient;
 
 import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
 import android.view.MenuItem;
 import android.widget.Button;
+import android.widget.TextView;
 
 import androidx.appcompat.app.AppCompatActivity;
 
@@ -32,6 +37,7 @@
     private Button mUceButton;
     private Button mGbaButton;
     private Button mMessageClientButton;
+    private TextView mVersionInfo;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -47,6 +53,7 @@
         mMessageClientButton = (Button) this.findViewById(R.id.msgClient);
         mUceButton = (Button) this.findViewById(R.id.uce);
         mGbaButton = (Button) this.findViewById(R.id.gba);
+        mVersionInfo = this.findViewById(R.id.version_info);
         mProvisionButton.setOnClickListener(view -> {
             Intent intent = new Intent(this, ProvisioningActivity.class);
             MainActivity.this.startActivity(intent);
@@ -70,6 +77,13 @@
             Intent intent = new Intent(this, ContactListActivity.class);
             MainActivity.this.startActivity(intent);
         });
+
+        String appVersionName = getVersionCode(getPackageName());
+        if (!TextUtils.isEmpty(appVersionName)) {
+            String version = String.format(getResources().getString(R.string.version_info),
+                    appVersionName);
+            mVersionInfo.setText(version);
+        }
     }
 
     @Override
@@ -79,5 +93,15 @@
         }
         return super.onOptionsItemSelected(item);
     }
-}
 
+    private String getVersionCode(String packageName) {
+        try {
+            // get android:versionName from the android manifest
+            PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0 /*flags*/);
+            return info.versionName;
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "couldn't get version info for package name:" + packageName);
+        }
+        return null;
+    }
+}
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 04efb6e..9edb817 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
@@ -19,8 +19,12 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.telephony.SmsManager;
+import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsRcsManager;
+import android.telephony.ims.RcsContactPresenceTuple;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.MenuItem;
@@ -36,8 +40,6 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-//import android.telephony.ims.RcsUceAdapter.CapabilitiesCallback;
-
 /** An activity to verify UCE. */
 public class UceActivity extends AppCompatActivity {
 
@@ -72,62 +74,81 @@
 
         List<Uri> contactList = getContectList();
         mImsRcsManager = getImsRcsManager(mDefaultSmsSubId);
-//        mCapabilityButton.setOnClickListener(view -> {
-//            if(contactList.size() == 0) {
-//                Log.i(TAG, "empty contact list");
-//                return;
-//            }
-//            mImsRcsManager.getUceAdapter().requestCapabilities(mExecutorService, contactList,
-//                    new CapabilitiesCallback() {
-//                        public void onCapabilitiesReceived(
-//                                @NonNull List<RcsContactUceCapability> contactCapabilities) {
-//                            Log.i(TAG, "onCapabilitiesReceived()");
-//                            mCapabilityResult.setText("onCapabilitiesReceived()");
-//                        }
-//
-//                        public void onComplete() {
-//                            Log.i(TAG, "onComplete()");
-//                            mCapabilityResult.setText("onComplete()");
-//
-//                        }
-//
-//                        public void onError(int errorCode, long retryAfterMilliseconds) {
-//                            Log.i(TAG, "onError() errorCode:" + errorCode + " retryAfterMs:"
-//                                    + retryAfterMilliseconds);
-//                            mCapabilityResult.setText("onError() errorCode:" + errorCode
-//                                    + " retryAfterMs:" + retryAfterMilliseconds);
-//                        }
-//                    });
-//        });
-//
-//        mAvailabilityButton.setOnClickListener(view -> {
-//            if(contactList.size() == 0) {
-//                Log.i(TAG, "empty contact list");
-//                return;
-//            }
-//            mImsRcsManager.getUceAdapter().requestNetworkAvailability(mExecutorService,
-//            contactList,
-//                    new CapabilitiesCallback() {
-//                        public void onCapabilitiesReceived(
-//                                @NonNull List<RcsContactUceCapability> contactCapabilities) {
-//                            Log.i(TAG, "onCapabilitiesReceived()");
-//                            mAvailabilityResult.setText("onCapabilitiesReceived()");
-//                        }
-//
-//                        public void onComplete() {
-//                            Log.i(TAG, "onComplete()");
-//                            mAvailabilityResult.setText("onComplete()");
-//
-//                        }
-//
-//                        public void onError(int errorCode, long retryAfterMilliseconds) {
-//                            Log.i(TAG, "onError() errorCode:" + errorCode + " retryAfterMs:"
-//                                    + retryAfterMilliseconds);
-//                            mAvailabilityResult.setText("onError() errorCode:" + errorCode
-//                                    + " retryAfterMs:" + retryAfterMilliseconds);
-//                        }
-//                    });
-//        });
+        mCapabilityButton.setOnClickListener(view -> {
+            if (contactList.size() == 0) {
+                Log.i(TAG, "empty contact list");
+                return;
+            }
+            mCapabilityResult.setText("pending...\n");
+            try {
+                mImsRcsManager.getUceAdapter().requestCapabilities(contactList, mExecutorService,
+                        new RcsUceAdapter.CapabilitiesCallback() {
+                            public void onCapabilitiesReceived(
+                                    List<RcsContactUceCapability> contactCapabilities) {
+                                Log.i(TAG, "onCapabilitiesReceived()");
+                                StringBuilder b = new StringBuilder("onCapabilitiesReceived:\n");
+                                for (RcsContactUceCapability c : contactCapabilities) {
+                                    b.append(getReadableCapability(c));
+                                    b.append("\n");
+                                }
+                                mCapabilityResult.append(b.toString() + "\n");
+                            }
+
+                            public void onComplete() {
+                                Log.i(TAG, "onComplete()");
+                                mCapabilityResult.append("complete");
+
+                            }
+
+                            public void onError(int errorCode, long retryAfterMilliseconds) {
+                                Log.i(TAG, "onError() errorCode:" + errorCode + " retryAfterMs:"
+                                        + retryAfterMilliseconds);
+                                mCapabilityResult.append("error - errorCode:" + errorCode
+                                        + " retryAfterMs:" + retryAfterMilliseconds);
+                            }
+                        });
+            } catch (ImsException e) {
+                mCapabilityResult.setText("ImsException:" + e);
+            }
+        });
+
+        mAvailabilityButton.setOnClickListener(view -> {
+            if (contactList.size() == 0) {
+                Log.i(TAG, "empty contact list");
+                return;
+            }
+            mAvailabilityResult.setText("pending...\n");
+            try {
+                mImsRcsManager.getUceAdapter().requestAvailability(contactList.get(0),
+                        mExecutorService, new RcsUceAdapter.CapabilitiesCallback() {
+                            public void onCapabilitiesReceived(
+                                    List<RcsContactUceCapability> contactCapabilities) {
+                                Log.i(TAG, "onCapabilitiesReceived()");
+                                StringBuilder b = new StringBuilder("onCapabilitiesReceived:\n");
+                                for (RcsContactUceCapability c : contactCapabilities) {
+                                    b.append(getReadableCapability(c));
+                                    b.append("\n");
+                                }
+                                mAvailabilityResult.append(b.toString() + "\n");
+                            }
+
+                            public void onComplete() {
+                                Log.i(TAG, "onComplete()");
+                                mAvailabilityResult.append("complete");
+
+                            }
+
+                            public void onError(int errorCode, long retryAfterMilliseconds) {
+                                Log.i(TAG, "onError() errorCode:" + errorCode + " retryAfterMs:"
+                                        + retryAfterMilliseconds);
+                                mAvailabilityResult.append("error - errorCode:" + errorCode
+                                        + " retryAfterMs:" + retryAfterMilliseconds);
+                            }
+                        });
+            } catch (ImsException e) {
+                mAvailabilityResult.setText("ImsException:" + e);
+            }
+        });
     }
 
     private List<Uri> getContectList() {
@@ -165,4 +186,36 @@
         }
         return super.onOptionsItemSelected(item);
     }
+
+    private String getReadableCapability(RcsContactUceCapability c) {
+        StringBuilder b = new StringBuilder("RcsContactUceCapability: uri=");
+        b.append(c.getContactUri());
+        b.append(", requestResult=");
+        b.append(c.getRequestResult());
+        b.append(", sourceType=");
+        b.append(c.getSourceType());
+        if (c.getCapabilityMechanism() == RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE) {
+            b.append(", tuples={");
+            for (RcsContactPresenceTuple t : c.getCapabilityTuples()) {
+                b.append("[uri=");
+                b.append(t.getContactUri());
+                b.append(", serviceId=");
+                b.append(t.getServiceId());
+                b.append(", serviceVersion=");
+                b.append(t.getServiceVersion());
+                if (t.getServiceCapabilities() != null) {
+                    RcsContactPresenceTuple.ServiceCapabilities servCaps =
+                            t.getServiceCapabilities();
+                    b.append(", servCaps=(supported=");
+                    b.append(servCaps.getSupportedDuplexModes());
+                    b.append("), servCaps=(unsupported=");
+                    b.append(servCaps.getUnsupportedDuplexModes());
+                    b.append("))");
+                }
+                b.append("]");
+            }
+            b.append("}");
+        }
+        return b.toString();
+    }
 }