• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.UnsupportedAppUsage;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.AsyncResult;
23 import android.os.Message;
24 import android.telephony.Rlog;
25 
26 import com.android.internal.telephony.CommandsInterface;
27 import com.android.internal.telephony.gsm.SimTlv;
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 /**
36  * {@hide}
37  */
38 public class IsimUiccRecords extends IccRecords implements IsimRecords {
39     protected static final String LOG_TAG = "IsimUiccRecords";
40 
41     private static final boolean DBG = true;
42     private static final boolean VDBG = false; // STOPSHIP if true
43     private static final boolean DUMP_RECORDS = false;  // Note: PII is logged when this is true
44                                                         // STOPSHIP if true
45     public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
46 
47     private static final int EVENT_APP_READY = 1;
48     private static final int EVENT_ISIM_AUTHENTICATE_DONE          = 91;
49 
50     // ISIM EF records (see 3GPP TS 31.103)
51     @UnsupportedAppUsage
52     private String mIsimImpi;               // IMS private user identity
53     @UnsupportedAppUsage
54     private String mIsimDomain;             // IMS home network domain name
55     @UnsupportedAppUsage
56     private String[] mIsimImpu;             // IMS public user identity(s)
57     @UnsupportedAppUsage
58     private String mIsimIst;                // IMS Service Table
59     @UnsupportedAppUsage
60     private String[] mIsimPcscf;            // IMS Proxy Call Session Control Function
61     @UnsupportedAppUsage
62     private String auth_rsp;
63 
64     @UnsupportedAppUsage
65     private final Object mLock = new Object();
66 
67     private static final int TAG_ISIM_VALUE = 0x80;     // From 3GPP TS 31.103
68 
69     @Override
toString()70     public String toString() {
71         return "IsimUiccRecords: " + super.toString()
72                 + (DUMP_RECORDS ? (" mIsimImpi=" + mIsimImpi
73                 + " mIsimDomain=" + mIsimDomain
74                 + " mIsimImpu=" + mIsimImpu
75                 + " mIsimIst=" + mIsimIst
76                 + " mIsimPcscf=" + mIsimPcscf) : "");
77     }
78 
IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci)79     public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
80         super(app, c, ci);
81 
82         mRecordsRequested = false;  // No load request is made till SIM ready
83         //todo: currently locked state for ISIM is not handled well and may cause app state to not
84         //be broadcast
85         mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
86 
87         // recordsToLoad is set to 0 because no requests are made yet
88         mRecordsToLoad = 0;
89         // Start off by setting empty state
90         resetRecords();
91 
92         mParentApp.registerForReady(this, EVENT_APP_READY, null);
93         if (DBG) log("IsimUiccRecords X ctor this=" + this);
94     }
95 
96     @Override
dispose()97     public void dispose() {
98         log("Disposing " + this);
99         //Unregister for all events
100         mCi.unregisterForIccRefresh(this);
101         mParentApp.unregisterForReady(this);
102         resetRecords();
103         super.dispose();
104     }
105 
106     // ***** Overridden from Handler
handleMessage(Message msg)107     public void handleMessage(Message msg) {
108         AsyncResult ar;
109 
110         if (mDestroyed.get()) {
111             Rlog.e(LOG_TAG, "Received message " + msg +
112                     "[" + msg.what + "] while being destroyed. Ignoring.");
113             return;
114         }
115         loge("IsimUiccRecords: handleMessage " + msg + "[" + msg.what + "] ");
116 
117         try {
118             switch (msg.what) {
119                 case EVENT_APP_READY:
120                     onReady();
121                     break;
122 
123                 case EVENT_REFRESH:
124                     broadcastRefresh();
125                     super.handleMessage(msg);
126                     break;
127 
128                 case EVENT_ISIM_AUTHENTICATE_DONE:
129                     ar = (AsyncResult)msg.obj;
130                     log("EVENT_ISIM_AUTHENTICATE_DONE");
131                     if (ar.exception != null) {
132                         log("Exception ISIM AKA: " + ar.exception);
133                     } else {
134                         try {
135                             auth_rsp = (String)ar.result;
136                             log("ISIM AKA: auth_rsp = " + auth_rsp);
137                         } catch (Exception e) {
138                             log("Failed to parse ISIM AKA contents: " + e);
139                         }
140                     }
141                     synchronized (mLock) {
142                         mLock.notifyAll();
143                     }
144 
145                     break;
146 
147                 default:
148                     super.handleMessage(msg);   // IccRecords handles generic record load responses
149 
150             }
151         } catch (RuntimeException exc) {
152             // I don't want these exceptions to be fatal
153             Rlog.w(LOG_TAG, "Exception parsing SIM record", exc);
154         }
155     }
156 
157     @UnsupportedAppUsage
fetchIsimRecords()158     protected void fetchIsimRecords() {
159         mRecordsRequested = true;
160 
161         mFh.loadEFTransparent(EF_IMPI, obtainMessage(
162                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
163         mRecordsToLoad++;
164 
165         mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
166                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
167         mRecordsToLoad++;
168 
169         mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
170                 IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
171         mRecordsToLoad++;
172         mFh.loadEFTransparent(EF_IST, obtainMessage(
173                     IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimIstLoaded()));
174         mRecordsToLoad++;
175         mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
176                     IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
177         mRecordsToLoad++;
178 
179         if (DBG) log("fetchIsimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested);
180     }
181 
resetRecords()182     protected void resetRecords() {
183         // recordsRequested is set to false indicating that the SIM
184         // read requests made so far are not valid. This is set to
185         // true only when fresh set of read requests are made.
186         mIsimImpi = null;
187         mIsimDomain = null;
188         mIsimImpu = null;
189         mIsimIst = null;
190         mIsimPcscf = null;
191         auth_rsp = null;
192 
193         mRecordsRequested = false;
194         mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
195         mLoaded.set(false);
196     }
197 
198     private class EfIsimImpiLoaded implements IccRecords.IccRecordLoaded {
getEfName()199         public String getEfName() {
200             return "EF_ISIM_IMPI";
201         }
onRecordLoaded(AsyncResult ar)202         public void onRecordLoaded(AsyncResult ar) {
203             byte[] data = (byte[]) ar.result;
204             mIsimImpi = isimTlvToString(data);
205             if (DUMP_RECORDS) log("EF_IMPI=" + mIsimImpi);
206         }
207     }
208 
209     private class EfIsimImpuLoaded implements IccRecords.IccRecordLoaded {
getEfName()210         public String getEfName() {
211             return "EF_ISIM_IMPU";
212         }
onRecordLoaded(AsyncResult ar)213         public void onRecordLoaded(AsyncResult ar) {
214             ArrayList<byte[]> impuList = (ArrayList<byte[]>) ar.result;
215             if (DBG) log("EF_IMPU record count: " + impuList.size());
216             mIsimImpu = new String[impuList.size()];
217             int i = 0;
218             for (byte[] identity : impuList) {
219                 String impu = isimTlvToString(identity);
220                 if (DUMP_RECORDS) log("EF_IMPU[" + i + "]=" + impu);
221                 mIsimImpu[i++] = impu;
222             }
223         }
224     }
225 
226     private class EfIsimDomainLoaded implements IccRecords.IccRecordLoaded {
getEfName()227         public String getEfName() {
228             return "EF_ISIM_DOMAIN";
229         }
onRecordLoaded(AsyncResult ar)230         public void onRecordLoaded(AsyncResult ar) {
231             byte[] data = (byte[]) ar.result;
232             mIsimDomain = isimTlvToString(data);
233             if (DUMP_RECORDS) log("EF_DOMAIN=" + mIsimDomain);
234         }
235     }
236 
237     private class EfIsimIstLoaded implements IccRecords.IccRecordLoaded {
getEfName()238         public String getEfName() {
239             return "EF_ISIM_IST";
240         }
onRecordLoaded(AsyncResult ar)241         public void onRecordLoaded(AsyncResult ar) {
242             byte[] data = (byte[]) ar.result;
243             mIsimIst = IccUtils.bytesToHexString(data);
244             if (DUMP_RECORDS) log("EF_IST=" + mIsimIst);
245         }
246     }
247     private class EfIsimPcscfLoaded implements IccRecords.IccRecordLoaded {
getEfName()248         public String getEfName() {
249             return "EF_ISIM_PCSCF";
250         }
onRecordLoaded(AsyncResult ar)251         public void onRecordLoaded(AsyncResult ar) {
252             ArrayList<byte[]> pcscflist = (ArrayList<byte[]>) ar.result;
253             if (DBG) log("EF_PCSCF record count: " + pcscflist.size());
254             mIsimPcscf = new String[pcscflist.size()];
255             int i = 0;
256             for (byte[] identity : pcscflist) {
257                 String pcscf = isimTlvToString(identity);
258                 if (DUMP_RECORDS) log("EF_PCSCF[" + i + "]=" + pcscf);
259                 mIsimPcscf[i++] = pcscf;
260             }
261         }
262     }
263 
264     /**
265      * ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string
266      * with tag value 0x80.
267      * @param record the byte array containing the IMS data string
268      * @return the decoded String value, or null if the record can't be decoded
269      */
270     @UnsupportedAppUsage
isimTlvToString(byte[] record)271     private static String isimTlvToString(byte[] record) {
272         SimTlv tlv = new SimTlv(record, 0, record.length);
273         do {
274             if (tlv.getTag() == TAG_ISIM_VALUE) {
275                 return new String(tlv.getData(), Charset.forName("UTF-8"));
276             }
277         } while (tlv.nextObject());
278 
279         if (VDBG) {
280             Rlog.d(LOG_TAG, "[ISIM] can't find TLV. record = " + IccUtils.bytesToHexString(record));
281         }
282         return null;
283     }
284 
285     @Override
onRecordLoaded()286     protected void onRecordLoaded() {
287         // One record loaded successfully or failed, In either case
288         // we need to update the recordsToLoad count
289         mRecordsToLoad -= 1;
290         if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested);
291 
292         if (getRecordsLoaded()) {
293             onAllRecordsLoaded();
294         } else if (getLockedRecordsLoaded() || getNetworkLockedRecordsLoaded()) {
295             onLockedAllRecordsLoaded();
296         } else if (mRecordsToLoad < 0) {
297             loge("recordsToLoad <0, programmer error suspected");
298             mRecordsToLoad = 0;
299         }
300     }
301 
onLockedAllRecordsLoaded()302     private void onLockedAllRecordsLoaded() {
303         if (DBG) log("SIM locked; record load complete");
304         if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_LOCKED) {
305             mLockedRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
306         } else if (mLockedRecordsReqReason == LOCKED_RECORDS_REQ_REASON_NETWORK_LOCKED) {
307             mNetworkLockedRecordsLoadedRegistrants.notifyRegistrants(
308                     new AsyncResult(null, null, null));
309         } else {
310             loge("onLockedAllRecordsLoaded: unexpected mLockedRecordsReqReason "
311                     + mLockedRecordsReqReason);
312         }
313     }
314 
315     @Override
onAllRecordsLoaded()316     protected void onAllRecordsLoaded() {
317        if (DBG) log("record load complete");
318         mLoaded.set(true);
319         mRecordsLoadedRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
320     }
321 
322     @Override
handleFileUpdate(int efid)323     protected void handleFileUpdate(int efid) {
324         switch (efid) {
325             case EF_IMPI:
326                 mFh.loadEFTransparent(EF_IMPI, obtainMessage(
327                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
328                 mRecordsToLoad++;
329                 break;
330 
331             case EF_IMPU:
332                 mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
333                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
334                 mRecordsToLoad++;
335             break;
336 
337             case EF_DOMAIN:
338                 mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
339                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
340                 mRecordsToLoad++;
341             break;
342 
343             case EF_IST:
344                 mFh.loadEFTransparent(EF_IST, obtainMessage(
345                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimIstLoaded()));
346                 mRecordsToLoad++;
347             break;
348 
349             case EF_PCSCF:
350                 mFh.loadEFLinearFixedAll(EF_PCSCF, obtainMessage(
351                             IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimPcscfLoaded()));
352                 mRecordsToLoad++;
353 
354             default:
355                 fetchIsimRecords();
356                 break;
357         }
358     }
359 
broadcastRefresh()360     private void broadcastRefresh() {
361         Intent intent = new Intent(INTENT_ISIM_REFRESH);
362         log("send ISim REFRESH: " + INTENT_ISIM_REFRESH);
363         mContext.sendBroadcast(intent);
364     }
365 
366     /**
367      * Return the IMS private user identity (IMPI).
368      * Returns null if the IMPI hasn't been loaded or isn't present on the ISIM.
369      * @return the IMS private user identity string, or null if not available
370      */
371     @Override
getIsimImpi()372     public String getIsimImpi() {
373         return mIsimImpi;
374     }
375 
376     /**
377      * Return the IMS home network domain name.
378      * Returns null if the IMS domain hasn't been loaded or isn't present on the ISIM.
379      * @return the IMS home network domain name, or null if not available
380      */
381     @Override
getIsimDomain()382     public String getIsimDomain() {
383         return mIsimDomain;
384     }
385 
386     /**
387      * Return an array of IMS public user identities (IMPU).
388      * Returns null if the IMPU hasn't been loaded or isn't present on the ISIM.
389      * @return an array of IMS public user identity strings, or null if not available
390      */
391     @Override
getIsimImpu()392     public String[] getIsimImpu() {
393         return (mIsimImpu != null) ? mIsimImpu.clone() : null;
394     }
395 
396     /**
397      * Returns the IMS Service Table (IST) that was loaded from the ISIM.
398      * @return IMS Service Table or null if not present or not loaded
399      */
400     @Override
getIsimIst()401     public String getIsimIst() {
402         return mIsimIst;
403     }
404 
405     /**
406      * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
407      * @return an array of  PCSCF strings with one PCSCF per string, or null if
408      *      not present or not loaded
409      */
410     @Override
getIsimPcscf()411     public String[] getIsimPcscf() {
412         return (mIsimPcscf != null) ? mIsimPcscf.clone() : null;
413     }
414 
415     @Override
onReady()416     public void onReady() {
417         fetchIsimRecords();
418     }
419 
420     @Override
onRefresh(boolean fileChanged, int[] fileList)421     public void onRefresh(boolean fileChanged, int[] fileList) {
422         if (fileChanged) {
423             // A future optimization would be to inspect fileList and
424             // only reload those files that we care about.  For now,
425             // just re-fetch all SIM records that we cache.
426             fetchIsimRecords();
427         }
428     }
429 
430     @Override
setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete)431     public void setVoiceMailNumber(String alphaTag, String voiceNumber,
432             Message onComplete) {
433         // Not applicable to Isim
434     }
435 
436     @Override
setVoiceMessageWaiting(int line, int countWaiting)437     public void setVoiceMessageWaiting(int line, int countWaiting) {
438         // Not applicable to Isim
439     }
440 
441     @UnsupportedAppUsage
442     @Override
log(String s)443     protected void log(String s) {
444         if (DBG) Rlog.d(LOG_TAG, "[ISIM] " + s);
445     }
446 
447     @Override
loge(String s)448     protected void loge(String s) {
449         if (DBG) Rlog.e(LOG_TAG, "[ISIM] " + s);
450     }
451 
452     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)453     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
454         pw.println("IsimRecords: " + this);
455         pw.println(" extends:");
456         super.dump(fd, pw, args);
457         if (DUMP_RECORDS) {
458             pw.println(" mIsimImpi=" + mIsimImpi);
459             pw.println(" mIsimDomain=" + mIsimDomain);
460             pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
461             pw.println(" mIsimIst" + mIsimIst);
462             pw.println(" mIsimPcscf" + mIsimPcscf);
463         }
464         pw.flush();
465     }
466 
467     @Override
getVoiceMessageCount()468     public int getVoiceMessageCount() {
469         return 0; // Not applicable to Isim
470     }
471 
472 }
473