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