Issues with messaging apps on non-primary user account

Bug 18399514

Adjust the contentUri to contain the caller's userId so that when
the phone process tries to open the Uri, it will open the provider
on the correct user.
Also make sure the Uri grants are properly qualified. We only need to grant
permission for sending. Receiving an MMS is always done by the primary
user and doesn't need special permissions. Move various permission grants
from the SmsManager to here.

Change-Id: Ib192f651ab05db9f07e9e6245bb343ed7a55b18e
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index da50751..b1c4955 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -20,6 +20,7 @@
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -33,11 +34,15 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Telephony;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
 
 import com.android.internal.telephony.IMms;
 
+import java.util.List;
+
 /**
  * This class is a proxy for MmsService APIs. We need this because MmsService runs
  * in phone process and may crash anytime. This manages a connection to the actual
@@ -226,6 +231,8 @@
 
     // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
     private final class BinderService extends IMms.Stub {
+        private static final String PHONE_PACKAGE_NAME = "com.android.phone";
+
         @Override
         public void sendMessage(int subId, String callingPkg, Uri contentUri,
                 String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
@@ -236,6 +243,9 @@
                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
                 return;
             }
+            contentUri = adjustUriForUserAndGrantPermission(contentUri,
+                    Telephony.Mms.Intents.MMS_SEND_ACTION,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
             getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
                     configOverrides, sentIntent);
         }
@@ -251,6 +261,10 @@
                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
                 return;
             }
+            contentUri = adjustUriForUserAndGrantPermission(contentUri,
+                    Telephony.Mms.Intents.MMS_DOWNLOAD_ACTION,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
             getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
                     configOverrides, downloadedIntent);
         }
@@ -397,5 +411,40 @@
         public boolean getAutoPersisting() throws RemoteException {
             return getServiceGuarded().getAutoPersisting();
         }
+
+        /**
+         * Modifies the Uri to contain the caller's userId, if necessary.
+         * Grants the phone package on primary user permission to access the contentUri,
+         * even if the caller is not in the primary user.
+         *
+         * @param contentUri The Uri to adjust
+         * @param action The intent action used to find the associated carrier app
+         * @param permission The permission to add
+         * @return The adjusted Uri containing the calling userId.
+         */
+        private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
+                int permission) {
+            final int callingUserId = UserHandle.getCallingUserId();
+            if (callingUserId != UserHandle.USER_OWNER) {
+                contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mContext.grantUriPermission(PHONE_PACKAGE_NAME, contentUri, permission);
+
+                // Grant permission for the carrier app.
+                Intent intent = new Intent(action);
+                TelephonyManager telephonyManager =
+                    (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+                List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
+                        intent);
+                if (carrierPackages != null && carrierPackages.size() == 1) {
+                    mContext.grantUriPermission(carrierPackages.get(0), contentUri, permission);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return contentUri;
+        }
     }
 }