Add telephony commands about SMS apps

- Also add a new broadcast that's sent after the default SMS app
changes.

- Also fix on which user ACTION_DEFAULT_SMS_PACKAGE_CHANGED is sent.

New commands:
- cmd phone sms get-apps [--user USER_ID]
  Print all SMS apps on a user.

- cmd phone sms get-default-app [--user USER_ID]
  Get the default SMS app.

- cmd phone sms set-default-app [--user USER_ID] PACKAGE_NAME
  Set PACKAGE_NAME as the default SMS app.

Bug: 109809543
Test: With a secondary user (user 10) running and "com.textra" installed on
user-0 and selected as the default:

--------------------------------------------------------
- Test get-apps

$ cmd phone sms get-apps
com.textra
com.google.android.apps.messaging

$ cmd phone sms get-apps --user 0
com.textra
com.google.android.apps.messaging

$ cmd phone sms get-apps --user 10
com.google.android.apps.messaging

--------------------------------------------------------
- Test get-deafult-app

$ cmd phone sms get-default-app
com.textra

$ cmd phone sms get-default-app --user 0
com.textra

$ cmd phone sms get-default-app --user 10
com.google.android.apps.messaging

--------------------------------------------------------
- Change the default app

$ cmd phone sms set-default-app --use0 com.google.android.apps.messaging
SMS app set to com.google.android.apps.messaging

$ cmd phone sms get-default-app --user 0
com.google.android.apps.messaging

$ cmd phone sms get-default-app --user 10
com.google.android.apps.messaging

- Change again
$ cmd phone sms set-default-app com.textra
SMS app set to com.textra

$ cmd phone sms get-default-app
com.textra

$ cmd phone sms get-default-app --user 10
com.google.android.apps.messaging

--------------------------------------------------------
- Package doesn't exist

$ cmd phone sms set-default-app a.b.c

Exception occurred while executing:
java.lang.IllegalArgumentException: Package a.b.c is not an SMS app
    at com.android.phone.PhoneInterfaceManager.setDefaultSmsApp(PhoneInterfaceManager.java:5420)
    at com.android.phone.TelephonyShellCommand.handleSmsSetDefaultApp(TelephonyShellCommand.java:398)
    at com.android.phone.TelephonyShellCommand.handleSmsCommand(TelephonyShellCommand.java:338)
    at com.android.phone.TelephonyShellCommand.onCommand(TelephonyShellCommand.java:72)
    at android.os.ShellCommand.exec(ShellCommand.java:103)
    at com.android.phone.PhoneInterfaceManager.onShellCommand(PhoneInterfaceManager.java:4896)
    at android.os.Binder.shellCommand(Binder.java:677)
    at android.os.Binder.onTransact(Binder.java:561)
    at com.android.internal.telephony.ITelephony$Stub.onTransact(ITelephony.java:4985)
    at android.os.Binder.execTransact(Binder.java:776)

--------------------------------------------------------
- User doesn't exist

$ cmd phone sms get-apps --user 123

Exception occurred while executing:
java.lang.IllegalStateException: User 123 does not exist or not running
    at com.android.phone.PhoneInterfaceManager.ensureUserRunning(PhoneInterfaceManager.java:5366)
    at com.android.phone.PhoneInterfaceManager.getSmsApps(PhoneInterfaceManager.java:5376)
    at com.android.phone.TelephonyShellCommand.handleSmsGetApps(TelephonyShellCommand.java:375)
    at com.android.phone.TelephonyShellCommand.handleSmsCommand(TelephonyShellCommand.java:332)
    at com.android.phone.TelephonyShellCommand.onCommand(TelephonyShellCommand.java:72)
    at android.os.ShellCommand.exec(ShellCommand.java:103)
    at com.android.phone.PhoneInterfaceManager.onShellCommand(PhoneInterfaceManager.java:4896)
    at android.os.Binder.shellCommand(Binder.java:677)
    at android.os.Binder.onTransact(Binder.java:561)
    at com.android.internal.telephony.ITelephony$Stub.onTransact(ITelephony.java:4985)
    at android.os.Binder.execTransact(Binder.java:776)

$ cmd phone sms get-default-app --user 123

