MmsServiceBroker.java revision fa58ac09df51ddc7710a7b2837170dd3780aaf3a
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 com.android.internal.telephony.IMms; 20 21import android.Manifest; 22import android.app.AppOpsManager; 23import android.app.PendingIntent; 24import android.content.ComponentName; 25import android.content.ContentValues; 26import android.content.Context; 27import android.content.Intent; 28import android.content.ServiceConnection; 29import android.content.pm.PackageManager; 30import android.net.Uri; 31import android.os.Binder; 32import android.os.Handler; 33import android.os.IBinder; 34import android.os.Message; 35import android.os.RemoteException; 36import android.telephony.TelephonyManager; 37import android.util.Slog; 38 39/** 40 * This class is a proxy for MmsService APIs. We need this because MmsService runs 41 * in phone process and may crash anytime. This manages a connection to the actual 42 * MmsService and bridges the public SMS/MMS APIs with MmsService implementation. 43 */ 44public class MmsServiceBroker extends SystemService { 45 private static final String TAG = "MmsServiceBroker"; 46 47 private static final ComponentName MMS_SERVICE_COMPONENT = 48 new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService"); 49 50 private static final int MSG_TRY_CONNECTING = 1; 51 52 private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0"); 53 private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0"); 54 private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0"); 55 private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0"); 56 57 private Context mContext; 58 // The actual MMS service instance to invoke 59 private volatile IMms mService; 60 private boolean mIsConnecting; 61 62 // Cached system service instances 63 private volatile AppOpsManager mAppOpsManager = null; 64 private volatile PackageManager mPackageManager = null; 65 private volatile TelephonyManager mTelephonyManager = null; 66 67 private final Handler mConnectionHandler = new Handler() { 68 @Override 69 public void handleMessage(Message msg) { 70 switch (msg.what) { 71 case MSG_TRY_CONNECTING: 72 tryConnecting(); 73 break; 74 default: 75 Slog.e(TAG, "Unknown message"); 76 } 77 } 78 }; 79 80 private ServiceConnection mConnection = new ServiceConnection() { 81 @Override 82 public void onServiceConnected(ComponentName name, IBinder service) { 83 Slog.i(TAG, "MmsService connected"); 84 synchronized (MmsServiceBroker.this) { 85 mService = IMms.Stub.asInterface(service); 86 mIsConnecting = false; 87 } 88 } 89 90 @Override 91 public void onServiceDisconnected(ComponentName name) { 92 Slog.i(TAG, "MmsService unexpectedly disconnected"); 93 synchronized (MmsServiceBroker.this) { 94 mService = null; 95 mIsConnecting = false; 96 } 97 } 98 }; 99 100 public MmsServiceBroker(Context context) { 101 super(context); 102 mContext = context; 103 mService = null; 104 mIsConnecting = false; 105 } 106 107 @Override 108 public void onStart() { 109 publishBinderService("imms", new BinderService()); 110 } 111 112 public void systemRunning() { 113 tryConnecting(); 114 } 115 116 private void tryConnecting() { 117 Slog.i(TAG, "Connecting to MmsService"); 118 synchronized (this) { 119 if (mIsConnecting) { 120 Slog.d(TAG, "Already connecting"); 121 return; 122 } 123 final Intent intent = new Intent(); 124 intent.setComponent(MMS_SERVICE_COMPONENT); 125 try { 126 if (mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) { 127 mIsConnecting = true; 128 } else { 129 Slog.e(TAG, "Failed to connect to MmsService"); 130 } 131 } catch (SecurityException e) { 132 Slog.e(TAG, "Forbidden to connect to MmsService", e); 133 } 134 } 135 } 136 137 private void ensureService() { 138 if (mService == null) { 139 // Service instance lost, kicking off the connection again 140 mConnectionHandler.sendMessage(mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING)); 141 throw new RuntimeException("MMS service is not connected"); 142 } 143 } 144 145 /** 146 * Making sure when we obtain the mService instance it is always valid. 147 * Throws {@link RuntimeException} when it is empty. 148 */ 149 private IMms getServiceGuarded() { 150 ensureService(); 151 return mService; 152 } 153 154 private AppOpsManager getAppOpsManager() { 155 if (mAppOpsManager == null) { 156 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 157 } 158 return mAppOpsManager; 159 } 160 161 private PackageManager getPackageManager() { 162 if (mPackageManager == null) { 163 mPackageManager = mContext.getPackageManager(); 164 } 165 return mPackageManager; 166 } 167 168 private TelephonyManager getTelephonyManager() { 169 if (mTelephonyManager == null) { 170 mTelephonyManager = (TelephonyManager) mContext.getSystemService( 171 Context.TELEPHONY_SERVICE); 172 } 173 return mTelephonyManager; 174 } 175 176 /* 177 * Throws a security exception unless the caller has carrier privilege. 178 */ 179 private void enforceCarrierPrivilege() { 180 String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); 181 for (String pkg : packages) { 182 if (getTelephonyManager().checkCarrierPrivilegesForPackage(pkg) == 183 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { 184 return; 185 } 186 } 187 throw new SecurityException("No carrier privilege"); 188 } 189 190 // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service" 191 private final class BinderService extends IMms.Stub { 192 @Override 193 public void sendMessage(long subId, String callingPkg, byte[] pdu, String locationUrl, 194 PendingIntent sentIntent) throws RemoteException { 195 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message"); 196 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 197 callingPkg) != AppOpsManager.MODE_ALLOWED) { 198 return; 199 } 200 getServiceGuarded().sendMessage(subId, callingPkg, pdu, locationUrl, sentIntent); 201 } 202 203 @Override 204 public void downloadMessage(long subId, String callingPkg, String locationUrl, 205 PendingIntent downloadedIntent) throws RemoteException { 206 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS, 207 "Download MMS message"); 208 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(), 209 callingPkg) != AppOpsManager.MODE_ALLOWED) { 210 return; 211 } 212 getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, downloadedIntent); 213 } 214 215 @Override 216 public void updateMmsSendStatus(int messageRef, boolean success) throws RemoteException { 217 enforceCarrierPrivilege(); 218 getServiceGuarded().updateMmsSendStatus(messageRef, success); 219 } 220 221 @Override 222 public void updateMmsDownloadStatus(int messageRef, byte[] pdu) throws RemoteException { 223 enforceCarrierPrivilege(); 224 getServiceGuarded().updateMmsDownloadStatus(messageRef, pdu); 225 } 226 227 @Override 228 public boolean getCarrierConfigBoolean(String name, boolean defaultValue) 229 throws RemoteException { 230 return getServiceGuarded().getCarrierConfigBoolean(name, defaultValue); 231 } 232 233 @Override 234 public int getCarrierConfigInt(String name, int defaultValue) throws RemoteException { 235 return getServiceGuarded().getCarrierConfigInt(name, defaultValue); 236 } 237 238 @Override 239 public String getCarrierConfigString(String name, String defaultValue) 240 throws RemoteException { 241 return getServiceGuarded().getCarrierConfigString(name, defaultValue); 242 } 243 244 @Override 245 public void setCarrierConfigBoolean(String callingPkg, String name, boolean value) 246 throws RemoteException { 247 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config"); 248 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 249 callingPkg) != AppOpsManager.MODE_ALLOWED) { 250 return; 251 } 252 getServiceGuarded().setCarrierConfigBoolean(callingPkg, name, value); 253 } 254 255 @Override 256 public void setCarrierConfigInt(String callingPkg, String name, int value) 257 throws RemoteException { 258 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config"); 259 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 260 callingPkg) != AppOpsManager.MODE_ALLOWED) { 261 return; 262 } 263 getServiceGuarded().setCarrierConfigInt(callingPkg, name, value); 264 } 265 266 @Override 267 public void setCarrierConfigString(String callingPkg, String name, String value) 268 throws RemoteException { 269 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config"); 270 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 271 callingPkg) != AppOpsManager.MODE_ALLOWED) { 272 return; 273 } 274 getServiceGuarded().setCarrierConfigString(callingPkg, name, value); 275 } 276 277 @Override 278 public Uri importTextMessage(String callingPkg, String address, int type, String text, 279 long timestampMillis, boolean seen, boolean read) throws RemoteException { 280 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message"); 281 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 282 callingPkg) != AppOpsManager.MODE_ALLOWED) { 283 // Silently fail AppOps failure due to not being the default SMS app 284 // while writing the TelephonyProvider 285 return FAKE_SMS_SENT_URI; 286 } 287 return getServiceGuarded().importTextMessage( 288 callingPkg, address, type, text, timestampMillis, seen, read); 289 } 290 291 @Override 292 public Uri importMultimediaMessage(String callingPkg, byte[] pdu, String messageId, 293 long timestampSecs, boolean seen, boolean read) throws RemoteException { 294 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS 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_MMS_SENT_URI; 300 } 301 return getServiceGuarded().importMultimediaMessage( 302 callingPkg, pdu, messageId, timestampSecs, seen, read); 303 } 304 305 @Override 306 public boolean deleteStoredMessage(String callingPkg, Uri messageUri) 307 throws RemoteException { 308 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 309 "Delete SMS/MMS message"); 310 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 311 callingPkg) != AppOpsManager.MODE_ALLOWED) { 312 return false; 313 } 314 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri); 315 } 316 317 @Override 318 public boolean deleteStoredConversation(String callingPkg, long conversationId) 319 throws RemoteException { 320 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation"); 321 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 322 callingPkg) != AppOpsManager.MODE_ALLOWED) { 323 return false; 324 } 325 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId); 326 } 327 328 @Override 329 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri, 330 ContentValues statusValues) throws RemoteException { 331 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 332 "Update SMS/MMS message"); 333 return getServiceGuarded() 334 .updateStoredMessageStatus(callingPkg, messageUri, statusValues); 335 } 336 337 @Override 338 public boolean archiveStoredConversation(String callingPkg, long conversationId, 339 boolean archived) throws RemoteException { 340 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 341 "Update SMS/MMS message"); 342 return getServiceGuarded() 343 .archiveStoredConversation(callingPkg, conversationId, archived); 344 } 345 346 @Override 347 public Uri addTextMessageDraft(String callingPkg, String address, String text) 348 throws RemoteException { 349 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft"); 350 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 351 callingPkg) != AppOpsManager.MODE_ALLOWED) { 352 // Silently fail AppOps failure due to not being the default SMS app 353 // while writing the TelephonyProvider 354 return FAKE_SMS_DRAFT_URI; 355 } 356 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text); 357 } 358 359 @Override 360 public Uri addMultimediaMessageDraft(String callingPkg, byte[] pdu) throws RemoteException { 361 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft"); 362 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 363 callingPkg) != AppOpsManager.MODE_ALLOWED) { 364 // Silently fail AppOps failure due to not being the default SMS app 365 // while writing the TelephonyProvider 366 return FAKE_MMS_DRAFT_URI; 367 } 368 return getServiceGuarded().addMultimediaMessageDraft(callingPkg, pdu); 369 } 370 371 @Override 372 public void sendStoredMessage(long subId, String callingPkg, Uri messageUri, 373 PendingIntent sentIntent) throws RemoteException { 374 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, 375 "Send stored MMS message"); 376 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 377 callingPkg) != AppOpsManager.MODE_ALLOWED) { 378 return; 379 } 380 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, sentIntent); 381 } 382 383 @Override 384 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException { 385 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist"); 386 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 387 callingPkg) != AppOpsManager.MODE_ALLOWED) { 388 return; 389 } 390 getServiceGuarded().setAutoPersisting(callingPkg, enabled); 391 } 392 393 @Override 394 public boolean getAutoPersisting() throws RemoteException { 395 return getServiceGuarded().getAutoPersisting(); 396 } 397 } 398} 399