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