UsimPhoneBookManager.java revision cbaa45bbf2cab852b6c9c3a887e9f803d4e857ea
10825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/* 20825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Copyright (C) 2009 The Android Open Source Project 30825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 40825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Licensed under the Apache License, Version 2.0 (the "License"); 50825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * you may not use this file except in compliance with the License. 60825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * You may obtain a copy of the License at 70825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 80825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * http://www.apache.org/licenses/LICENSE-2.0 90825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unless required by applicable law or agreed to in writing, software 110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * distributed under the License is distributed on an "AS IS" BASIS, 120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * See the License for the specific language governing permissions and 140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * limitations under the License. 150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 170825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepackage com.android.internal.telephony.gsm; 180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 190825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.AsyncResult; 200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Handler; 210825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Message; 22ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Savilleimport android.telephony.Rlog; 230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 24d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenkaimport com.android.internal.telephony.uicc.AdnRecord; 25d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenkaimport com.android.internal.telephony.uicc.AdnRecordCache; 26d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenkaimport com.android.internal.telephony.uicc.IccConstants; 27d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenkaimport com.android.internal.telephony.uicc.IccFileHandler; 28d720945f2be5ea5fe0faf67e67d9ea0e184eba67Alex Yakavenkaimport com.android.internal.telephony.uicc.IccUtils; 290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 300825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.ArrayList; 310825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.HashMap; 320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Map; 330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/** 350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This class implements reading and parsing USIM records. 360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Refer to Spec 3GPP TS 31.102 for more details. 370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@hide} 390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class UsimPhoneBookManager extends Handler implements IccConstants { 41cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville private static final String LOG_TAG = "UsimPhoneBookManager"; 420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final boolean DBG = true; 430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private PbrFile mPbrFile; 440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Boolean mIsPbrPresent; 450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private IccFileHandler mFh; 460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private AdnRecordCache mAdnCache; 470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Object mLock = new Object(); 480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private ArrayList<AdnRecord> mPhoneBookRecords; 490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean mEmailPresentInIap = false; 500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private int mEmailTagNumberInIap = 0; 510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private ArrayList<byte[]> mIapFileRecord; 520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private ArrayList<byte[]> mEmailFileRecord; 530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Map<Integer, ArrayList<String>> mEmailsForAdnRec; 540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean mRefreshCache = false; 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int EVENT_PBR_LOAD_DONE = 1; 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int EVENT_USIM_ADN_LOAD_DONE = 2; 580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int EVENT_IAP_LOAD_DONE = 3; 590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int EVENT_EMAIL_LOAD_DONE = 4; 600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_TYPE1_TAG = 0xA8; 620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_TYPE2_TAG = 0xA9; 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_TYPE3_TAG = 0xAA; 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFADN_TAG = 0xC0; 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFIAP_TAG = 0xC1; 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFEXT1_TAG = 0xC2; 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFSNE_TAG = 0xC3; 680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFANR_TAG = 0xC4; 690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFPBC_TAG = 0xC5; 700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFGRP_TAG = 0xC6; 710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFAAS_TAG = 0xC7; 720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFGSD_TAG = 0xC8; 730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFUID_TAG = 0xC9; 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFEMAIL_TAG = 0xCA; 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int USIM_EFCCP1_TAG = 0xCB; 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public UsimPhoneBookManager(IccFileHandler fh, AdnRecordCache cache) { 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mFh = fh; 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPhoneBookRecords = new ArrayList<AdnRecord>(); 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPbrFile = null; 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // We assume its present, after the first read this is updated. 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // So we don't have to read from UICC if its not present on subsequent reads. 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIsPbrPresent = true; 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mAdnCache = cache; 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void reset() { 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPhoneBookRecords.clear(); 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIapFileRecord = null; 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mEmailFileRecord = null; 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPbrFile = null; 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIsPbrPresent = true; 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mRefreshCache = false; 940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public ArrayList<AdnRecord> loadEfFilesFromUsim() { 970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mLock) { 980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!mPhoneBookRecords.isEmpty()) { 990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mRefreshCache) { 1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mRefreshCache = false; 1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville refreshCache(); 1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mPhoneBookRecords; 1040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!mIsPbrPresent) return null; 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Check if the PBR file is present in the cache, if not read it 1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // from the USIM. 1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mPbrFile == null) { 1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville readPbrFileAndWait(); 1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mPbrFile == null) return null; 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int numRecs = mPbrFile.mFileIds.size(); 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < numRecs; i++) { 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville readAdnFileAndWait(i); 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville readEmailFileAndWait(i); 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // All EF files are loaded, post the response. 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mPhoneBookRecords; 1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void refreshCache() { 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mPbrFile == null) return; 1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPhoneBookRecords.clear(); 1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int numRecs = mPbrFile.mFileIds.size(); 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < numRecs; i++) { 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville readAdnFileAndWait(i); 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void invalidateCache() { 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mRefreshCache = true; 1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void readPbrFileAndWait() { 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mFh.loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE)); 1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLock.wait(); 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (InterruptedException e) { 145ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville Rlog.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait"); 1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void readEmailFileAndWait(int recNum) { 1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Map <Integer,Integer> fileIds; 1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville fileIds = mPbrFile.mFileIds.get(recNum); 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (fileIds == null) return; 1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (fileIds.containsKey(USIM_EFEMAIL_TAG)) { 1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int efid = fileIds.get(USIM_EFEMAIL_TAG); 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Check if the EFEmail is a Type 1 file or a type 2 file. 1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // If mEmailPresentInIap is true, its a type 2 file. 1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // So we read the IAP file and then read the email records. 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // instead of reading directly. 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mEmailPresentInIap) { 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville readIapFileAndWait(fileIds.get(USIM_EFIAP_TAG)); 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mIapFileRecord == null) { 163ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville Rlog.e(LOG_TAG, "Error: IAP file is empty"); 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Read the EFEmail file. 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mFh.loadEFLinearFixedAll(fileIds.get(USIM_EFEMAIL_TAG), 1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville obtainMessage(EVENT_EMAIL_LOAD_DONE)); 1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLock.wait(); 1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (InterruptedException e) { 173ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville Rlog.e(LOG_TAG, "Interrupted Exception in readEmailFileAndWait"); 1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mEmailFileRecord == null) { 177ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville Rlog.e(LOG_TAG, "Error: Email file is empty"); 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneAdnRecord(); 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void readIapFileAndWait(int efid) { 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mFh.loadEFLinearFixedAll(efid, obtainMessage(EVENT_IAP_LOAD_DONE)); 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLock.wait(); 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (InterruptedException e) { 190ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville Rlog.e(LOG_TAG, "Interrupted Exception in readIapFileAndWait"); 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void updatePhoneAdnRecord() { 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mEmailFileRecord == null) return; 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int numAdnRecs = mPhoneBookRecords.size(); 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mIapFileRecord != null) { 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // The number of records in the IAP file is same as the number of records in ADN file. 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // The order of the pointers in an EFIAP shall be the same as the order of file IDs 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // that appear in the TLV object indicated by Tag 'A9' in the reference file record. 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // i.e value of mEmailTagNumberInIap 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < numAdnRecs; i++) { 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[] record = null; 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville record = mIapFileRecord.get(i); 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (IndexOutOfBoundsException e) { 208ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville Rlog.e(LOG_TAG, "Error: Improper ICC card: No IAP record for ADN, continuing"); 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int recNum = record[mEmailTagNumberInIap]; 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (recNum != -1) { 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String[] emails = new String[1]; 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // SIM record numbers are 1 based 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville emails[0] = readEmailRecord(recNum - 1); 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AdnRecord rec = mPhoneBookRecords.get(i); 2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (rec != null) { 2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville rec.setEmails(emails); 2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // might be a record with only email 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville rec = new AdnRecord("", "", emails); 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPhoneBookRecords.set(i, rec); 2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // ICC cards can be made such that they have an IAP file but all 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // records are empty. So we read both type 1 and type 2 file 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // email records, just to be sure. 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int len = mPhoneBookRecords.size(); 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Type 1 file, the number of records is the same as the number of 2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // records in the ADN file. 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mEmailsForAdnRec == null) { 2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville parseType1EmailFile(len); 2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < numAdnRecs; i++) { 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ArrayList<String> emailList = null; 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville emailList = mEmailsForAdnRec.get(i); 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (IndexOutOfBoundsException e) { 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (emailList == null) continue; 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AdnRecord rec = mPhoneBookRecords.get(i); 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String[] emails = new String[emailList.size()]; 2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size()); 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville rec.setEmails(emails); 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPhoneBookRecords.set(i, rec); 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void parseType1EmailFile(int numRecs) { 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mEmailsForAdnRec = new HashMap<Integer, ArrayList<String>>(); 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[] emailRec = null; 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < numRecs; i++) { 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville emailRec = mEmailFileRecord.get(i); 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (IndexOutOfBoundsException e) { 264ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville Rlog.e(LOG_TAG, "Error: Improper ICC card: No email record for ADN, continuing"); 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int adnRecNum = emailRec[emailRec.length - 1]; 2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (adnRecNum == -1) { 2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville continue; 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String email = readEmailRecord(i); 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (email == null || email.equals("")) { 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville continue; 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // SIM record numbers are 1 based. 2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ArrayList<String> val = mEmailsForAdnRec.get(adnRecNum - 1); 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (val == null) { 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville val = new ArrayList<String>(); 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville val.add(email); 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // SIM record numbers are 1 based. 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mEmailsForAdnRec.put(adnRecNum - 1, val); 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String readEmailRecord(int recNum) { 2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[] emailRec = null; 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville emailRec = mEmailFileRecord.get(recNum); 2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (IndexOutOfBoundsException e) { 2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return null; 2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // The length of the record is X+2 byte, where X bytes is the email address 2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String email = IccUtils.adnStringFieldToString(emailRec, 0, emailRec.length - 2); 3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return email; 3010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void readAdnFileAndWait(int recNum) { 3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Map <Integer,Integer> fileIds; 3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville fileIds = mPbrFile.mFileIds.get(recNum); 3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (fileIds == null || fileIds.isEmpty()) return; 3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int extEf = 0; 3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Only call fileIds.get while EFEXT1_TAG is available 3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (fileIds.containsKey(USIM_EFEXT1_TAG)) { 3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville extEf = fileIds.get(USIM_EFEXT1_TAG); 3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mAdnCache.requestLoadAllAdnLike(fileIds.get(USIM_EFADN_TAG), 3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville extEf, obtainMessage(EVENT_USIM_ADN_LOAD_DONE)); 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLock.wait(); 3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (InterruptedException e) { 320ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville Rlog.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait"); 3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void createPbrFile(ArrayList<byte[]> records) { 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (records == null) { 3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPbrFile = null; 3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIsPbrPresent = false; 3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPbrFile = new PbrFile(records); 3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void handleMessage(Message msg) { 3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult ar; 3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch(msg.what) { 3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case EVENT_PBR_LOAD_DONE: 3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ar = (AsyncResult) msg.obj; 3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (ar.exception == null) { 3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville createPbrFile((ArrayList<byte[]>)ar.result); 3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mLock) { 3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLock.notify(); 3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case EVENT_USIM_ADN_LOAD_DONE: 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville log("Loading USIM ADN records done"); 3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ar = (AsyncResult) msg.obj; 3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (ar.exception == null) { 3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPhoneBookRecords.addAll((ArrayList<AdnRecord>)ar.result); 3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mLock) { 3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLock.notify(); 3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case EVENT_IAP_LOAD_DONE: 3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville log("Loading USIM IAP records done"); 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ar = (AsyncResult) msg.obj; 3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (ar.exception == null) { 3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIapFileRecord = ((ArrayList<byte[]>)ar.result); 3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mLock) { 3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLock.notify(); 3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case EVENT_EMAIL_LOAD_DONE: 3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville log("Loading USIM Email records done"); 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ar = (AsyncResult) msg.obj; 3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (ar.exception == null) { 3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mEmailFileRecord = ((ArrayList<byte[]>)ar.result); 3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mLock) { 3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLock.notify(); 3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class PbrFile { 3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // RecNum <EF Tag, efid> 3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville HashMap<Integer,Map<Integer,Integer>> mFileIds; 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville PbrFile(ArrayList<byte[]> records) { 3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mFileIds = new HashMap<Integer, Map<Integer, Integer>>(); 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SimTlv recTlv; 3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int recNum = 0; 3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (byte[] record: records) { 3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville recTlv = new SimTlv(record, 0, record.length); 3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville parseTag(recTlv, recNum); 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville recNum ++; 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void parseTag(SimTlv tlv, int recNum) { 3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SimTlv tlvEf; 3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int tag; 3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[] data; 4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Map<Integer, Integer> val = new HashMap<Integer, Integer>(); 4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville do { 4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tag = tlv.getTag(); 4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch(tag) { 4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_TYPE1_TAG: // A8 4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_TYPE3_TAG: // AA 4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_TYPE2_TAG: // A9 4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville data = tlv.getData(); 4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tlvEf = new SimTlv(data, 0, data.length); 4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville parseEf(tlvEf, val, tag); 4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } while (tlv.nextObject()); 4130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mFileIds.put(recNum, val); 4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void parseEf(SimTlv tlv, Map<Integer, Integer> val, int parentTag) { 4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int tag; 4180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[] data; 4190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int tagNumberWithinParentTag = 0; 4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville do { 4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tag = tlv.getTag(); 4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (parentTag == USIM_TYPE2_TAG && tag == USIM_EFEMAIL_TAG) { 4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mEmailPresentInIap = true; 4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mEmailTagNumberInIap = tagNumberWithinParentTag; 4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch(tag) { 4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFEMAIL_TAG: 4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFADN_TAG: 4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFEXT1_TAG: 4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFANR_TAG: 4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFPBC_TAG: 4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFGRP_TAG: 4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFAAS_TAG: 4340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFGSD_TAG: 4350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFUID_TAG: 4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFCCP1_TAG: 4370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFIAP_TAG: 4380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case USIM_EFSNE_TAG: 4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville data = tlv.getData(); 4400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int efid = ((data[0] & 0xFF) << 8) | (data[1] & 0xFF); 4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville val.put(tag, efid); 4420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tagNumberWithinParentTag ++; 4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } while(tlv.nextObject()); 4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void log(String msg) { 450ded9c0af7fa49504c047275ed34c2d3b22bf0c3aWink Saville if(DBG) Rlog.d(LOG_TAG, msg); 4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 453