1 /* 2 * Copyright (C) 2011 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.uicc; 18 19 import android.content.Context; 20 import android.os.AsyncResult; 21 import android.os.Handler; 22 import android.os.Message; 23 import android.telephony.Rlog; 24 25 import com.android.internal.telephony.CommandsInterface; 26 import com.android.internal.telephony.gsm.SimTlv; 27 //import com.android.internal.telephony.gsm.VoiceMailConstants; 28 29 import java.io.FileDescriptor; 30 import java.io.PrintWriter; 31 import java.nio.charset.Charset; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 35 import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN; 36 import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI; 37 import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU; 38 39 /** 40 * {@hide} 41 */ 42 public final class IsimUiccRecords extends IccRecords implements IsimRecords { 43 protected static final String LOG_TAG = "IsimUiccRecords"; 44 45 private static final boolean DBG = true; 46 private static final boolean DUMP_RECORDS = false; // Note: PII is logged when this is true 47 48 private static final int EVENT_APP_READY = 1; 49 50 // ISIM EF records (see 3GPP TS 31.103) 51 private String mIsimImpi; // IMS private user identity 52 private String mIsimDomain; // IMS home network domain name 53 private String[] mIsimImpu; // IMS public user identity(s) 54 55 private static final int TAG_ISIM_VALUE = 0x80; // From 3GPP TS 31.103 56 57 @Override 58 public String toString() { 59 return "IsimUiccRecords: " + super.toString() 60 + " mIsimImpi=" + mIsimImpi 61 + " mIsimDomain=" + mIsimDomain 62 + " mIsimImpu=" + mIsimImpu; 63 } 64 65 public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) { 66 super(app, c, ci); 67 68 mRecordsRequested = false; // No load request is made till SIM ready 69 70 // recordsToLoad is set to 0 because no requests are made yet 71 mRecordsToLoad = 0; 72 73 mParentApp.registerForReady(this, EVENT_APP_READY, null); 74 if (DBG) log("IsimUiccRecords X ctor this=" + this); 75 } 76 77 @Override 78 public void dispose() { 79 log("Disposing " + this); 80 //Unregister for all events 81 mParentApp.unregisterForReady(this); 82 resetRecords(); 83 super.dispose(); 84 } 85 86 // ***** Overridden from Handler 87 public void handleMessage(Message msg) { 88 if (mDestroyed.get()) { 89 Rlog.e(LOG_TAG, "Received message " + msg + 90 "[" + msg.what + "] while being destroyed. Ignoring."); 91 return; 92 } 93 94 try { 95 switch (msg.what) { 96 case EVENT_APP_READY: 97 onReady(); 98 break; 99 100 default: 101 super.handleMessage(msg); // IccRecords handles generic record load responses 102 103 } 104 } catch (RuntimeException exc) { 105 // I don't want these exceptions to be fatal 106 Rlog.w(LOG_TAG, "Exception parsing SIM record", exc); 107 } 108 } 109 110 protected void fetchIsimRecords() { 111 mRecordsRequested = true; 112 113 mFh.loadEFTransparent(EF_IMPI, obtainMessage( 114 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded())); 115 mRecordsToLoad++; 116 117 mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage( 118 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded())); 119 mRecordsToLoad++; 120 121 mFh.loadEFTransparent(EF_DOMAIN, obtainMessage( 122 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded())); 123 mRecordsToLoad++; 124 125 log("fetchIsimRecords " + mRecordsToLoad); 126 } 127 128 protected void resetRecords() { 129 // recordsRequested is set to false indicating that the SIM 130 // read requests made so far are not valid. This is set to 131 // true only when fresh set of read requests are made. 132 mRecordsRequested = false; 133 } 134 135 private class EfIsimImpiLoaded implements IccRecords.IccRecordLoaded { 136 public String getEfName() { 137 return "EF_ISIM_IMPI"; 138 } 139 public void onRecordLoaded(AsyncResult ar) { 140 byte[] data = (byte[]) ar.result; 141 mIsimImpi = isimTlvToString(data); 142 if (DUMP_RECORDS) log("EF_IMPI=" + mIsimImpi); 143 } 144 } 145 146 private class EfIsimImpuLoaded implements IccRecords.IccRecordLoaded { 147 public String getEfName() { 148 return "EF_ISIM_IMPU"; 149 } 150 public void onRecordLoaded(AsyncResult ar) { 151 ArrayList<byte[]> impuList = (ArrayList<byte[]>) ar.result; 152 if (DBG) log("EF_IMPU record count: " + impuList.size()); 153 mIsimImpu = new String[impuList.size()]; 154 int i = 0; 155 for (byte[] identity : impuList) { 156 String impu = isimTlvToString(identity); 157 if (DUMP_RECORDS) log("EF_IMPU[" + i + "]=" + impu); 158 mIsimImpu[i++] = impu; 159 } 160 } 161 } 162 163 private class EfIsimDomainLoaded implements IccRecords.IccRecordLoaded { 164 public String getEfName() { 165 return "EF_ISIM_DOMAIN"; 166 } 167 public void onRecordLoaded(AsyncResult ar) { 168 byte[] data = (byte[]) ar.result; 169 mIsimDomain = isimTlvToString(data); 170 if (DUMP_RECORDS) log("EF_DOMAIN=" + mIsimDomain); 171 } 172 } 173 174 /** 175 * ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string 176 * with tag value 0x80. 177 * @param record the byte array containing the IMS data string 178 * @return the decoded String value, or null if the record can't be decoded 179 */ 180 private static String isimTlvToString(byte[] record) { 181 SimTlv tlv = new SimTlv(record, 0, record.length); 182 do { 183 if (tlv.getTag() == TAG_ISIM_VALUE) { 184 return new String(tlv.getData(), Charset.forName("UTF-8")); 185 } 186 } while (tlv.nextObject()); 187 188 Rlog.e(LOG_TAG, "[ISIM] can't find TLV tag in ISIM record, returning null"); 189 return null; 190 } 191 192 @Override 193 protected void onRecordLoaded() { 194 // One record loaded successfully or failed, In either case 195 // we need to update the recordsToLoad count 196 mRecordsToLoad -= 1; 197 198 if (mRecordsToLoad == 0 && mRecordsRequested == true) { 199 onAllRecordsLoaded(); 200 } else if (mRecordsToLoad < 0) { 201 loge("recordsToLoad <0, programmer error suspected"); 202 mRecordsToLoad = 0; 203 } 204 } 205 206 @Override 207 protected void onAllRecordsLoaded() { 208 mRecordsLoadedRegistrants.notifyRegistrants( 209 new AsyncResult(null, null, null)); 210 } 211 212 /** 213 * Return the IMS private user identity (IMPI). 214 * Returns null if the IMPI hasn't been loaded or isn't present on the ISIM. 215 * @return the IMS private user identity string, or null if not available 216 */ 217 @Override 218 public String getIsimImpi() { 219 return mIsimImpi; 220 } 221 222 /** 223 * Return the IMS home network domain name. 224 * Returns null if the IMS domain hasn't been loaded or isn't present on the ISIM. 225 * @return the IMS home network domain name, or null if not available 226 */ 227 @Override 228 public String getIsimDomain() { 229 return mIsimDomain; 230 } 231 232 /** 233 * Return an array of IMS public user identities (IMPU). 234 * Returns null if the IMPU hasn't been loaded or isn't present on the ISIM. 235 * @return an array of IMS public user identity strings, or null if not available 236 */ 237 @Override 238 public String[] getIsimImpu() { 239 return (mIsimImpu != null) ? mIsimImpu.clone() : null; 240 } 241 242 @Override 243 public int getDisplayRule(String plmn) { 244 // Not applicable to Isim 245 return 0; 246 } 247 248 @Override 249 public void onReady() { 250 fetchIsimRecords(); 251 } 252 253 @Override 254 public void onRefresh(boolean fileChanged, int[] fileList) { 255 // We do not handle it in Isim 256 } 257 258 @Override 259 public void setVoiceMailNumber(String alphaTag, String voiceNumber, 260 Message onComplete) { 261 // Not applicable to Isim 262 } 263 264 @Override 265 public void setVoiceMessageWaiting(int line, int countWaiting) { 266 // Not applicable to Isim 267 } 268 269 @Override 270 protected void log(String s) { 271 if (DBG) Rlog.d(LOG_TAG, "[ISIM] " + s); 272 } 273 274 @Override 275 protected void loge(String s) { 276 if (DBG) Rlog.e(LOG_TAG, "[ISIM] " + s); 277 } 278 279 @Override 280 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 281 pw.println("IsimRecords: " + this); 282 pw.println(" extends:"); 283 super.dump(fd, pw, args); 284 pw.println(" mIsimImpi=" + mIsimImpi); 285 pw.println(" mIsimDomain=" + mIsimDomain); 286 pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu)); 287 pw.flush(); 288 } 289 } 290