Exception occurred while executing:
java.lang.IllegalStateException: User 123 does not exist or not running
    at com.android.phone.PhoneInterfaceManager.ensureUserRunning(PhoneInterfaceManager.java:5366)
    at com.android.phone.PhoneInterfaceManager.getDefaultSmsApp(PhoneInterfaceManager.java:5396)
    at com.android.phone.TelephonyShellCommand.handleSmsGetDefaultApp(TelephonyShellCommand.java:387)
    at com.android.phone.TelephonyShellCommand.handleSmsCommand(TelephonyShellCommand.java:335)
    at com.android.phone.TelephonyShellCommand.onCommand(TelephonyShellCommand.java:72)
    at android.os.ShellCommand.exec(ShellCommand.java:103)
    at com.android.phone.PhoneInterfaceManager.onShellCommand(PhoneInterfaceManager.java:4896)
    at android.os.Binder.shellCommand(Binder.java:677)
    at android.os.Binder.onTransact(Binder.java:561)
    at com.android.internal.telephony.ITelephony$Stub.onTransact(ITelephony.java:4985)
    at android.os.Binder.execTransact(Binder.java:776)

$ cmd phone sms set-default-app --user 123 a.b.c

Exception occurred while executing:
java.lang.IllegalStateException: User 123 does not exist or not running
    at com.android.phone.PhoneInterfaceManager.ensureUserRunning(PhoneInterfaceManager.java:5366)
    at com.android.phone.PhoneInterfaceManager.setDefaultSmsApp(PhoneInterfaceManager.java:5410)
    at com.android.phone.TelephonyShellCommand.handleSmsSetDefaultApp(TelephonyShellCommand.java:398)
    at com.android.phone.TelephonyShellCommand.handleSmsCommand(TelephonyShellCommand.java:338)
    at com.android.phone.TelephonyShellCommand.onCommand(TelephonyShellCommand.java:72)
    at android.os.ShellCommand.exec(ShellCommand.java:103)
    at com.android.phone.PhoneInterfaceManager.onShellCommand(PhoneInterfaceManager.java:4896)
    at android.os.Binder.shellCommand(Binder.java:677)
    at android.os.Binder.onTransact(Binder.java:561)
    at com.android.internal.telephony.ITelephony$Stub.onTransact(ITelephony.java:4985)
    at android.os.Binder.execTransact(Binder.java:776)

Merged-In: I8e01ae3d18f6e58963a39977dccb64a607cfb55d
Change-Id: I8e01ae3d18f6e58963a39977dccb64a607cfb55d
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 5bb617e..8fea37a 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -116,6 +116,8 @@
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SmsApplication;
+import com.android.internal.telephony.SmsApplication.SmsApplicationData;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.euicc.EuiccConnector;
@@ -139,6 +141,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -5671,6 +5674,75 @@
         }
     }
 
+    private void ensureUserRunning(int userId) {
+        if (!mUserManager.isUserRunning(userId)) {
+            throw new IllegalStateException("User " + userId + " does not exist or not running");
+        }
+    }
+
+    /**
+     * Returns a list of SMS apps on a given user.
+     *
+     * Only the shell user (UID 2000 or 0) can call it.
+     * Target user must be running.
+     */
+    @Override
+    public String[] getSmsApps(int userId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getSmsApps");
+        ensureUserRunning(userId);
+
+        final Collection<SmsApplicationData> apps =
+                SmsApplication.getApplicationCollectionAsUser(mApp, userId);
+
+        String[] ret = new String[apps.size()];
+        int i = 0;
+        for (SmsApplicationData app : apps) {
+            ret[i++] = app.mPackageName;
+        }
+        return ret;
+    }
+
+    /**
+     * Returns the default SMS app package name on a given user.
+     *
+     * Only the shell user (UID 2000 or 0) can call it.
+     * Target user must be running.
+     */
+    @Override
+    public String getDefaultSmsApp(int userId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDefaultSmsApp");
+        ensureUserRunning(userId);
+
+        final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(mApp,
+                /* updateIfNeeded= */ true, userId);
+        return cn == null ? null : cn.getPackageName();
+    }
+
+    /**
+     * Set a package as the default SMS app on a given user.
+     *
+     * Only the shell user (UID 2000 or 0) can call it.
+     * Target user must be running.
+     */
+    @Override
+    public void setDefaultSmsApp(int userId, String packageName) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDefaultSmsApp");
+        ensureUserRunning(userId);
+
+        boolean found = false;
+        for (String pkg : getSmsApps(userId)) {
+            if (TextUtils.equals(packageName, pkg)) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            throw new IllegalArgumentException("Package " + packageName + " is not an SMS app");
+        }
+
+        SmsApplication.setDefaultApplicationAsUser(packageName, mApp, userId);
+    }
+
     @Override
     public List<String> getCertsFromCarrierPrivilegeAccessRules(int subId) {
         enforceReadPrivilegedPermission("getCertsFromCarrierPrivilegeAccessRules");
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 4acb46b..1a25ae3 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -18,6 +18,7 @@
 
 import android.os.RemoteException;
 import android.os.ShellCommand;
+import android.os.UserHandle;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
 
@@ -39,11 +40,16 @@
     private static final int DEFAULT_PHONE_ID = 0;
 
     private static final String IMS_SUBCOMMAND = "ims";
+    private static final String SMS_SUBCOMMAND = "sms";
     private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
     private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
     private static final String IMS_ENABLE = "enable";
     private static final String IMS_DISABLE = "disable";
 
+    private static final String SMS_GET_APPS = "get-apps";
+    private static final String SMS_GET_DEFAULT_APP = "get-default-app";
+    private static final String SMS_SET_DEFAULT_APP = "set-default-app";
+
     // Take advantage of existing methods that already contain permissions checks when possible.
     private final ITelephony mInterface;
 
@@ -61,6 +67,9 @@
             case IMS_SUBCOMMAND: {
                 return handleImsCommand();
             }
+            case SMS_SUBCOMMAND: {
+                return handleSmsCommand();
+            }
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -75,7 +84,10 @@
         pw.println("    Print this help text.");
         pw.println("  ims");
         pw.println("    IMS Commands.");
+        pw.println("  sms");
+        pw.println("    SMS Commands.");
         onHelpIms();
+        onHelpSms();
     }
 
     private void onHelpIms() {
@@ -103,6 +115,17 @@
         pw.println("    slot if none is specified.");
     }
 
