Trampoline SmsManager SimDialogActivity intent

Start the SimDialogActivity using a transparent
"trampoline" activity in order to process the
activity's result in startActivityForResult.

1) Create PickSmsSubscripionActivity, whose only
purpose is to start SimDialogActivity and process
the result. This is called by SmsManager to process
operations on MSIM devices, where the user has
chosen "ask every time" as their default SMS
subscription.

2) Create SmsManagerTestApp app, which sends an SMS
using a foreground activity as well as a background
service for testing.

Bug: 130853716
Test: run SmsManagerTestApp
Merged-In: If4fa7d512cba134ac4a40f3838cbf761a378514d
Change-Id: I44b2c352548e5b61f61250547bfd3261b5060e5a
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e2d9c4a..2bc7ccf 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -616,6 +616,14 @@
             android:name="com.android.internal.telephony.uicc.ShowInstallAppNotificationReceiver"
             android:exported="false"/>
 
+        <activity
+            android:name="com.android.phone.settings.PickSmsSubscriptionActivity"
+            android:exported="false"
+            android:excludeFromRecents="true"
+            android:launchMode="singleTop"
+            android:configChanges="orientation|screenSize|keyboardHidden"
+            android:theme="@style/Theme.Transparent"/>
+
         <service
             android:name="com.android.phone.vvm.RemoteVvmTaskManager"
             android:exported="false"/>
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 6c3ec89..b231f7b 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -121,6 +121,7 @@
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.DefaultPhoneNotifier;
 import com.android.internal.telephony.HalVersion;
+import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.IccCard;
@@ -140,6 +141,7 @@
 import com.android.internal.telephony.SmsApplication;
 import com.android.internal.telephony.SmsApplication.SmsApplicationData;
 import com.android.internal.telephony.SmsController;
+import com.android.internal.telephony.SmsPermissions;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.dataconnection.ApnSettingUtils;
@@ -157,6 +159,7 @@
 import com.android.internal.telephony.uicc.UiccSlot;
 import com.android.internal.telephony.util.VoicemailNotificationSettingsUtil;
 import com.android.internal.util.HexDump;
+import com.android.phone.settings.PickSmsSubscriptionActivity;
 import com.android.phone.vvm.PhoneAccountHandleConverter;
 import com.android.phone.vvm.RemoteVvmTaskManager;
 import com.android.phone.vvm.VisualVoicemailSettingsUtil;
@@ -7037,6 +7040,23 @@
     }
 
     @Override
