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