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