+    public void enqueueSmsPickResult(String callingPackage, IIntegerConsumer pendingSubIdResult) {
+        SmsPermissions permissions = new SmsPermissions(getDefaultPhone(), mApp,
+                (AppOpsManager) mApp.getSystemService(Context.APP_OPS_SERVICE));
+        if (!permissions.checkCallingCanSendSms(callingPackage, "Sending message")) {
+            throw new SecurityException("Requires SEND_SMS permission to perform this operation");
+        }
+        PickSmsSubscriptionActivity.addPendingResult(pendingSubIdResult);
+        Intent intent = new Intent();
+        intent.setClass(mApp, PickSmsSubscriptionActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // Bring up choose default SMS subscription dialog right now
+        intent.putExtra(PickSmsSubscriptionActivity.DIALOG_TYPE_KEY,
+                PickSmsSubscriptionActivity.SMS_PICK_FOR_MESSAGE);
+        mApp.startActivity(intent);
+    }
+
+    @Override
     public String getMmsUAProfUrl(int subId) {
         //TODO investigate if this API should require proper permission check in R b/133791609
         final long identity = Binder.clearCallingIdentity();
diff --git a/src/com/android/phone/settings/PickSmsSubscriptionActivity.java b/src/com/android/phone/settings/PickSmsSubscriptionActivity.java
new file mode 100644
index 0000000..cfbce28
--- /dev/null
+++ b/src/com/android/phone/settings/PickSmsSubscriptionActivity.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2019 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.android.phone.settings;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.internal.telephony.IIntegerConsumer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Trampolines a request to Settings to get the SMS subscription associated with an SmsManager
+ * operation.
+ *
+ * Since a Service can not start an Activity with
+ * {@link Activity#startActivityForResult(Intent, int)} and get a response (only Activities can
+ * handle the results), we have to "Trampoline" this operation by creating an empty Activity whose
+ * only job is to call startActivityForResult with the correct Intent and handle the result.
+ */
+// TODO: SmsManager should be constructed with an activity context so it can start as part of its
+// task and fall back to PickSmsSubscriptionActivity being called in PhoneInterfaceManager if not
+// called from an activity context.
+public class PickSmsSubscriptionActivity extends Activity {
+
+    private static final String LOG_TAG = "PickSmsSubActivity";
+
+    // Defined in Settings SimDialogActivity
+    private static final String RESULT_SUB_ID = "result_sub_id";
+    public static final String DIALOG_TYPE_KEY = "dialog_type";
+    public static final int SMS_PICK_FOR_MESSAGE = 4;
+
+    private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName(
+            "com.android.settings", "com.android.settings.sim.SimDialogActivity");
+
+    private static final List<IIntegerConsumer> sSmsPickPendingList = new ArrayList<>();
+
+    private static final int REQUEST_GET_SMS_SUB_ID = 1;
+
+    /**
+     * Adds a consumer to the list of pending results that will be accepted once the activity
+     * completes.
+     */
+    public static void addPendingResult(IIntegerConsumer consumer) {
+        synchronized (sSmsPickPendingList) {
+            sSmsPickPendingList.add(consumer);
+        }
+        Log.i(LOG_TAG, "queue pending result, token: " + consumer);
+    }
+
+    private static void sendResultAndClear(int resultId) {
+        // If the calling process died, just ignore callback.
+        synchronized (sSmsPickPendingList) {
+            for (IIntegerConsumer c : sSmsPickPendingList) {
+                try {
+                    c.accept(resultId);
+                    Log.i(LOG_TAG, "Result received, token: " + c + ", result: " + resultId);
+                } catch (RemoteException e) {
+                    // The calling process died, skip this one.
+                }
+            }
+            sSmsPickPendingList.clear();
+        }
+    }
+
+    // Keep track if this activity has been stopped (i.e. user navigated away, power screen off,...)
+    // if so, treat it as the user navigating away and end the task if it is restarted without an
+    // onCreate/onNewIntent.
+    private boolean mPreviouslyStopped = false;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mPreviouslyStopped = false;
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        mPreviouslyStopped = false;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        // This is cause a little jank with the recents display, but there is no other way to handle
+        // the case where activity has stopped and we want to dismiss the dialog. We use the
+        // tag "excludeFromRecents", but in the cases where it is still shown, kill it in onResume.
+        if (mPreviouslyStopped) {
+            finishAndRemoveTask();
+        } else {
+            launchSmsPicker(new Intent(getIntent()));
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        // User navigated away from dialog, send invalid sub id result.
+        mPreviouslyStopped = true;
+        sendResultAndClear(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        // triggers cancelled result for onActivityResult
+        finishActivity(REQUEST_GET_SMS_SUB_ID);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_GET_SMS_SUB_ID) {
+            int result = data == null ? SubscriptionManager.INVALID_SUBSCRIPTION_ID :
+                    data.getIntExtra(RESULT_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            if (resultCode == Activity.RESULT_OK) {
+                sendResultAndClear(result);
+            } else {
+                sendResultAndClear(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            }
+        }
+        // This will be handled in onResume - we do not want to call this all the time here because
+        // we need to be able to restart if stopped and a new intent comes in via onNewIntent.
+        if (!mPreviouslyStopped) {
+            finishAndRemoveTask();
+        }
+    }
+
+    private void launchSmsPicker(Intent trampolineIntent) {
+        trampolineIntent.setComponent(SETTINGS_SUB_PICK_ACTIVITY);
+        // Remove this flag if it exists, we want the settings activity to be part of this task.
+        trampolineIntent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivityForResult(trampolineIntent, REQUEST_GET_SMS_SUB_ID);
+    }
+}
diff --git a/testapps/SmsManagerTestApp/Android.mk b/testapps/SmsManagerTestApp/Android.mk
new file mode 100644
index 0000000..307366b
--- /dev/null
+++ b/testapps/SmsManagerTestApp/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+src_dirs := src
+res_dirs := res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_PACKAGE_NAME := SmsManagerTestApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/testapps/SmsManagerTestApp/AndroidManifest.xml b/testapps/SmsManagerTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..c5f4621
--- /dev/null
+++ b/testapps/SmsManagerTestApp/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.phone.testapps.smsmanagertestapp">
+    <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="29" />
+    <uses-permission android:name="android.permission.SEND_SMS"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <application android:label="SmsManagerTestApp">
+        <activity
+            android:name=".SmsManagerTestApp"
+            android:label="SmsManagerTestApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <service android:name=".SmsManagerTestService" android:exported="false" />
+        <receiver android:name=".SendStatusReceiver"
+                  android:exported="false">
+            <intent-filter>
+                <action android:name="com.android.phone.testapps.smsmanagertestapp.message_sent_action" />
+                <data android:scheme="content" />
+            </intent-filter>
+        </receiver>
+
+    </application>
+</manifest>
+
diff --git a/testapps/SmsManagerTestApp/res/layout/activity_main.xml b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..39fb6c6
--- /dev/null
+++ b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:text="Outgoing SMS Phone Number"/>
+
+        <EditText
+            android:id="@+id/phone_number_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:inputType="phone" android:text="5555551212"/>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height= "1dp"
+            android:paddingRight="4dp"
+            android:background="?android:attr/listDivider" />
+
+        <Button
+            android:id="@+id/send_text_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/send_text_button"/>
+        <Button
+            android:id="@+id/send_text_button_service"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/send_text_service_button"/>
+        <Button
+            android:id="@+id/get_sub_for_result_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/get_sub_for_result_button"/>
+    </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
new file mode 100644
index 0000000..d6497a3
--- /dev/null
+++ b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+
+<resources>
+    <string name="send_text_button">Send Outgoing Text Now.</string>
+    <string name="send_text_service_button">Send Outgoing Text after 5 sec.</string>
+    <string name="get_sub_for_result_button">Ask user for sub id.</string>
+</resources>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java
new file mode 100644
index 0000000..03709d1
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SendStatusReceiver.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 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.android.phone.testapps.smsmanagertestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+
+/**
+ * Handles the PendingIntent result from SMS messages send to Telephony. Reports the results of
+ * those messages using Toasts.
+ */
+public class SendStatusReceiver extends BroadcastReceiver {
+
+    public static final String MESSAGE_SENT_ACTION =
+            "com.android.phone.testapps.smsmanagertestapp.message_sent_action";
+
+    // Defined by platform, but no constant provided. See docs for SmsManager.sendTextMessage.
+    private static final String EXTRA_ERROR_CODE = "errorCode";
+    private static final String EXTRA_NO_DEFAULT = "noDefault";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final int resultCode = getResultCode();
+        if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
+            int errorCode = intent.getIntExtra(EXTRA_ERROR_CODE, -1);
+            boolean userCancel = intent.getBooleanExtra(EXTRA_NO_DEFAULT, false);
+            if (userCancel) {
+                Toast.makeText(context, "SMS not sent, user cancelled.", Toast.LENGTH_LONG).show();
+            } else {
+                Toast.makeText(context, "SMS result=" + resultCode + ", error extra=" + errorCode,
+                        Toast.LENGTH_LONG).show();
+            }
+        }
+    }
+}
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
new file mode 100644
index 0000000..75536f3
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2019 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.android.phone.testapps.smsmanagertestapp;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Supports sending an SMS immediately and offloading the sending of the SMS to a background task.
+ */
+public class SmsManagerTestApp extends Activity {
+
+    private static final int REQUEST_PERMISSION_READ_STATE = 1;
+    private static final int REQUEST_GET_SMS_SUB_ID = 2;
+
+    private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName(
+            "com.android.settings", "com.android.settings.sim.SimDialogActivity");
+
+    /*
+     * Forwarded constants from SimDialogActivity.
+     */
+    private static final String DIALOG_TYPE_KEY = "dialog_type";
+    public static final String RESULT_SUB_ID = "result_sub_id";
+    private static final int SMS_PICK = 2;
+
+    private static int sMessageId = 0;
+    private boolean mIsReadPhoneStateGranted = false;
+
+    private EditText mPhoneNumber;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_main);
+
+        findViewById(R.id.send_text_button).setOnClickListener(this::sendOutgoingSms);
+        findViewById(R.id.send_text_button_service)
+                .setOnClickListener(this::sendOutgoingSmsService);
+        findViewById(R.id.get_sub_for_result_button).setOnClickListener(this::getSubIdForResult);
+        mPhoneNumber = (EditText) findViewById(R.id.phone_number_text);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
+                != PackageManager.PERMISSION_GRANTED
+                || checkSelfPermission(Manifest.permission.SEND_SMS)
+                        != PackageManager.PERMISSION_GRANTED) {
+            mIsReadPhoneStateGranted = false;
+            requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE,
+                    Manifest.permission.SEND_SMS}, REQUEST_PERMISSION_READ_STATE);
+        } else {
+            mIsReadPhoneStateGranted = true;
+        }
+        if (mIsReadPhoneStateGranted) {
+            mPhoneNumber.setText(getPhoneNumber(), TextView.BufferType.NORMAL);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        stopService(new Intent(this, SmsManagerTestService.class));
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        switch (requestCode) {
+            case REQUEST_PERMISSION_READ_STATE: {
+                if (grantResults.length > 0
+                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    mIsReadPhoneStateGranted = true;
+                } else {
+                    // permission denied
+                    Toast.makeText(this, "read_phone_state denied.", Toast.LENGTH_SHORT).show();
+                }
+            }
+
+        }
+
+        if (mIsReadPhoneStateGranted) {
+            mPhoneNumber.setText(getPhoneNumber(), TextView.BufferType.NORMAL);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case (REQUEST_GET_SMS_SUB_ID) : {
+                int resultSubId;
+                if (resultCode == RESULT_OK) {
+                    resultSubId = data == null ? -1 : data.getIntExtra(RESULT_SUB_ID,
+                            SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                    Toast.makeText(this, "User picked sub id = " + resultSubId,
+                            Toast.LENGTH_LONG).show();
+                } else {
+                    Toast.makeText(this, "User cancelled dialog.",
+                            Toast.LENGTH_SHORT).show();
+                }
+                break;
+            }
+        }
+    }
+
+
+    private void sendOutgoingSms(View view) {
+        String phoneNumber = mPhoneNumber.getText().toString();
+        if (TextUtils.isEmpty(phoneNumber)) {
+            Toast.makeText(this, "Couldn't get phone number from view! Ignoring request...",
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
+        if (mIsReadPhoneStateGranted) {
+            SmsManager m = SmsManager.getDefault();
+            m.sendTextMessage(phoneNumber, null, "Test",
+                    PendingIntent.getBroadcast(this, sMessageId, getSendStatusIntent(), 0),
+                    null);
+            sMessageId++;
+        }
+    }
+
+    private void sendOutgoingSmsService(View view) {
+        String phoneNumber = mPhoneNumber.getText().toString();
+        if (TextUtils.isEmpty(phoneNumber)) {
+            Toast.makeText(this, "Couldn't get phone number from view! Ignoring request...",
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
+        if (mIsReadPhoneStateGranted) {
+            Intent sendSmsIntent = new Intent(SmsManagerTestService.SEND_SMS);
+            sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_TEXT, "Text");
+            sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_NUMBER, phoneNumber);
+            sendSmsIntent.putExtra(SmsManagerTestService.EXTRA_SEND_INTENT,
+                    PendingIntent.getBroadcast(this, sMessageId, getSendStatusIntent(), 0));
+            sendSmsIntent.setComponent(new ComponentName(this, SmsManagerTestService.class));
+            startService(sendSmsIntent);
+            sMessageId++;
+        }
+    }
+    private void getSubIdForResult(View view) {
+        // ask the user for a default SMS SIM.
+        Intent intent = new Intent();
+        intent.setComponent(SETTINGS_SUB_PICK_ACTIVITY);
+        intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK);
+        try {
+            startActivity(intent, null);
+        } catch (ActivityNotFoundException anfe) {
+            // If Settings is not installed, only log the error as we do not want to break
+            // legacy applications.
+            Toast.makeText(this, "Unable to launch Settings application.",
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private Intent getSendStatusIntent() {
+        // Encode requestId in intent data
+        return new Intent(SendStatusReceiver.MESSAGE_SENT_ACTION, null, this,
+                SendStatusReceiver.class);
+    }
+
+    private String getPhoneNumber() {
+        String result = "6505551212";
+        int defaultSmsSub = SubscriptionManager.getDefaultSmsSubscriptionId();
+        if (mIsReadPhoneStateGranted) {
+            TelephonyManager tm = getSystemService(TelephonyManager.class);
+            if (tm != null) {
+                tm = tm.createForSubscriptionId(defaultSmsSub);
+                String line1Number = tm.getLine1Number();
+                if (!TextUtils.isEmpty(line1Number)) {
+                    return line1Number;
+                }
+            }
+        } else {
+            Toast.makeText(this, "Couldn't resolve line 1 due to permissions error.",
+                    Toast.LENGTH_LONG).show();
+        }
+        return result;
+    }
+}
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java
new file mode 100644
index 0000000..fcf4a67
--- /dev/null
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 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.android.phone.testapps.smsmanagertestapp;
+
+import android.app.IntentService;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.telephony.SmsManager;
+import android.util.Log;
+
+/**
+ * IntentService whose purpose is to handle outgoing SMS intents for this application and schedule
+ * them onto a AsyncTask to sleep for 5 seconds. This allows us to simulate SMS messages being sent
+ * from background services.
+ */
+public class SmsManagerTestService extends IntentService {
+
+    private static final String LOG_TAG = "smsmanagertestservice";
+
+    private static class SendSmsJob extends AsyncTask<Intent, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Intent... intents) {
+            Intent intent = intents[0];
+            try {
+                Thread.sleep(5000);
+            } catch (InterruptedException e) {
+                // testing
+            }
+
+            String text = intent.getStringExtra(EXTRA_SEND_TEXT);
+            String phoneNumber = intent.getStringExtra(EXTRA_SEND_NUMBER);
+            PendingIntent sendIntent = intent.getParcelableExtra(EXTRA_SEND_INTENT);
+            sendSms(phoneNumber, text, sendIntent);
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void aVoid) {
+            Log.i(LOG_TAG, "SMS sent");
+        }
+
+    }
+
+    public static final String SEND_SMS = "com.android.phone.testapps.smsmanagertestapp.send_sms";
+    public static final String EXTRA_SEND_TEXT = "text";
+    public static final String EXTRA_SEND_NUMBER = "number";
+    public static final String EXTRA_SEND_INTENT = "sendIntent";
+
+    public SmsManagerTestService() {
+        super("SmsManagerTestService");
+    }
+
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        switch (intent.getAction()) {
+            case SEND_SMS : {
+                new SendSmsJob().execute(intent);
+                break;
+            }
+        }
+    }
+
+    private static void sendSms(String phoneNumber, String text, PendingIntent sendIntent) {
+        SmsManager m = SmsManager.getDefault();
+        m.sendTextMessage(phoneNumber, null, text, sendIntent, null);
+    }
+}