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