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