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