+    private void onHelpSms() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("SMS Commands:");
+        pw.println("  sms get-apps [--user USER_ID]");
+        pw.println("    Print all SMS apps on a user.");
+        pw.println("  sms get-default-app [--user USER_ID]");
+        pw.println("    Get the default SMS app.");
+        pw.println("  sms set-default-app [--user USER_ID] PACKAGE_NAME");
+        pw.println("    Set PACKAGE_NAME as the default SMS app.");
+    }
+
     private int handleImsCommand() {
         String arg = getNextArg();
         if (arg == null) {
@@ -296,4 +319,85 @@
         }
         return slotId;
     }
+
+    private int handleSmsCommand() {
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpSms();
+            return 0;
+        }
+
+        try {
+            switch (arg) {
+                case SMS_GET_APPS: {
+                    return handleSmsGetApps();
+                }
+                case SMS_GET_DEFAULT_APP: {
+                    return handleSmsGetDefaultApp();
+                }
+                case SMS_SET_DEFAULT_APP: {
+                    return handleSmsSetDefaultApp();
+                }
+                default:
+                    getErrPrintWriter().println("Unknown command " + arg);
+            }
+        } catch (RemoteException e) {
+            getErrPrintWriter().println("RemoteException: " + e.getMessage());
+        }
+
+        return -1;
+    }
+
+    private int maybeParseUserIdArg() {
+        int userId = UserHandle.USER_SYSTEM;
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "--user": {
+                    try {
+                        userId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        getErrPrintWriter().println("Invalid user ID for --user");
+                        return -1;
+                    }
+                    break;
+                }
+            }
+        }
+        return userId;
+    }
+
+    private int handleSmsGetApps() throws RemoteException {
+        final int userId = maybeParseUserIdArg();
+        if (userId < 0) {
+            return -1;
+        }
+
+        for (String packageName : mInterface.getSmsApps(userId)) {
+            getOutPrintWriter().println(packageName);
+        }
+        return 0;
+    }
+
+    private int handleSmsGetDefaultApp() throws RemoteException {
+        final int userId = maybeParseUserIdArg();
+        if (userId < 0) {
+            return -1;
+        }
+
+        getOutPrintWriter().println(mInterface.getDefaultSmsApp(userId));
+        return 0;
+    }
+
+    private int handleSmsSetDefaultApp() throws RemoteException {
+        final int userId = maybeParseUserIdArg();
+        if (userId < 0) {
+            return -1;
+        }
+
+        String packageName = getNextArgRequired();
+        mInterface.setDefaultSmsApp(userId, packageName);
+        getOutPrintWriter().println("SMS app set to " + mInterface.getDefaultSmsApp(userId));
+        return 0;
+    }
 }