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