MmsServiceBroker.java revision 63c00c4379ba4ac0f50161980b67c8acc2548f7b
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 ContentValues configOverrides, 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, configOverrides, 201 sentIntent); 202 } 203 204 @Override 205 public void downloadMessage(long subId, String callingPkg, String locationUrl, 206 ContentValues configOverrides, PendingIntent downloadedIntent) 207 throws RemoteException { 208 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS, 209 "Download MMS message"); 210 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(), 211 callingPkg) != AppOpsManager.MODE_ALLOWED) { 212 return; 213 } 214 getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, configOverrides, 215 downloadedIntent); 216 } 217 218 @Override 219 public void updateMmsSendStatus(int messageRef, boolean success) throws RemoteException { 220 enforceCarrierPrivilege(); 221 getServiceGuarded().updateMmsSendStatus(messageRef, success); 222 } 223 224 @Override 225 public void updateMmsDownloadStatus(int messageRef, byte[] pdu) throws RemoteException { 226 enforceCarrierPrivilege(); 227 getServiceGuarded().updateMmsDownloadStatus(messageRef, pdu); 228 } 229 230 @Override 231 public boolean getCarrierConfigBoolean(long subId, String name, boolean defaultValue) 232 throws RemoteException { 233 return getServiceGuarded().getCarrierConfigBoolean(subId, name, defaultValue); 234 } 235 236 @Override 237 public int getCarrierConfigInt(long subId, String name, int defaultValue) 238 throws RemoteException { 239 return getServiceGuarded().getCarrierConfigInt(subId, name, defaultValue); 240 } 241 242 @Override 243 public String getCarrierConfigString(long subId, String name, String defaultValue) 244 throws RemoteException { 245 return getServiceGuarded().getCarrierConfigString(subId, name, defaultValue); 246 } 247 248 @Override 249 public Uri importTextMessage(String callingPkg, String address, int type, String text, 250 long timestampMillis, boolean seen, boolean read) throws RemoteException { 251 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message"); 252 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 253 callingPkg) != AppOpsManager.MODE_ALLOWED) { 254 // Silently fail AppOps failure due to not being the default SMS app 255 // while writing the TelephonyProvider 256 return FAKE_SMS_SENT_URI; 257 } 258 return getServiceGuarded().importTextMessage( 259 callingPkg, address, type, text, timestampMillis, seen, read); 260 } 261 262 @Override 263 public Uri importMultimediaMessage(String callingPkg, byte[] pdu, String messageId, 264 long timestampSecs, boolean seen, boolean read) throws RemoteException { 265 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message"); 266 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 267 callingPkg) != AppOpsManager.MODE_ALLOWED) { 268 // Silently fail AppOps failure due to not being the default SMS app 269 // while writing the TelephonyProvider 270 return FAKE_MMS_SENT_URI; 271 } 272 return getServiceGuarded().importMultimediaMessage( 273 callingPkg, pdu, messageId, timestampSecs, seen, read); 274 } 275 276 @Override 277 public boolean deleteStoredMessage(String callingPkg, Uri messageUri) 278 throws RemoteException { 279 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 280 "Delete SMS/MMS message"); 281 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 282 callingPkg) != AppOpsManager.MODE_ALLOWED) { 283 return false; 284 } 285 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri); 286 } 287 288 @Override 289 public boolean deleteStoredConversation(String callingPkg, long conversationId) 290 throws RemoteException { 291 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation"); 292 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 293 callingPkg) != AppOpsManager.MODE_ALLOWED) { 294 return false; 295 } 296 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId); 297 } 298 299 @Override 300 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri, 301 ContentValues statusValues) throws RemoteException { 302 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 303 "Update SMS/MMS message"); 304 return getServiceGuarded() 305 .updateStoredMessageStatus(callingPkg, messageUri, statusValues); 306 } 307 308 @Override 309 public boolean archiveStoredConversation(String callingPkg, long conversationId, 310 boolean archived) throws RemoteException { 311 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, 312 "Update SMS/MMS message"); 313 return getServiceGuarded() 314 .archiveStoredConversation(callingPkg, conversationId, archived); 315 } 316 317 @Override 318 public Uri addTextMessageDraft(String callingPkg, String address, String text) 319 throws RemoteException { 320 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft"); 321 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 322 callingPkg) != AppOpsManager.MODE_ALLOWED) { 323 // Silently fail AppOps failure due to not being the default SMS app 324 // while writing the TelephonyProvider 325 return FAKE_SMS_DRAFT_URI; 326 } 327 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text); 328 } 329 330 @Override 331 public Uri addMultimediaMessageDraft(String callingPkg, byte[] pdu) throws RemoteException { 332 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft"); 333 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 334 callingPkg) != AppOpsManager.MODE_ALLOWED) { 335 // Silently fail AppOps failure due to not being the default SMS app 336 // while writing the TelephonyProvider 337 return FAKE_MMS_DRAFT_URI; 338 } 339 return getServiceGuarded().addMultimediaMessageDraft(callingPkg, pdu); 340 } 341 342 @Override 343 public void sendStoredMessage(long subId, String callingPkg, Uri messageUri, 344 ContentValues configOverrides, PendingIntent sentIntent) throws RemoteException { 345 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, 346 "Send stored MMS message"); 347 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 348 callingPkg) != AppOpsManager.MODE_ALLOWED) { 349 return; 350 } 351 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides, 352 sentIntent); 353 } 354 355 @Override 356 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException { 357 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist"); 358 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 359 callingPkg) != AppOpsManager.MODE_ALLOWED) { 360 return; 361 } 362 getServiceGuarded().setAutoPersisting(callingPkg, enabled); 363 } 364 365 @Override 366 public boolean getAutoPersisting() throws RemoteException { 367 return getServiceGuarded().getAutoPersisting(); 368 } 369 } 370} 371