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.ActivityManagerInternal;
     21 import android.app.AppOpsManager;
     22 import android.app.PendingIntent;
     23 import android.content.ComponentName;
     24 import android.content.ContentProvider;
     25 import android.content.ContentValues;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.ServiceConnection;
     29 import android.content.pm.PackageManager;
     30 import android.net.Uri;
     31 import android.os.Binder;
     32 import android.os.Bundle;
     33 import android.os.Handler;
     34 import android.os.IBinder;
     35 import android.os.Message;
     36 import android.os.RemoteException;
     37 import android.os.SystemClock;
     38 import android.os.UserHandle;
     39 import android.service.carrier.CarrierMessagingService;
     40 import android.telephony.SmsManager;
     41 import android.telephony.TelephonyManager;
     42 import android.util.Slog;
     43 
     44 import com.android.internal.telephony.IMms;
     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                 return;
    340             }
    341             contentUri = adjustUriForUserAndGrantPermission(contentUri,
    342                     CarrierMessagingService.SERVICE_INTERFACE,
    343                     Intent.FLAG_GRANT_READ_URI_PERMISSION);
    344             getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
    345                     configOverrides, sentIntent);
    346         }
    347 
    348         @Override
    349         public void downloadMessage(int subId, String callingPkg, String locationUrl,
    350                 Uri contentUri, Bundle configOverrides,
    351                 PendingIntent downloadedIntent) throws RemoteException {
    352             Slog.d(TAG, "downloadMessage() by " + callingPkg);
    353             mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
    354                     "Download MMS message");
    355             if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
    356                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    357                 return;
    358             }
    359             contentUri = adjustUriForUserAndGrantPermission(contentUri,
    360                     CarrierMessagingService.SERVICE_INTERFACE,
    361                     Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    362 
    363             getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
    364                     configOverrides, downloadedIntent);
    365         }
    366 
    367         @Override
    368         public Bundle getCarrierConfigValues(int subId) throws RemoteException {
    369             Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
    370             return getServiceGuarded().getCarrierConfigValues(subId);
    371         }
    372 
    373         @Override
    374         public Uri importTextMessage(String callingPkg, String address, int type, String text,
    375                 long timestampMillis, boolean seen, boolean read) throws RemoteException {
    376             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    377                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    378                 // Silently fail AppOps failure due to not being the default SMS app
    379                 // while writing the TelephonyProvider
    380                 return FAKE_SMS_SENT_URI;
    381             }
    382             return getServiceGuarded().importTextMessage(
    383                     callingPkg, address, type, text, timestampMillis, seen, read);
    384         }
    385 
    386         @Override
    387         public Uri importMultimediaMessage(String callingPkg, Uri contentUri,
    388                 String messageId, long timestampSecs, boolean seen, boolean read)
    389                         throws RemoteException {
    390             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    391                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    392                 // Silently fail AppOps failure due to not being the default SMS app
    393                 // while writing the TelephonyProvider
    394                 return FAKE_MMS_SENT_URI;
    395             }
    396             return getServiceGuarded().importMultimediaMessage(
    397                     callingPkg, contentUri, messageId, timestampSecs, seen, read);
    398         }
    399 
    400         @Override
    401         public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
    402                 throws RemoteException {
    403             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    404                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    405                 return false;
    406             }
    407             return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
    408         }
    409 
    410         @Override
    411         public boolean deleteStoredConversation(String callingPkg, long conversationId)
    412                 throws RemoteException {
    413             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    414                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    415                 return false;
    416             }
    417             return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
    418         }
    419 
    420         @Override
    421         public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
    422                 ContentValues statusValues) throws RemoteException {
    423             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    424                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    425                 return false;
    426             }
    427             return getServiceGuarded()
    428                     .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
    429         }
    430 
    431         @Override
    432         public boolean archiveStoredConversation(String callingPkg, long conversationId,
    433                 boolean archived) throws RemoteException {
    434             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    435                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    436                 return false;
    437             }
    438             return getServiceGuarded()
    439                     .archiveStoredConversation(callingPkg, conversationId, archived);
    440         }
    441 
    442         @Override
    443         public Uri addTextMessageDraft(String callingPkg, String address, String text)
    444                 throws RemoteException {
    445             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    446                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    447                 // Silently fail AppOps failure due to not being the default SMS app
    448                 // while writing the TelephonyProvider
    449                 return FAKE_SMS_DRAFT_URI;
    450             }
    451             return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
    452         }
    453 
    454         @Override
    455         public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
    456                 throws RemoteException {
    457             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    458                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    459                 // Silently fail AppOps failure due to not being the default SMS app
    460                 // while writing the TelephonyProvider
    461                 return FAKE_MMS_DRAFT_URI;
    462             }
    463             return getServiceGuarded().addMultimediaMessageDraft(callingPkg, contentUri);
    464         }
    465 
    466         @Override
    467         public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
    468                 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
    469             if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    470                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    471                 return;
    472             }
    473             getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides,
    474                     sentIntent);
    475         }
    476 
    477         @Override
    478         public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
    479             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
    480                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
    481                 return;
    482             }
    483             getServiceGuarded().setAutoPersisting(callingPkg, enabled);
    484         }
    485 
    486         @Override
    487         public boolean getAutoPersisting() throws RemoteException {
    488             return getServiceGuarded().getAutoPersisting();
    489         }
    490 
    491         /**
    492          * Modifies the Uri to contain the caller's userId, if necessary.
    493          * Grants the phone package on primary user permission to access the contentUri,
    494          * even if the caller is not in the primary user.
    495          *
    496          * @param contentUri The Uri to adjust
    497          * @param action The intent action used to find the associated carrier app
    498          * @param permission The permission to add
    499          * @return The adjusted Uri containing the calling userId.
    500          */
    501         private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
    502                 int permission) {
    503             final Intent grantIntent = new Intent();
    504             grantIntent.setData(contentUri);
    505             grantIntent.setFlags(permission);
    506 
    507             final int callingUid = Binder.getCallingUid();
    508             final int callingUserId = UserHandle.getCallingUserId();
    509             if (callingUserId != UserHandle.USER_SYSTEM) {
    510                 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
    511             }
    512 
    513             long token = Binder.clearCallingIdentity();
    514             try {
    515                 LocalServices.getService(ActivityManagerInternal.class)
    516                         .grantUriPermissionFromIntent(callingUid, PHONE_PACKAGE_NAME,
    517                                 grantIntent, UserHandle.USER_SYSTEM);
    518 
    519                 // Grant permission for the carrier app.
    520                 Intent intent = new Intent(action);
    521                 TelephonyManager telephonyManager =
    522                     (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    523                 List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
    524                         intent);
    525                 if (carrierPackages != null && carrierPackages.size() == 1) {
    526                     LocalServices.getService(ActivityManagerInternal.class)
    527                             .grantUriPermissionFromIntent(callingUid, carrierPackages.get(0),
    528                                     grantIntent, UserHandle.USER_SYSTEM);
    529                 }
    530             } finally {
    531                 Binder.restoreCallingIdentity(token);
    532             }
    533             return contentUri;
    534         }
    535     }
    536 }
    537