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