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 17package com.android.internal.telephony.uicc; 18 19import android.os.*; 20 21import com.android.internal.telephony.CommandsInterface; 22 23import java.util.ArrayList; 24 25/** 26 * {@hide} 27 */ 28public abstract class IccFileHandler extends Handler implements IccConstants { 29 private static final boolean VDBG = false; 30 31 //from TS 11.11 9.1 or elsewhere 32 static protected final int COMMAND_READ_BINARY = 0xb0; 33 static protected final int COMMAND_UPDATE_BINARY = 0xd6; 34 static protected final int COMMAND_READ_RECORD = 0xb2; 35 static protected final int COMMAND_UPDATE_RECORD = 0xdc; 36 static protected final int COMMAND_SEEK = 0xa2; 37 static protected final int COMMAND_GET_RESPONSE = 0xc0; 38 39 // from TS 11.11 9.2.5 40 static protected final int READ_RECORD_MODE_ABSOLUTE = 4; 41 42 //***** types of files TS 11.11 9.3 43 static protected final int EF_TYPE_TRANSPARENT = 0; 44 static protected final int EF_TYPE_LINEAR_FIXED = 1; 45 static protected final int EF_TYPE_CYCLIC = 3; 46 47 //***** types of files TS 11.11 9.3 48 static protected final int TYPE_RFU = 0; 49 static protected final int TYPE_MF = 1; 50 static protected final int TYPE_DF = 2; 51 static protected final int TYPE_EF = 4; 52 53 // size of GET_RESPONSE for EF's 54 static protected final int GET_RESPONSE_EF_SIZE_BYTES = 15; 55 static protected final int GET_RESPONSE_EF_IMG_SIZE_BYTES = 10; 56 57 // Byte order received in response to COMMAND_GET_RESPONSE 58 // Refer TS 51.011 Section 9.2.1 59 static protected final int RESPONSE_DATA_RFU_1 = 0; 60 static protected final int RESPONSE_DATA_RFU_2 = 1; 61 62 static protected final int RESPONSE_DATA_FILE_SIZE_1 = 2; 63 static protected final int RESPONSE_DATA_FILE_SIZE_2 = 3; 64 65 static protected final int RESPONSE_DATA_FILE_ID_1 = 4; 66 static protected final int RESPONSE_DATA_FILE_ID_2 = 5; 67 static protected final int RESPONSE_DATA_FILE_TYPE = 6; 68 static protected final int RESPONSE_DATA_RFU_3 = 7; 69 static protected final int RESPONSE_DATA_ACCESS_CONDITION_1 = 8; 70 static protected final int RESPONSE_DATA_ACCESS_CONDITION_2 = 9; 71 static protected final int RESPONSE_DATA_ACCESS_CONDITION_3 = 10; 72 static protected final int RESPONSE_DATA_FILE_STATUS = 11; 73 static protected final int RESPONSE_DATA_LENGTH = 12; 74 static protected final int RESPONSE_DATA_STRUCTURE = 13; 75 static protected final int RESPONSE_DATA_RECORD_LENGTH = 14; 76 77 78 //***** Events 79 80 /** Finished retrieving size of transparent EF; start loading. */ 81 static protected final int EVENT_GET_BINARY_SIZE_DONE = 4; 82 /** Finished loading contents of transparent EF; post result. */ 83 static protected final int EVENT_READ_BINARY_DONE = 5; 84 /** Finished retrieving size of records for linear-fixed EF; now load. */ 85 static protected final int EVENT_GET_RECORD_SIZE_DONE = 6; 86 /** Finished loading single record from a linear-fixed EF; post result. */ 87 static protected final int EVENT_READ_RECORD_DONE = 7; 88 /** Finished retrieving record size; post result. */ 89 static protected final int EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE = 8; 90 /** Finished retrieving image instance record; post result. */ 91 static protected final int EVENT_READ_IMG_DONE = 9; 92 /** Finished retrieving icon data; post result. */ 93 static protected final int EVENT_READ_ICON_DONE = 10; 94 /** Finished retrieving size of record for EFimg now. */ 95 static protected final int EVENT_GET_RECORD_SIZE_IMG_DONE = 11; 96 97 // member variables 98 protected final CommandsInterface mCi; 99 protected final UiccCardApplication mParentApp; 100 protected final String mAid; 101 102 static class LoadLinearFixedContext { 103 104 int mEfid; 105 int mRecordNum, mRecordSize, mCountRecords; 106 boolean mLoadAll; 107 String mPath; 108 109 Message mOnLoaded; 110 111 ArrayList<byte[]> results; 112 113 LoadLinearFixedContext(int efid, int recordNum, Message onLoaded) { 114 mEfid = efid; 115 mRecordNum = recordNum; 116 mOnLoaded = onLoaded; 117 mLoadAll = false; 118 mPath = null; 119 } 120 121 LoadLinearFixedContext(int efid, int recordNum, String path, Message onLoaded) { 122 mEfid = efid; 123 mRecordNum = recordNum; 124 mOnLoaded = onLoaded; 125 mLoadAll = false; 126 mPath = path; 127 } 128 129 LoadLinearFixedContext(int efid, String path, Message onLoaded) { 130 mEfid = efid; 131 mRecordNum = 1; 132 mLoadAll = true; 133 mOnLoaded = onLoaded; 134 mPath = path; 135 } 136 137 LoadLinearFixedContext(int efid, Message onLoaded) { 138 mEfid = efid; 139 mRecordNum = 1; 140 mLoadAll = true; 141 mOnLoaded = onLoaded; 142 mPath = null; 143 } 144 } 145 146 /** 147 * Default constructor 148 */ 149 protected IccFileHandler(UiccCardApplication app, String aid, CommandsInterface ci) { 150 mParentApp = app; 151 mAid = aid; 152 mCi = ci; 153 } 154 155 public void dispose() { 156 } 157 158 //***** Public Methods 159 160 /** 161 * Load a record from a SIM Linear Fixed EF 162 * 163 * @param fileid EF id 164 * @param path Path of the EF on the card 165 * @param recordNum 1-based (not 0-based) record number 166 * @param onLoaded 167 * 168 * ((AsyncResult)(onLoaded.obj)).result is the byte[] 169 * 170 */ 171 public void loadEFLinearFixed(int fileid, String path, int recordNum, Message onLoaded) { 172 String efPath = (path == null) ? getEFPath(fileid) : path; 173 Message response 174 = obtainMessage(EVENT_GET_RECORD_SIZE_DONE, 175 new LoadLinearFixedContext(fileid, recordNum, efPath, onLoaded)); 176 177 mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath, 178 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response); 179 } 180 181 /** 182 * Load a record from a SIM Linear Fixed EF 183 * 184 * @param fileid EF id 185 * @param recordNum 1-based (not 0-based) record number 186 * @param onLoaded 187 * 188 * ((AsyncResult)(onLoaded.obj)).result is the byte[] 189 * 190 */ 191 public void loadEFLinearFixed(int fileid, int recordNum, Message onLoaded) { 192 loadEFLinearFixed(fileid, getEFPath(fileid), recordNum, onLoaded); 193 } 194 195 /** 196 * Load a image instance record from a SIM Linear Fixed EF-IMG 197 * 198 * @param recordNum 1-based (not 0-based) record number 199 * @param onLoaded 200 * 201 * ((AsyncResult)(onLoaded.obj)).result is the byte[] 202 * 203 */ 204 public void loadEFImgLinearFixed(int recordNum, Message onLoaded) { 205 Message response = obtainMessage(EVENT_GET_RECORD_SIZE_IMG_DONE, 206 new LoadLinearFixedContext(IccConstants.EF_IMG, recordNum, 207 onLoaded)); 208 209 mCi.iccIOForApp(COMMAND_GET_RESPONSE, IccConstants.EF_IMG, 210 getEFPath(IccConstants.EF_IMG), recordNum, 211 READ_RECORD_MODE_ABSOLUTE, GET_RESPONSE_EF_IMG_SIZE_BYTES, 212 null, null, mAid, response); 213 } 214 215 /** 216 * get record size for a linear fixed EF 217 * 218 * @param fileid EF id 219 * @param path Path of the EF on the card 220 * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[] 221 * int[0] is the record length int[1] is the total length of the EF 222 * file int[3] is the number of records in the EF file So int[0] * 223 * int[3] = int[1] 224 */ 225 public void getEFLinearRecordSize(int fileid, String path, Message onLoaded) { 226 String efPath = (path == null) ? getEFPath(fileid) : path; 227 Message response 228 = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE, 229 new LoadLinearFixedContext(fileid, efPath, onLoaded)); 230 mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath, 231 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response); 232 } 233 234 /** 235 * get record size for a linear fixed EF 236 * 237 * @param fileid EF id 238 * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[] 239 * int[0] is the record length int[1] is the total length of the EF 240 * file int[3] is the number of records in the EF file So int[0] * 241 * int[3] = int[1] 242 */ 243 public void getEFLinearRecordSize(int fileid, Message onLoaded) { 244 getEFLinearRecordSize(fileid, getEFPath(fileid), onLoaded); 245 } 246 247 /** 248 * Load all records from a SIM Linear Fixed EF 249 * 250 * @param fileid EF id 251 * @param path Path of the EF on the card 252 * @param onLoaded 253 * 254 * ((AsyncResult)(onLoaded.obj)).result is an ArrayList<byte[]> 255 * 256 */ 257 public void loadEFLinearFixedAll(int fileid, String path, Message onLoaded) { 258 String efPath = (path == null) ? getEFPath(fileid) : path; 259 Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE, 260 new LoadLinearFixedContext(fileid, efPath, onLoaded)); 261 262 mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath, 263 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response); 264 } 265 266 /** 267 * Load all records from a SIM Linear Fixed EF 268 * 269 * @param fileid EF id 270 * @param onLoaded 271 * 272 * ((AsyncResult)(onLoaded.obj)).result is an ArrayList<byte[]> 273 * 274 */ 275 public void loadEFLinearFixedAll(int fileid, Message onLoaded) { 276 loadEFLinearFixedAll(fileid, getEFPath(fileid), onLoaded); 277 } 278 279 /** 280 * Load a SIM Transparent EF 281 * 282 * @param fileid EF id 283 * @param onLoaded 284 * 285 * ((AsyncResult)(onLoaded.obj)).result is the byte[] 286 * 287 */ 288 289 public void loadEFTransparent(int fileid, Message onLoaded) { 290 Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE, 291 fileid, 0, onLoaded); 292 293 mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid), 294 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response); 295 } 296 297 /** 298 * Load first @size bytes from SIM Transparent EF 299 * 300 * @param fileid EF id 301 * @param size 302 * @param onLoaded 303 * 304 * ((AsyncResult)(onLoaded.obj)).result is the byte[] 305 * 306 */ 307 public void loadEFTransparent(int fileid, int size, Message onLoaded) { 308 Message response = obtainMessage(EVENT_READ_BINARY_DONE, 309 fileid, 0, onLoaded); 310 311 mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid), 312 0, 0, size, null, null, mAid, response); 313 } 314 315 /** 316 * Load a SIM Transparent EF-IMG. Used right after loadEFImgLinearFixed to 317 * retrive STK's icon data. 318 * 319 * @param fileid EF id 320 * @param onLoaded 321 * 322 * ((AsyncResult)(onLoaded.obj)).result is the byte[] 323 * 324 */ 325 public void loadEFImgTransparent(int fileid, int highOffset, int lowOffset, 326 int length, Message onLoaded) { 327 Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0, 328 onLoaded); 329 330 logd("IccFileHandler: loadEFImgTransparent fileid = " + fileid 331 + " filePath = " + getEFPath(EF_IMG) + " highOffset = " + highOffset 332 + " lowOffset = " + lowOffset + " length = " + length); 333 334 /* Per TS 31.102, for displaying of Icon, under 335 * DF Telecom and DF Graphics , EF instance(s) (4FXX,transparent files) 336 * are present. The possible image file identifiers (EF instance) for 337 * EF img ( 4F20, linear fixed file) are : 4F01 ... 4F05. 338 * It should be MF_SIM + DF_TELECOM + DF_GRAPHICS, same path as EF IMG 339 */ 340 mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(EF_IMG), 341 highOffset, lowOffset, length, null, null, mAid, response); 342 } 343 344 /** 345 * Update a record in a linear fixed EF 346 * @param fileid EF id 347 * @param path Path of the EF on the card 348 * @param recordNum 1-based (not 0-based) record number 349 * @param data must be exactly as long as the record in the EF 350 * @param pin2 for CHV2 operations, otherwist must be null 351 * @param onComplete onComplete.obj will be an AsyncResult 352 * onComplete.obj.userObj will be a IccIoResult on success 353 */ 354 public void updateEFLinearFixed(int fileid, String path, int recordNum, byte[] data, 355 String pin2, Message onComplete) { 356 String efPath = (path == null) ? getEFPath(fileid) : path; 357 mCi.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, efPath, 358 recordNum, READ_RECORD_MODE_ABSOLUTE, data.length, 359 IccUtils.bytesToHexString(data), pin2, mAid, onComplete); 360 } 361 362 /** 363 * Update a record in a linear fixed EF 364 * @param fileid EF id 365 * @param recordNum 1-based (not 0-based) record number 366 * @param data must be exactly as long as the record in the EF 367 * @param pin2 for CHV2 operations, otherwist must be null 368 * @param onComplete onComplete.obj will be an AsyncResult 369 * onComplete.obj.userObj will be a IccIoResult on success 370 */ 371 public void updateEFLinearFixed(int fileid, int recordNum, byte[] data, 372 String pin2, Message onComplete) { 373 mCi.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, getEFPath(fileid), 374 recordNum, READ_RECORD_MODE_ABSOLUTE, data.length, 375 IccUtils.bytesToHexString(data), pin2, mAid, onComplete); 376 } 377 378 /** 379 * Update a transparent EF 380 * @param fileid EF id 381 * @param data must be exactly as long as the EF 382 */ 383 public void updateEFTransparent(int fileid, byte[] data, Message onComplete) { 384 mCi.iccIOForApp(COMMAND_UPDATE_BINARY, fileid, getEFPath(fileid), 385 0, 0, data.length, 386 IccUtils.bytesToHexString(data), null, mAid, onComplete); 387 } 388 389 390 //***** Abstract Methods 391 392 393 //***** Private Methods 394 395 private void sendResult(Message response, Object result, Throwable ex) { 396 if (response == null) { 397 return; 398 } 399 400 AsyncResult.forMessage(response, result, ex); 401 402 response.sendToTarget(); 403 } 404 405 private boolean processException(Message response, AsyncResult ar) { 406 IccException iccException; 407 boolean flag = false; 408 IccIoResult result = (IccIoResult) ar.result; 409 if (ar.exception != null) { 410 sendResult(response, null, ar.exception); 411 flag = true; 412 } else { 413 iccException = result.getException(); 414 if (iccException != null) { 415 sendResult(response, null, iccException); 416 flag = true; 417 } 418 } 419 return flag; 420 } 421 422 //***** Overridden from Handler 423 424 @Override 425 public void handleMessage(Message msg) { 426 AsyncResult ar; 427 IccIoResult result; 428 Message response = null; 429 String str; 430 LoadLinearFixedContext lc; 431 432 byte data[]; 433 int size; 434 int fileid; 435 int recordSize[]; 436 String path = null; 437 438 try { 439 switch (msg.what) { 440 case EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE: 441 ar = (AsyncResult)msg.obj; 442 lc = (LoadLinearFixedContext) ar.userObj; 443 result = (IccIoResult) ar.result; 444 response = lc.mOnLoaded; 445 446 if (processException(response, (AsyncResult) msg.obj)) { 447 break; 448 } 449 450 data = result.payload; 451 452 if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE] || 453 EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) { 454 throw new IccFileTypeMismatch(); 455 } 456 457 recordSize = new int[3]; 458 recordSize[0] = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF; 459 recordSize[1] = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) 460 + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); 461 recordSize[2] = recordSize[1] / recordSize[0]; 462 463 sendResult(response, recordSize, null); 464 break; 465 466 case EVENT_GET_RECORD_SIZE_IMG_DONE: 467 case EVENT_GET_RECORD_SIZE_DONE: 468 ar = (AsyncResult)msg.obj; 469 lc = (LoadLinearFixedContext) ar.userObj; 470 result = (IccIoResult) ar.result; 471 response = lc.mOnLoaded; 472 473 if (processException(response, (AsyncResult) msg.obj)) { 474 loge("exception caught from EVENT_GET_RECORD_SIZE"); 475 break; 476 } 477 478 data = result.payload; 479 path = lc.mPath; 480 481 if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) { 482 throw new IccFileTypeMismatch(); 483 } 484 485 if (EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) { 486 throw new IccFileTypeMismatch(); 487 } 488 489 lc.mRecordSize = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF; 490 491 size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) 492 + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); 493 494 lc.mCountRecords = size / lc.mRecordSize; 495 496 if (lc.mLoadAll) { 497 lc.results = new ArrayList<byte[]>(lc.mCountRecords); 498 } 499 500 if (path == null) { 501 path = getEFPath(lc.mEfid); 502 } 503 mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, path, 504 lc.mRecordNum, 505 READ_RECORD_MODE_ABSOLUTE, 506 lc.mRecordSize, null, null, mAid, 507 obtainMessage(EVENT_READ_RECORD_DONE, lc)); 508 break; 509 case EVENT_GET_BINARY_SIZE_DONE: 510 ar = (AsyncResult)msg.obj; 511 response = (Message) ar.userObj; 512 result = (IccIoResult) ar.result; 513 514 if (processException(response, (AsyncResult) msg.obj)) { 515 break; 516 } 517 518 data = result.payload; 519 520 fileid = msg.arg1; 521 522 if (VDBG) { 523 logd(String.format("Contents of the Select Response for command %x: ", fileid) 524 + IccUtils.bytesToHexString(data)); 525 } 526 527 if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) { 528 throw new IccFileTypeMismatch(); 529 } 530 531 if (EF_TYPE_TRANSPARENT != data[RESPONSE_DATA_STRUCTURE]) { 532 throw new IccFileTypeMismatch(); 533 } 534 535 size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) 536 + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); 537 538 mCi.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid), 539 0, 0, size, null, null, mAid, 540 obtainMessage(EVENT_READ_BINARY_DONE, 541 fileid, 0, response)); 542 break; 543 544 case EVENT_READ_IMG_DONE: 545 case EVENT_READ_RECORD_DONE: 546 547 ar = (AsyncResult)msg.obj; 548 lc = (LoadLinearFixedContext) ar.userObj; 549 result = (IccIoResult) ar.result; 550 response = lc.mOnLoaded; 551 path = lc.mPath; 552 553 if (processException(response, (AsyncResult) msg.obj)) { 554 break; 555 } 556 557 if (!lc.mLoadAll) { 558 sendResult(response, result.payload, null); 559 } else { 560 lc.results.add(result.payload); 561 562 lc.mRecordNum++; 563 564 if (lc.mRecordNum > lc.mCountRecords) { 565 sendResult(response, lc.results, null); 566 } else { 567 if (path == null) { 568 path = getEFPath(lc.mEfid); 569 } 570 571 mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, path, 572 lc.mRecordNum, 573 READ_RECORD_MODE_ABSOLUTE, 574 lc.mRecordSize, null, null, mAid, 575 obtainMessage(EVENT_READ_RECORD_DONE, lc)); 576 } 577 } 578 579 break; 580 581 case EVENT_READ_BINARY_DONE: 582 case EVENT_READ_ICON_DONE: 583 ar = (AsyncResult)msg.obj; 584 response = (Message) ar.userObj; 585 result = (IccIoResult) ar.result; 586 587 if (processException(response, (AsyncResult) msg.obj)) { 588 break; 589 } 590 591 sendResult(response, result.payload, null); 592 break; 593 594 }} catch (Exception exc) { 595 if (response != null) { 596 sendResult(response, null, exc); 597 } else { 598 loge("uncaught exception" + exc); 599 } 600 } 601 } 602 603 /** 604 * Returns the root path of the EF file. 605 * i.e returns MasterFile + DFfile as a string. 606 * Ex: For EF_ADN on a SIM, it will return "3F007F10" 607 * This function handles only EFids that are common to 608 * RUIM, SIM, USIM and other types of Icc cards. 609 * 610 * @param efid of path to retrieve 611 * @return root path of the file. 612 */ 613 protected String getCommonIccEFPath(int efid) { 614 switch(efid) { 615 case EF_ADN: 616 case EF_FDN: 617 case EF_MSISDN: 618 case EF_SDN: 619 case EF_EXT1: 620 case EF_EXT2: 621 case EF_EXT3: 622 case EF_PSI: 623 return MF_SIM + DF_TELECOM; 624 625 case EF_ICCID: 626 case EF_PL: 627 return MF_SIM; 628 case EF_PBR: 629 // we only support global phonebook. 630 return MF_SIM + DF_TELECOM + DF_PHONEBOOK; 631 case EF_IMG: 632 return MF_SIM + DF_TELECOM + DF_GRAPHICS; 633 } 634 return null; 635 } 636 637 protected abstract String getEFPath(int efid); 638 protected abstract void logd(String s); 639 protected abstract void loge(String s); 640 641} 642