Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server;
     18 
     19 import android.Manifest;
     20 import android.app.AppOpsManager;
     21 import android.app.PendingIntent;
     22 import android.content.ComponentName;
     23 import android.content.ContentProvider;
     24 import android.content.ContentValues;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.ServiceConnection;
     28 import android.content.pm.PackageManager;
     29 import android.net.Uri;
     30 import android.os.Binder;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.os.IBinder;
     34 import android.os.Message;
     35 import android.os.RemoteException;
     36 import android.os.SystemClock;
     37 import android.os.UserHandle;
     38 import android.service.carrier.CarrierMessagingService;
     39 import android.telephony.SmsManager;
     40 import android.telephony.TelephonyManager;
     41 import android.util.Slog;
     42 
     43 import com.android.internal.telephony.IMms;
     44 import com.android.server.uri.UriGrantsManagerInternal;
     45 
     46 import java.util.List;
     47 
     48 /**
     49  * This class is a proxy for MmsService APIs. We need this because MmsService runs
     50  * in phone process and may crash anytime. This manages a connection to the actual
     51  * MmsService and bridges the public SMS/MMS APIs with MmsService implementation.
     52  */
     53 public class MmsServiceBroker extends SystemService {
     54     private static final String TAG = "MmsServiceBroker";
     55 
     56     private static final ComponentName MMS_SERVICE_COMPONENT =
     57             new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService");
     58 
     59     private static final int MSG_TRY_CONNECTING = 1;
     60 
     61     private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0");
     62     private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0");
     63     private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0");
     64     private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0");
     65 
     66     private static final long SERVICE_CONNECTION_WAIT_TIME_MS = 4 * 1000L; // 4 seconds
     67     private static final long RETRY_DELAY_ON_DISCONNECTION_MS = 3 * 1000L; // 3 seconds
     68 
     69     private Context mContext;
     70     // The actual MMS service instance to invoke
     71     private volatile IMms mService;
     72 
     73     // Cached system service instances
     74     private volatile AppOpsManager mAppOpsManager = null;
     75     private volatile PackageManager mPackageManager = null;
     76     private volatile TelephonyManager mTelephonyManager = null;
     77 
     78     private final Handler mConnectionHandler = new Handler() {
     79         @Override
     80         public void handleMessage(Message msg) {
     81             switch (msg.what) {
     82                 case MSG_TRY_CONNECTING:
     83                     tryConnecting();
     84                     break;
     85                 default:
     86                     Slog.e(TAG, "Unknown message");
     87             }
     88         }
     89     };
     90 
     91     private ServiceConnection mConnection = new ServiceConnection() {
     92         @Override
     93         public void onServiceConnected(ComponentName name, IBinder service) {
     94             Slog.i(TAG, "MmsService connected");
     95             synchronized (MmsServiceBroker.this) {
     96                 mService = IMms.Stub.asInterface(Binder.allowBlocking(service));
     97                 MmsServiceBroker.this.notifyAll();
     98             }
     99         }
    100 
    101         @Override
    102         public void onServiceDisconnected(ComponentName name) {
    103             Slog.i(TAG, "MmsService unexpectedly disconnected");
    104             synchronized (MmsServiceBroker.this) {
    105                 mService = null;
    106                 MmsServiceBroker.this.notifyAll();
    107             }
    108             // Retry connecting, but not too eager (with a delay)
    109             // since it may come back by itself.
    110             mConnectionHandler.sendMessageDelayed(
    111                     mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING),
    112                     RETRY_DELAY_ON_DISCONNECTION_MS);
    113         }
    114     };
    115 
    116     // Instance of IMms for returning failure to service API caller,
    117     // used when MmsService cannot be connected.
    118     private final IMms mServiceStubForFailure = new IMms() {
    119 
    120         @Override
    121         public IBinder asBinder() {
    122             return null;
    123         }
    124 
    125         @Override
    126         public void sendMessage(int subId, String callingPkg, Uri contentUri, String locationUrl,
    127                 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
    128             returnPendingIntentWithError(sentIntent);
    129         }
    130 
    131         @Override
    132         public void downloadMessage(int subId, String callingPkg, String locationUrl,
    133                 Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)
    134                 throws RemoteException {
    135             returnPendingIntentWithError(downloadedIntent);
    136         }
    137 
    138         @Override
    139         public Bundle getCarrierConfigValues(int subId) throws RemoteException {
    140             return null;
    141         }
    142 
    143         @Override
    144         public Uri importTextMessage(String callingPkg, String address, int type, String text,
    145                 long timestampMillis, boolean seen, boolean read) throws RemoteException {
    146             return null;
    147         }
    148 
    149         @Override
    150         public Uri importMultimediaMessage(String callingPkg, Uri contentUri, String messageId,
    151                 long timestampSecs, boolean seen, boolean read) throws RemoteException {
    152             return null;
    153         }
    154 
    155         @Override
    156         public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
    157                 throws RemoteException {
    158             return false;
    159         }
    160 
    161         @Override
    162         public boolean deleteStoredConversation(String callingPkg, long conversationId)
    163                 throws RemoteException {
    164             return false;
    165         }
    166 
    167         @Override
    168         public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
    169                 ContentValues statusValues) throws RemoteException {
    170             return false;
    171         }
    172 
    173         @Override
    174         public boolean archiveStoredConversation(String callingPkg, long conversationId,
    175                 boolean archived) throws RemoteException {
    176             return false;
    177         }
    178 
    179         @Override
    180         public Uri addTextMessageDraft(String callingPkg, String address, String text)
    181                 throws RemoteException {
    182             return null;
    183         }
    184 
    185         @Override
    186         public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
    187                 throws RemoteException {
    188             return null;
    189         }
    190 
    191         @Override
    192         public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
    193                 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
    194             returnPendingIntentWithError(sentIntent);
    195         }
    196 
    197         @Override
    198         public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
    199             // Do nothing
    200         }
    201 
    202         @Override
    203         public boolean getAutoPersisting() throws RemoteException {
    204             return false;
    205         }
    206 
    207         private void returnPendingIntentWithError(PendingIntent pendingIntent) {
    208             try {
    209                 pendingIntent.send(mContext, SmsManager.MMS_ERROR_UNSPECIFIED, null);
    210             } catch (PendingIntent.CanceledException e) {
    211                 Slog.e(TAG, "Failed to return pending intent result", e);
    212             }
    213         }
    214     };
    215 
    216     public MmsServiceBroker(Context context) {
    217         super(context);
    218         mContext = context;
    219         mService = null;
    220     }
    221 
    222     @Override
    223     public void onStart() {
    224         publishBinderService("imms", new BinderService());
    225     }
    226 
    227     public void systemRunning() {
    228         Slog.i(TAG, "Delay connecting to MmsService until an API is called");
    229     }
    230 
    231     private void tryConnecting() {
    232         Slog.i(TAG, "Connecting to MmsService");
    233         synchronized (this) {
    234             if (mService != null) {
    235                 Slog.d(TAG, "Already connected");
    236                 return;
    237             }
    238             final Intent intent = new Intent();
    239             intent.setComponent(MMS_SERVICE_COMPONENT);
    240             try {
    241                 if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
    242                     Slog.e(TAG, "Failed to bind to MmsService");
    243                 }
    244             } catch (SecurityException e) {
    245                 Slog.e(TAG, "Forbidden to bind to MmsService", e);
    246             }
    247         }
    248     }
    249 
    250     private IMms getOrConnectService() {
    251         synchronized (this) {
    252             if (mService != null) {
    253                 return mService;
    254             }
    255             // Service is not connected. Try blocking connecting.
    256             Slog.w(TAG, "MmsService not connected. Try connecting...");
    257             mConnectionHandler.sendMessage(
    258                     mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
    259             final long shouldEnd =
    260                     SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS;
    261             long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS;
    262             while (waitTime > 0) {
    263                 try {
    264                     // TODO: consider using Java concurrent construct instead of raw object wait
    265                     this.wait(waitTime);
    266                 } catch (InterruptedException e) {
    267                     Slog.w(TAG, "Connection wait interrupted", e);
    268                 }
    269                 if (mService != null) {
    270                     // Success
    271                     return mService;
    272                 }
    273                 // Calculate remaining waiting time to make sure we wait the full timeout period
    274                 waitTime = shouldEnd - SystemClock.elapsedRealtime();
    275             }
    276             // Timed out. Something's really wrong.
    277             Slog.e(TAG, "Can not connect to MmsService (timed out)");
    278             return null;
    279         }
    280     }
    281 
    282     /**
    283      * Make sure to return a non-empty service instance. Return the connected MmsService
    284      * instance, if not connected, try connecting. If fail to connect, return a fake service
    285      * instance which returns failure to service caller.
    286      *
    287      * @return a non-empty service instance, real or fake
    288      */
    289     private IMms getServiceGuarded() {
    290         final IMms service = getOrConnectService();
    291         if (service != null) {
    292             return service;
    293         }
    294         return mServiceStubForFailure;
    295     }
    296 
    297     private AppOpsManager getAppOpsManager() {
    298         if (mAppOpsManager == null) {
    299             mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
    300         }
    301         return mAppOpsManager;
    302     }
    303 
    304     private PackageManager getPackageManager() {
    305         if (mPackageManager == null) {
    306             mPackageManager = mContext.getPackageManager();
    307         }
    308         return mPackageManager;
    309     }
    310 
    311     private TelephonyManager getTelephonyManager() {
    312         if (mTelephonyManager == null) {
    313             mTelephonyManager = (TelephonyManager) mContext.getSystemService(
    314                     Context.TELEPHONY_SERVICE);
    315         }
    316         return mTelephonyManager;
    317     }
    318 
    319     private String getCallingPackageName() {
    320         final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
    321         if (packages != null && packages.length > 0) {
    322             return packages[0];
    323         }
    324         return "unknown";
    325     }
    326 
    327     // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
    328     private final class BinderService extends IMms.Stub {
    329         private static final String PHONE_PACKAGE_NAME = "com.android.phone";
    330 
    331         @Override
    332         public void sendMessage(int subId, String callingPkg, Uri contentUri,
    333                 String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
    334                         throws RemoteException {
    335             Slog.d(TAG, "sendMessage() by " + callingPkg);
    336             mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
    337             if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    338                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    339                 Slog.e(TAG, callingPkg + " is not allowed to call sendMessage()");
    340                 return;
    341             }
    342             contentUri = adjustUriForUserAndGrantPermission(contentUri,
    343                     CarrierMessagingService.SERVICE_INTERFACE,
    344                     Intent.FLAG_GRANT_READ_URI_PERMISSION);
    345             getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
    346                     configOverrides, sentIntent);
    347         }
    348 
    349         @Override
    350         public void downloadMessage(int subId, String callingPkg, String locationUrl,
    351                 Uri contentUri, Bundle configOverrides,
    352                 PendingIntent downloadedIntent) throws RemoteException {
    353             Slog.d(TAG, "downloadMessage() by " + callingPkg);
    354             mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
    355                     "Download MMS message");
    356             if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
    357                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    358                 Slog.e(TAG, callingPkg + " is not allowed to call downloadMessage()");
    359                 return;
    360             }
    361             contentUri = adjustUriForUserAndGrantPermission(contentUri,
    362                     CarrierMessagingService.SERVICE_INTERFACE,
    363                     Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    364 
    365             getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
    366                     configOverrides, downloadedIntent);
    367         }
    368 
    369         @Override
    370         public Bundle getCarrierConfigValues(int subId) throws RemoteException {
    371             Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
    372             return getServiceGuarded().getCarrierConfigValues(subId);
    373         }
    374 
    375         @Override
    376         public Uri importTextMessage(String callingPkg, String address, int type, String text,
    377                 long timestampMillis, boolean seen, boolean read) throws RemoteException {
    378             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    379                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    380                 // Silently fail AppOps failure due to not being the default SMS app
    381                 // while writing the TelephonyProvider
    382                 return FAKE_SMS_SENT_URI;
    383             }
    384             return getServiceGuarded().importTextMessage(
    385                     callingPkg, address, type, text, timestampMillis, seen, read);
    386         }
    387 
    388         @Override
    389         public Uri importMultimediaMessage(String callingPkg, Uri contentUri,
    390                 String messageId, long timestampSecs, boolean seen, boolean read)
    391                         throws RemoteException {
    392             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    393                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    394                 // Silently fail AppOps failure due to not being the default SMS app
    395                 // while writing the TelephonyProvider
    396                 return FAKE_MMS_SENT_URI;
    397             }
    398             return getServiceGuarded().importMultimediaMessage(
    399                     callingPkg, contentUri, messageId, timestampSecs, seen, read);
    400         }
    401 
    402         @Override
    403         public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
    404                 throws RemoteException {
    405             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    406                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    407                 return false;
    408             }
    409             return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
    410         }
    411 
    412         @Override
    413         public boolean deleteStoredConversation(String callingPkg, long conversationId)
    414                 throws RemoteException {
    415             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    416                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    417                 return false;
    418             }
    419             return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
    420         }
    421 
    422         @Override
    423         public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
    424                 ContentValues statusValues) throws RemoteException {
    425             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    426                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    427                 return false;
    428             }
    429             return getServiceGuarded()
    430                     .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
    431         }
    432 
    433         @Override
    434         public boolean archiveStoredConversation(String callingPkg, long conversationId,
    435                 boolean archived) throws RemoteException {
    436             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    437                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    438                 return false;
    439             }
    440             return getServiceGuarded()
    441                     .archiveStoredConversation(callingPkg, conversationId, archived);
    442         }
    443 
    444         @Override
    445         public Uri addTextMessageDraft(String callingPkg, String address, String text)
    446                 throws RemoteException {
    447             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    448                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    449                 // Silently fail AppOps failure due to not being the default SMS app
    450                 // while writing the TelephonyProvider
    451                 return FAKE_SMS_DRAFT_URI;
    452             }
    453             return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
    454         }
    455 
    456         @Override
    457         public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
    458                 throws RemoteException {
    459             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    460                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    461                 // Silently fail AppOps failure due to not being the default SMS app
    462                 // while writing the TelephonyProvider
    463                 return FAKE_MMS_DRAFT_URI;
    464             }
    465             return getServiceGuarded().addMultimediaMessageDraft(callingPkg, contentUri);
    466         }
    467 
    468         @Override
    469         public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
    470                 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
    471             if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    472                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    473                 return;
    474             }
    475             getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides,
    476                     sentIntent);
    477         }
    478 
    479         @Override
    480         public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
    481             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    482                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    483                 return;
    484             }
    485             getServiceGuarded().setAutoPersisting(callingPkg, enabled);
    486         }
    487 
    488         @Override
    489         public boolean getAutoPersisting() throws RemoteException {
    490             return getServiceGuarded().getAutoPersisting();
    491         }
    492 
    493         /**
    494          * Modifies the Uri to contain the caller's userId, if necessary.
    495          * Grants the phone package on primary user permission to access the contentUri,
    496          * even if the caller is not in the primary user.
    497          *
    498          * @param contentUri The Uri to adjust
    499          * @param action The intent action used to find the associated carrier app
    500          * @param permission The permission to add
    501          * @return The adjusted Uri containing the calling userId.
    502          */
    503         private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
    504                 int permission) {
    505             final Intent grantIntent = new Intent();
    506             grantIntent.setData(contentUri);
    507             grantIntent.setFlags(permission);
    508 
    509             final int callingUid = Binder.getCallingUid();
    510             final int callingUserId = UserHandle.getCallingUserId();
    511             if (callingUserId != UserHandle.USER_SYSTEM) {
    512                 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
    513             }
    514 
    515             long token = Binder.clearCallingIdentity();
    516             try {
    517                 LocalServices.getService(UriGrantsManagerInternal.class)
    518                         .grantUriPermissionFromIntent(callingUid, PHONE_PACKAGE_NAME,
    519                                 grantIntent, UserHandle.USER_SYSTEM);
    520 
    521                 // Grant permission for the carrier app.
    522                 Intent intent = new Intent(action);
    523                 TelephonyManager telephonyManager =
    524                     (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    525                 List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
    526                         intent);
    527                 if (carrierPackages != null && carrierPackages.size() == 1) {
    528                     LocalServices.getService(UriGrantsManagerInternal.class)
    529                             .grantUriPermissionFromIntent(callingUid, carrierPackages.get(0),
    530                                     grantIntent, UserHandle.USER_SYSTEM);
    531                 }
    532             } finally {
    533                 Binder.restoreCallingIdentity(token);
    534             }
    535             return contentUri;
    536         }
    537     }
    538 }
    539