1 /* 2 * Copyright (C) 2008 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.internal.telephony; 18 19 import android.Manifest; 20 import android.app.AppOpsManager; 21 import android.app.PendingIntent; 22 import android.content.Context; 23 import android.os.AsyncResult; 24 import android.os.Binder; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.telephony.Rlog; 28 import android.util.Log; 29 30 import com.android.internal.telephony.uicc.IccConstants; 31 import com.android.internal.telephony.uicc.IccFileHandler; 32 import com.android.internal.util.HexDump; 33 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.List; 37 38 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; 39 import static android.telephony.SmsManager.STATUS_ON_ICC_READ; 40 import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD; 41 42 /** 43 * IccSmsInterfaceManager to provide an inter-process communication to 44 * access Sms in Icc. 45 */ 46 public abstract class IccSmsInterfaceManager extends ISms.Stub { 47 static final String LOG_TAG = "IccSmsInterfaceManager"; 48 static final boolean DBG = true; 49 50 protected final Object mLock = new Object(); 51 protected boolean mSuccess; 52 private List<SmsRawData> mSms; 53 54 private static final int EVENT_LOAD_DONE = 1; 55 private static final int EVENT_UPDATE_DONE = 2; 56 protected static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3; 57 protected static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4; 58 59 final protected PhoneBase mPhone; 60 final protected Context mContext; 61 final protected AppOpsManager mAppOps; 62 protected SMSDispatcher mDispatcher; 63 64 protected Handler mHandler = new Handler() { 65 @Override 66 public void handleMessage(Message msg) { 67 AsyncResult ar; 68 69 switch (msg.what) { 70 case EVENT_UPDATE_DONE: 71 ar = (AsyncResult) msg.obj; 72 synchronized (mLock) { 73 mSuccess = (ar.exception == null); 74 mLock.notifyAll(); 75 } 76 break; 77 case EVENT_LOAD_DONE: 78 ar = (AsyncResult)msg.obj; 79 synchronized (mLock) { 80 if (ar.exception == null) { 81 mSms = buildValidRawData((ArrayList<byte[]>) ar.result); 82 //Mark SMS as read after importing it from card. 83 markMessagesAsRead((ArrayList<byte[]>) ar.result); 84 } else { 85 if(DBG) log("Cannot load Sms records"); 86 if (mSms != null) 87 mSms.clear(); 88 } 89 mLock.notifyAll(); 90 } 91 break; 92 case EVENT_SET_BROADCAST_ACTIVATION_DONE: 93 case EVENT_SET_BROADCAST_CONFIG_DONE: 94 ar = (AsyncResult) msg.obj; 95 synchronized (mLock) { 96 mSuccess = (ar.exception == null); 97 mLock.notifyAll(); 98 } 99 break; 100 } 101 } 102 }; 103 104 protected IccSmsInterfaceManager(PhoneBase phone){ 105 mPhone = phone; 106 mContext = phone.getContext(); 107 mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); 108 } 109 110 protected void markMessagesAsRead(ArrayList<byte[]> messages) { 111 if (messages == null) { 112 return; 113 } 114 115 //IccFileHandler can be null, if icc card is absent. 116 IccFileHandler fh = mPhone.getIccFileHandler(); 117 if (fh == null) { 118 //shouldn't really happen, as messages are marked as read, only 119 //after importing it from icc. 120 if (Rlog.isLoggable("SMS", Log.DEBUG)) { 121 log("markMessagesAsRead - aborting, no icc card present."); 122 } 123 return; 124 } 125 126 int count = messages.size(); 127 128 for (int i = 0; i < count; i++) { 129 byte[] ba = messages.get(i); 130 if (ba[0] == STATUS_ON_ICC_UNREAD) { 131 int n = ba.length; 132 byte[] nba = new byte[n - 1]; 133 System.arraycopy(ba, 1, nba, 0, n - 1); 134 byte[] record = makeSmsRecordData(STATUS_ON_ICC_READ, nba); 135 fh.updateEFLinearFixed(IccConstants.EF_SMS, i + 1, record, null, null); 136 if (Rlog.isLoggable("SMS", Log.DEBUG)) { 137 log("SMS " + (i + 1) + " marked as read"); 138 } 139 } 140 } 141 } 142 143 protected void enforceReceiveAndSend(String message) { 144 mContext.enforceCallingPermission( 145 Manifest.permission.RECEIVE_SMS, message); 146 mContext.enforceCallingPermission( 147 Manifest.permission.SEND_SMS, message); 148 } 149 150 /** 151 * Update the specified message on the Icc. 152 * 153 * @param index record index of message to update 154 * @param status new message status (STATUS_ON_ICC_READ, 155 * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, 156 * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE) 157 * @param pdu the raw PDU to store 158 * @return success or not 159 * 160 */ 161 @Override 162 public boolean 163 updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu) { 164 if (DBG) log("updateMessageOnIccEf: index=" + index + 165 " status=" + status + " ==> " + 166 "("+ Arrays.toString(pdu) + ")"); 167 enforceReceiveAndSend("Updating message on Icc"); 168 if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(), 169 callingPackage) != AppOpsManager.MODE_ALLOWED) { 170 return false; 171 } 172 synchronized(mLock) { 173 mSuccess = false; 174 Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); 175 176 if (status == STATUS_ON_ICC_FREE) { 177 // RIL_REQUEST_DELETE_SMS_ON_SIM vs RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM 178 // Special case FREE: call deleteSmsOnSim/Ruim instead of 179 // manipulating the record 180 // Will eventually fail if icc card is not present. 181 deleteSms(index, response); 182 } else { 183 //IccFilehandler can be null if ICC card is not present. 184 IccFileHandler fh = mPhone.getIccFileHandler(); 185 if (fh == null) { 186 response.recycle(); 187 return mSuccess; /* is false */ 188 } 189 byte[] record = makeSmsRecordData(status, pdu); 190 fh.updateEFLinearFixed( 191 IccConstants.EF_SMS, 192 index, record, null, response); 193 } 194 try { 195 mLock.wait(); 196 } catch (InterruptedException e) { 197 log("interrupted while trying to update by index"); 198 } 199 } 200 return mSuccess; 201 } 202 203 /** 204 * Copy a raw SMS PDU to the Icc. 205 * 206 * @param pdu the raw PDU to store 207 * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, 208 * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) 209 * @return success or not 210 * 211 */ 212 @Override 213 public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc) { 214 //NOTE smsc not used in RUIM 215 if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " + 216 "pdu=("+ Arrays.toString(pdu) + 217 "), smsc=(" + Arrays.toString(smsc) +")"); 218 enforceReceiveAndSend("Copying message to Icc"); 219 if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(), 220 callingPackage) != AppOpsManager.MODE_ALLOWED) { 221 return false; 222 } 223 synchronized(mLock) { 224 mSuccess = false; 225 Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); 226 227 //RIL_REQUEST_WRITE_SMS_TO_SIM vs RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM 228 writeSms(status, smsc, pdu, response); 229 230 try { 231 mLock.wait(); 232 } catch (InterruptedException e) { 233 log("interrupted while trying to update by index"); 234 } 235 } 236 return mSuccess; 237 } 238 239 /** 240 * Retrieves all messages currently stored on Icc. 241 * 242 * @return list of SmsRawData of all sms on Icc 243 */ 244 @Override 245 public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) { 246 if (DBG) log("getAllMessagesFromEF"); 247 248 mContext.enforceCallingPermission( 249 Manifest.permission.RECEIVE_SMS, 250 "Reading messages from Icc"); 251 if (mAppOps.noteOp(AppOpsManager.OP_READ_ICC_SMS, Binder.getCallingUid(), 252 callingPackage) != AppOpsManager.MODE_ALLOWED) { 253 return new ArrayList<SmsRawData>(); 254 } 255 synchronized(mLock) { 256 257 IccFileHandler fh = mPhone.getIccFileHandler(); 258 if (fh == null) { 259 Rlog.e(LOG_TAG, "Cannot load Sms records. No icc card?"); 260 if (mSms != null) { 261 mSms.clear(); 262 return mSms; 263 } 264 } 265 266 Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); 267 fh.loadEFLinearFixedAll(IccConstants.EF_SMS, response); 268 269 try { 270 mLock.wait(); 271 } catch (InterruptedException e) { 272 log("interrupted while trying to load from the Icc"); 273 } 274 } 275 return mSms; 276 } 277 278 /** 279 * Send a data based SMS to a specific application port. 280 * 281 * @param destAddr the address to send the message to 282 * @param scAddr is the service center address or null to use 283 * the current default SMSC 284 * @param destPort the port to deliver the message to 285 * @param data the body of the message to send 286 * @param sentIntent if not NULL this <code>PendingIntent</code> is 287 * broadcast when the message is successfully sent, or failed. 288 * The result code will be <code>Activity.RESULT_OK<code> for success, 289 * or one of these errors:<br> 290 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 291 * <code>RESULT_ERROR_RADIO_OFF</code><br> 292 * <code>RESULT_ERROR_NULL_PDU</code><br> 293 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 294 * the extra "errorCode" containing a radio technology specific value, 295 * generally only useful for troubleshooting.<br> 296 * The per-application based SMS control checks sentIntent. If sentIntent 297 * is NULL the caller will be checked against all unknown applications, 298 * which cause smaller number of SMS to be sent in checking period. 299 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 300 * broadcast when the message is delivered to the recipient. The 301 * raw pdu of the status report is in the extended data ("pdu"). 302 */ 303 @Override 304 public void sendData(String callingPackage, String destAddr, String scAddr, int destPort, 305 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 306 mPhone.getContext().enforceCallingPermission( 307 Manifest.permission.SEND_SMS, 308 "Sending SMS message"); 309 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 310 log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" + 311 destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" + 312 sentIntent + " deliveryIntent=" + deliveryIntent); 313 } 314 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 315 callingPackage) != AppOpsManager.MODE_ALLOWED) { 316 return; 317 } 318 mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); 319 } 320 321 /** 322 * Send a text based SMS. 323 * 324 * @param destAddr the address to send the message to 325 * @param scAddr is the service center address or null to use 326 * the current default SMSC 327 * @param text the body of the message to send 328 * @param sentIntent if not NULL this <code>PendingIntent</code> is 329 * broadcast when the message is successfully sent, or failed. 330 * The result code will be <code>Activity.RESULT_OK<code> for success, 331 * or one of these errors:<br> 332 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 333 * <code>RESULT_ERROR_RADIO_OFF</code><br> 334 * <code>RESULT_ERROR_NULL_PDU</code><br> 335 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 336 * the extra "errorCode" containing a radio technology specific value, 337 * generally only useful for troubleshooting.<br> 338 * The per-application based SMS control checks sentIntent. If sentIntent 339 * is NULL the caller will be checked against all unknown applications, 340 * which cause smaller number of SMS to be sent in checking period. 341 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 342 * broadcast when the message is delivered to the recipient. The 343 * raw pdu of the status report is in the extended data ("pdu"). 344 */ 345 @Override 346 public void sendText(String callingPackage, String destAddr, String scAddr, 347 String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { 348 mPhone.getContext().enforceCallingPermission( 349 Manifest.permission.SEND_SMS, 350 "Sending SMS message"); 351 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 352 log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr + 353 " text='"+ text + "' sentIntent=" + 354 sentIntent + " deliveryIntent=" + deliveryIntent); 355 } 356 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 357 callingPackage) != AppOpsManager.MODE_ALLOWED) { 358 return; 359 } 360 mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent); 361 } 362 363 /** 364 * Send a multi-part text based SMS. 365 * 366 * @param destAddr the address to send the message to 367 * @param scAddr is the service center address or null to use 368 * the current default SMSC 369 * @param parts an <code>ArrayList</code> of strings that, in order, 370 * comprise the original message 371 * @param sentIntents if not null, an <code>ArrayList</code> of 372 * <code>PendingIntent</code>s (one for each message part) that is 373 * broadcast when the corresponding message part has been sent. 374 * The result code will be <code>Activity.RESULT_OK<code> for success, 375 * or one of these errors: 376 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 377 * <code>RESULT_ERROR_RADIO_OFF</code> 378 * <code>RESULT_ERROR_NULL_PDU</code>. 379 * The per-application based SMS control checks sentIntent. If sentIntent 380 * is NULL the caller will be checked against all unknown applications, 381 * which cause smaller number of SMS to be sent in checking period. 382 * @param deliveryIntents if not null, an <code>ArrayList</code> of 383 * <code>PendingIntent</code>s (one for each message part) that is 384 * broadcast when the corresponding message part has been delivered 385 * to the recipient. The raw pdu of the status report is in the 386 * extended data ("pdu"). 387 */ 388 @Override 389 public void sendMultipartText(String callingPackage, String destAddr, String scAddr, 390 List<String> parts, List<PendingIntent> sentIntents, 391 List<PendingIntent> deliveryIntents) { 392 mPhone.getContext().enforceCallingPermission( 393 Manifest.permission.SEND_SMS, 394 "Sending SMS message"); 395 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 396 int i = 0; 397 for (String part : parts) { 398 log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr + 399 ", part[" + (i++) + "]=" + part); 400 } 401 } 402 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 403 callingPackage) != AppOpsManager.MODE_ALLOWED) { 404 return; 405 } 406 mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts, 407 (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents); 408 } 409 410 @Override 411 public int getPremiumSmsPermission(String packageName) { 412 return mDispatcher.getPremiumSmsPermission(packageName); 413 } 414 415 @Override 416 public void setPremiumSmsPermission(String packageName, int permission) { 417 mDispatcher.setPremiumSmsPermission(packageName, permission); 418 } 419 420 /** 421 * create SmsRawData lists from all sms record byte[] 422 * Use null to indicate "free" record 423 * 424 * @param messages List of message records from EF_SMS. 425 * @return SmsRawData list of all in-used records 426 */ 427 protected ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) { 428 int count = messages.size(); 429 ArrayList<SmsRawData> ret; 430 431 ret = new ArrayList<SmsRawData>(count); 432 433 for (int i = 0; i < count; i++) { 434 byte[] ba = messages.get(i); 435 if (ba[0] == STATUS_ON_ICC_FREE) { 436 ret.add(null); 437 } else { 438 ret.add(new SmsRawData(messages.get(i))); 439 } 440 } 441 442 return ret; 443 } 444 445 /** 446 * Generates an EF_SMS record from status and raw PDU. 447 * 448 * @param status Message status. See TS 51.011 10.5.3. 449 * @param pdu Raw message PDU. 450 * @return byte array for the record. 451 */ 452 protected byte[] makeSmsRecordData(int status, byte[] pdu) { 453 byte[] data = new byte[IccConstants.SMS_RECORD_LENGTH]; 454 455 // Status bits for this record. See TS 51.011 10.5.3 456 data[0] = (byte)(status & 7); 457 458 System.arraycopy(pdu, 0, data, 1, pdu.length); 459 460 // Pad out with 0xFF's. 461 for (int j = pdu.length+1; j < IccConstants.SMS_RECORD_LENGTH; j++) { 462 data[j] = -1; 463 } 464 465 return data; 466 } 467 468 protected abstract void deleteSms(int index, Message response); 469 470 protected abstract void writeSms(int status, byte[] pdu, byte[] smsc, Message response); 471 472 protected abstract void log(String msg); 473 474 } 475