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.TelephonyManager; 40 import android.util.Slog; 41 42 import com.android.internal.telephony.IMms; 43 44 import 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 */ 51 public 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 private String getCallingPackageName() { 211 final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); 212 if (packages != null && packages.length > 0) { 213 return packages[0]; 214 } 215 return "unknown"; 216 } 217 218 // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service" 219 private final class BinderService extends IMms.Stub { 220 private static final String PHONE_PACKAGE_NAME = "com.android.phone"; 221 222 @Override 223 public void sendMessage(int subId, String callingPkg, Uri contentUri, 224 String locationUrl, Bundle configOverrides, PendingIntent sentIntent) 225 throws RemoteException { 226 Slog.d(TAG, "sendMessage() by " + callingPkg); 227 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message"); 228 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 229 callingPkg) != AppOpsManager.MODE_ALLOWED) { 230 return; 231 } 232 contentUri = adjustUriForUserAndGrantPermission(contentUri, 233 CarrierMessagingService.SERVICE_INTERFACE, 234 Intent.FLAG_GRANT_READ_URI_PERMISSION); 235 getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl, 236 configOverrides, sentIntent); 237 } 238 239 @Override 240 public void downloadMessage(int subId, String callingPkg, String locationUrl, 241 Uri contentUri, Bundle configOverrides, 242 PendingIntent downloadedIntent) throws RemoteException { 243 Slog.d(TAG, "downloadMessage() by " + callingPkg); 244 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS, 245 "Download MMS message"); 246 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(), 247 callingPkg) != AppOpsManager.MODE_ALLOWED) { 248 return; 249 } 250 contentUri = adjustUriForUserAndGrantPermission(contentUri, 251 CarrierMessagingService.SERVICE_INTERFACE, 252 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 253 254 getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri, 255 configOverrides, downloadedIntent); 256 } 257 258 @Override 259 public Bundle getCarrierConfigValues(int subId) throws RemoteException { 260 Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName()); 261 return getServiceGuarded().getCarrierConfigValues(subId); 262 } 263 264 @Override 265 public Uri importTextMessage(String callingPkg, String address, int type, String text, 266 long timestampMillis, boolean seen, boolean read) throws RemoteException { 267 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message"); 268 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 269 callingPkg) != AppOpsManager.MODE_ALLOWED) { 270 // Silently fail AppOps failure due to not being the default SMS app 271 // while writing the TelephonyProvider 272 return FAKE_SMS_SENT_URI; 273 } 274 return getServiceGuarded().importTextMessage( 275 callingPkg, address, type, text, timestampMillis, seen, read); 276 } 277 278 @Override 279 public Uri importMultimediaMessage(String callingPkg, Uri contentUri, 280 String messageId, long timestampSecs, boolean seen, boolean read) 281 throws RemoteException { 282 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message"); 283 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 284 callingPkg) != AppOpsManager.MODE_ALLOWED) { 285 // Silently fail AppOps failure due to not being the default SMS app 286 // while writing the TelephonyProvider 287 return FAKE_MMS_SENT_URI; 288 } 289 return getServiceGuarded().importMultimediaMessage( 290 callingPkg, contentUri, messageId, timestampSecs, seen, read); 291 } 292 293 @Override 294 public boolean deleteStoredMessage(String callingPkg, Uri messageUri) 295 throws RemoteException { 296 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 297 "Delete SMS/MMS message"); 298 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 299 callingPkg) != AppOpsManager.MODE_ALLOWED) { 300 return false; 301 } 302 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri); 303 } 304 305 @Override 306 public boolean deleteStoredConversation(String callingPkg, long conversationId) 307 throws RemoteException { 308 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation"); 309 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 310 callingPkg) != AppOpsManager.MODE_ALLOWED) { 311 return false; 312 } 313 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId); 314 } 315 316 @Override 317 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri, 318 ContentValues statusValues) throws RemoteException { 319 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 320 "Update SMS/MMS message"); 321 return getServiceGuarded() 322 .updateStoredMessageStatus(callingPkg, messageUri, statusValues); 323 } 324 325 @Override 326 public boolean archiveStoredConversation(String callingPkg, long conversationId, 327 boolean archived) throws RemoteException { 328 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 329 "Update SMS/MMS message"); 330 return getServiceGuarded() 331 .archiveStoredConversation(callingPkg, conversationId, archived); 332 } 333 334 @Override 335 public Uri addTextMessageDraft(String callingPkg, String address, String text) 336 throws RemoteException { 337 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft"); 338 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 339 callingPkg) != AppOpsManager.MODE_ALLOWED) { 340 // Silently fail AppOps failure due to not being the default SMS app 341 // while writing the TelephonyProvider 342 return FAKE_SMS_DRAFT_URI; 343 } 344 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text); 345 } 346 347 @Override 348 public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri) 349 throws RemoteException { 350 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft"); 351 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 352 callingPkg) != AppOpsManager.MODE_ALLOWED) { 353 // Silently fail AppOps failure due to not being the default SMS app 354 // while writing the TelephonyProvider 355 return FAKE_MMS_DRAFT_URI; 356 } 357 return getServiceGuarded().addMultimediaMessageDraft(callingPkg, contentUri); 358 } 359 360 @Override 361 public void sendStoredMessage(int subId, String callingPkg, Uri messageUri, 362 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException { 363 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, 364 "Send stored MMS message"); 365 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 366 callingPkg) != AppOpsManager.MODE_ALLOWED) { 367 return; 368 } 369 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides, 370 sentIntent); 371 } 372 373 @Override 374 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException { 375 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist"); 376 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 377 callingPkg) != AppOpsManager.MODE_ALLOWED) { 378 return; 379 } 380 getServiceGuarded().setAutoPersisting(callingPkg, enabled); 381 } 382 383 @Override 384 public boolean getAutoPersisting() throws RemoteException { 385 return getServiceGuarded().getAutoPersisting(); 386 } 387 388 /** 389 * Modifies the Uri to contain the caller's userId, if necessary. 390 * Grants the phone package on primary user permission to access the contentUri, 391 * even if the caller is not in the primary user. 392 * 393 * @param contentUri The Uri to adjust 394 * @param action The intent action used to find the associated carrier app 395 * @param permission The permission to add 396 * @return The adjusted Uri containing the calling userId. 397 */ 398 private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action, 399 int permission) { 400 final int callingUserId = UserHandle.getCallingUserId(); 401 if (callingUserId != UserHandle.USER_OWNER) { 402 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId); 403 } 404 long token = Binder.clearCallingIdentity(); 405 try { 406 mContext.grantUriPermission(PHONE_PACKAGE_NAME, contentUri, permission); 407 408 // Grant permission for the carrier app. 409 Intent intent = new Intent(action); 410 TelephonyManager telephonyManager = 411 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 412 List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent( 413 intent); 414 if (carrierPackages != null && carrierPackages.size() == 1) { 415 mContext.grantUriPermission(carrierPackages.get(0), contentUri, permission); 416 } 417 } finally { 418 Binder.restoreCallingIdentity(token); 419 } 420 return contentUri; 421 } 422 } 423 } 424