1831f4e513a90bf3217fca2fb1a56a4a8f0372f1
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / uicc / IccRecords.java
1 /*
2  * Copyright (C) 2006 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.os.Registrant;
24 import android.os.RegistrantList;
25
26 import com.android.internal.telephony.CommandsInterface;
27 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
28
29 import java.io.FileDescriptor;
30 import java.io.PrintWriter;
31 import java.util.concurrent.atomic.AtomicBoolean;
32
33 /**
34  * {@hide}
35  */
36 public abstract class IccRecords extends Handler implements IccConstants {
37     protected static final boolean DBG = true;
38
39     // ***** Instance Variables
40     protected AtomicBoolean mDestroyed = new AtomicBoolean(false);
41     protected Context mContext;
42     protected CommandsInterface mCi;
43     protected IccFileHandler mFh;
44     protected UiccCardApplication mParentApp;
45
46     protected RegistrantList mRecordsLoadedRegistrants = new RegistrantList();
47     protected RegistrantList mImsiReadyRegistrants = new RegistrantList();
48     protected RegistrantList mRecordsEventsRegistrants = new RegistrantList();
49     protected RegistrantList mNewSmsRegistrants = new RegistrantList();
50     protected RegistrantList mNetworkSelectionModeAutomaticRegistrants = new RegistrantList();
51
52     protected int mRecordsToLoad;  // number of pending load requests
53
54     protected AdnRecordCache mAdnCache;
55
56     // ***** Cached SIM State; cleared on channel close
57
58     protected boolean mRecordsRequested = false; // true if we've made requests for the sim records
59
60     protected String mIccId;
61     protected String mMsisdn = null;  // My mobile number
62     protected String mMsisdnTag = null;
63     protected String mVoiceMailNum = null;
64     protected String mVoiceMailTag = null;
65     protected String mNewVoiceMailNum = null;
66     protected String mNewVoiceMailTag = null;
67     protected boolean mIsVoiceMailFixed = false;
68     protected String mImsi;
69     private IccIoResult auth_rsp;
70
71     protected int mMncLength = UNINITIALIZED;
72     protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
73
74     private String mSpn;
75
76     protected String mGid1;
77
78     private final Object mLock = new Object();
79
80     // ***** Constants
81
82     // Markers for mncLength
83     protected static final int UNINITIALIZED = -1;
84     protected static final int UNKNOWN = 0;
85
86     // Bitmasks for SPN display rules.
87     public static final int SPN_RULE_SHOW_SPN  = 0x01;
88     public static final int SPN_RULE_SHOW_PLMN = 0x02;
89
90     // ***** Event Constants
91     protected static final int EVENT_SET_MSISDN_DONE = 30;
92     public static final int EVENT_MWI = 0; // Message Waiting indication
93     public static final int EVENT_CFI = 1; // Call Forwarding indication
94     public static final int EVENT_SPN = 2; // Service Provider Name
95
96     public static final int EVENT_GET_ICC_RECORD_DONE = 100;
97     protected static final int EVENT_APP_READY = 1;
98     private static final int EVENT_AKA_AUTHENTICATE_DONE          = 90;
99
100     @Override
101     public String toString() {
102         return "mDestroyed=" + mDestroyed
103                 + " mContext=" + mContext
104                 + " mCi=" + mCi
105                 + " mFh=" + mFh
106                 + " mParentApp=" + mParentApp
107                 + " recordsLoadedRegistrants=" + mRecordsLoadedRegistrants
108                 + " mImsiReadyRegistrants=" + mImsiReadyRegistrants
109                 + " mRecordsEventsRegistrants=" + mRecordsEventsRegistrants
110                 + " mNewSmsRegistrants=" + mNewSmsRegistrants
111                 + " mNetworkSelectionModeAutomaticRegistrants="
112                         + mNetworkSelectionModeAutomaticRegistrants
113                 + " recordsToLoad=" + mRecordsToLoad
114                 + " adnCache=" + mAdnCache
115                 + " recordsRequested=" + mRecordsRequested
116                 + " iccid=" + mIccId
117                 + " msisdn=" + mMsisdn
118                 + " msisdnTag=" + mMsisdnTag
119                 + " voiceMailNum=" + mVoiceMailNum
120                 + " voiceMailTag=" + mVoiceMailTag
121                 + " newVoiceMailNum=" + mNewVoiceMailNum
122                 + " newVoiceMailTag=" + mNewVoiceMailTag
123                 + " isVoiceMailFixed=" + mIsVoiceMailFixed
124                 + " mImsi=" + mImsi
125                 + " mncLength=" + mMncLength
126                 + " mailboxIndex=" + mMailboxIndex
127                 + " spn=" + mSpn;
128
129     }
130
131     /**
132      * Generic ICC record loaded callback. Subclasses can call EF load methods on
133      * {@link IccFileHandler} passing a Message for onLoaded with the what field set to
134      * {@link #EVENT_GET_ICC_RECORD_DONE} and the obj field set to an instance
135      * of this interface. The {@link #handleMessage} method in this class will print a
136      * log message using {@link #getEfName()} and decrement {@link #mRecordsToLoad}.
137      *
138      * If the record load was successful, {@link #onRecordLoaded} will be called with the result.
139      * Otherwise, an error log message will be output by {@link #handleMessage} and
140      * {@link #onRecordLoaded} will not be called.
141      */
142     public interface IccRecordLoaded {
143         String getEfName();
144         void onRecordLoaded(AsyncResult ar);
145     }
146
147     // ***** Constructor
148     public IccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
149         mContext = c;
150         mCi = ci;
151         mFh = app.getIccFileHandler();
152         mParentApp = app;
153     }
154
155     /**
156      * Call when the IccRecords object is no longer going to be used.
157      */
158     public void dispose() {
159         mDestroyed.set(true);
160         mParentApp = null;
161         mFh = null;
162         mCi = null;
163         mContext = null;
164     }
165
166     public abstract void onReady();
167
168     //***** Public Methods
169     public AdnRecordCache getAdnCache() {
170         return mAdnCache;
171     }
172
173     public String getIccId() {
174         return mIccId;
175     }
176
177     public void registerForRecordsLoaded(Handler h, int what, Object obj) {
178         if (mDestroyed.get()) {
179             return;
180         }
181
182         Registrant r = new Registrant(h, what, obj);
183         mRecordsLoadedRegistrants.add(r);
184
185         if (mRecordsToLoad == 0 && mRecordsRequested == true) {
186             r.notifyRegistrant(new AsyncResult(null, null, null));
187         }
188     }
189     public void unregisterForRecordsLoaded(Handler h) {
190         mRecordsLoadedRegistrants.remove(h);
191     }
192
193     public void registerForImsiReady(Handler h, int what, Object obj) {
194         if (mDestroyed.get()) {
195             return;
196         }
197
198         Registrant r = new Registrant(h, what, obj);
199         mImsiReadyRegistrants.add(r);
200
201         if (mImsi != null) {
202             r.notifyRegistrant(new AsyncResult(null, null, null));
203         }
204     }
205     public void unregisterForImsiReady(Handler h) {
206         mImsiReadyRegistrants.remove(h);
207     }
208
209     public void registerForRecordsEvents(Handler h, int what, Object obj) {
210         Registrant r = new Registrant (h, what, obj);
211         mRecordsEventsRegistrants.add(r);
212
213         /* Notify registrant of all the possible events. This is to make sure registrant is
214         notified even if event occurred in the past. */
215         r.notifyResult(EVENT_MWI);
216         r.notifyResult(EVENT_CFI);
217     }
218     public void unregisterForRecordsEvents(Handler h) {
219         mRecordsEventsRegistrants.remove(h);
220     }
221
222     public void registerForNewSms(Handler h, int what, Object obj) {
223         Registrant r = new Registrant (h, what, obj);
224         mNewSmsRegistrants.add(r);
225     }
226     public void unregisterForNewSms(Handler h) {
227         mNewSmsRegistrants.remove(h);
228     }
229
230     public void registerForNetworkSelectionModeAutomatic(
231             Handler h, int what, Object obj) {
232         Registrant r = new Registrant (h, what, obj);
233         mNetworkSelectionModeAutomaticRegistrants.add(r);
234     }
235     public void unregisterForNetworkSelectionModeAutomatic(Handler h) {
236         mNetworkSelectionModeAutomaticRegistrants.remove(h);
237     }
238
239     /**
240      * Get the International Mobile Subscriber ID (IMSI) on a SIM
241      * for GSM, UMTS and like networks. Default is null if IMSI is
242      * not supported or unavailable.
243      *
244      * @return null if SIM is not yet ready or unavailable
245      */
246     public String getIMSI() {
247         return null;
248     }
249
250     /**
251      * Imsi could be set by ServiceStateTrackers in case of cdma
252      * @param imsi
253      */
254     public void setImsi(String imsi) {
255         mImsi = imsi;
256         mImsiReadyRegistrants.notifyRegistrants();
257     }
258
259     public String getMsisdnNumber() {
260         return mMsisdn;
261     }
262
263     /**
264      * Get the Group Identifier Level 1 (GID1) on a SIM for GSM.
265      * @return null if SIM is not yet ready
266      */
267     public String getGid1() {
268         return null;
269     }
270
271     /**
272      * Set subscriber number to SIM record
273      *
274      * The subscriber number is stored in EF_MSISDN (TS 51.011)
275      *
276      * When the operation is complete, onComplete will be sent to its handler
277      *
278      * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
279      * @param number dailing nubmer (up to 20 digits)
280      *        if the number starts with '+', then set to international TOA
281      * @param onComplete
282      *        onComplete.obj will be an AsyncResult
283      *        ((AsyncResult)onComplete.obj).exception == null on success
284      *        ((AsyncResult)onComplete.obj).exception != null on fail
285      */
286     public void setMsisdnNumber(String alphaTag, String number,
287             Message onComplete) {
288
289         mMsisdn = number;
290         mMsisdnTag = alphaTag;
291
292         if (DBG) log("Set MSISDN: " + mMsisdnTag +" " + mMsisdn);
293
294
295         AdnRecord adn = new AdnRecord(mMsisdnTag, mMsisdn);
296
297         new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
298                 obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
299     }
300
301     public String getMsisdnAlphaTag() {
302         return mMsisdnTag;
303     }
304
305     public String getVoiceMailNumber() {
306         return mVoiceMailNum;
307     }
308
309     /**
310      * Return Service Provider Name stored in SIM (EF_SPN=0x6F46) or in RUIM (EF_RUIM_SPN=0x6F41).
311      *
312      * @return null if SIM is not yet ready or no RUIM entry
313      */
314     public String getServiceProviderName() {
315         String providerName = mSpn;
316
317         // Check for null pointers, mParentApp can be null after dispose,
318         // which did occur after removing a SIM.
319         UiccCardApplication parentApp = mParentApp;
320         if (parentApp != null) {
321             UiccCard card = parentApp.getUiccCard();
322             if (card != null) {
323                 String brandOverride = card.getOperatorBrandOverride();
324                 if (brandOverride != null) {
325                     log("getServiceProviderName: override");
326                     providerName = brandOverride;
327                 } else {
328                     log("getServiceProviderName: no brandOverride");
329                 }
330             } else {
331                 log("getServiceProviderName: card is null");
332             }
333         } else {
334             log("getServiceProviderName: mParentApp is null");
335         }
336         log("getServiceProviderName: providerName=" + providerName);
337         return providerName;
338     }
339
340     protected void setServiceProviderName(String spn) {
341         mSpn = spn;
342     }
343
344     /**
345      * Set voice mail number to SIM record
346      *
347      * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
348      * EF_MAILBOX_CPHS (CPHS 4.2)
349      *
350      * If EF_MBDN is available, store the voice mail number to EF_MBDN
351      *
352      * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
353      *
354      * So the voice mail number will be stored in both EFs if both are available
355      *
356      * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
357      *
358      * When the operation is complete, onComplete will be sent to its handler
359      *
360      * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
361      * @param voiceNumber dailing nubmer (upto 20 digits)
362      *        if the number is start with '+', then set to international TOA
363      * @param onComplete
364      *        onComplete.obj will be an AsyncResult
365      *        ((AsyncResult)onComplete.obj).exception == null on success
366      *        ((AsyncResult)onComplete.obj).exception != null on fail
367      */
368     public abstract void setVoiceMailNumber(String alphaTag, String voiceNumber,
369             Message onComplete);
370
371     public String getVoiceMailAlphaTag() {
372         return mVoiceMailTag;
373     }
374
375     /**
376      * Sets the SIM voice message waiting indicator records
377      * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
378      * @param countWaiting The number of messages waiting, if known. Use
379      *                     -1 to indicate that an unknown number of
380      *                      messages are waiting
381      */
382     public abstract void setVoiceMessageWaiting(int line, int countWaiting);
383
384     /**
385      * Called by GsmPhone to update VoiceMail count
386      */
387     public abstract int getVoiceMessageCount();
388
389     /**
390      * Called by STK Service when REFRESH is received.
391      * @param fileChanged indicates whether any files changed
392      * @param fileList if non-null, a list of EF files that changed
393      */
394     public abstract void onRefresh(boolean fileChanged, int[] fileList);
395
396     /**
397      * Called by subclasses (SimRecords and RuimRecords) whenever
398      * IccRefreshResponse.REFRESH_RESULT_INIT event received
399      */
400     protected void onIccRefreshInit() {
401         mAdnCache.reset();
402         UiccCardApplication parentApp = mParentApp;
403         if ((parentApp != null) &&
404                 (parentApp.getState() == AppState.APPSTATE_READY)) {
405             // This will cause files to be reread
406             sendMessage(obtainMessage(EVENT_APP_READY));
407         }
408     }
409
410     public boolean getRecordsLoaded() {
411         if (mRecordsToLoad == 0 && mRecordsRequested == true) {
412             return true;
413         } else {
414             return false;
415         }
416     }
417
418     //***** Overridden from Handler
419     @Override
420     public void handleMessage(Message msg) {
421         AsyncResult ar;
422
423         switch (msg.what) {
424             case EVENT_GET_ICC_RECORD_DONE:
425                 try {
426                     ar = (AsyncResult) msg.obj;
427                     IccRecordLoaded recordLoaded = (IccRecordLoaded) ar.userObj;
428                     if (DBG) log(recordLoaded.getEfName() + " LOADED");
429
430                     if (ar.exception != null) {
431                         loge("Record Load Exception: " + ar.exception);
432                     } else {
433                         recordLoaded.onRecordLoaded(ar);
434                     }
435                 }catch (RuntimeException exc) {
436                     // I don't want these exceptions to be fatal
437                     loge("Exception parsing SIM record: " + exc);
438                 } finally {
439                     // Count up record load responses even if they are fails
440                     onRecordLoaded();
441                 }
442                 break;
443
444             case EVENT_AKA_AUTHENTICATE_DONE:
445                 ar = (AsyncResult)msg.obj;
446                 auth_rsp = null;
447                 if (DBG) log("EVENT_AKA_AUTHENTICATE_DONE");
448                 if (ar.exception != null) {
449                     loge("Exception ICC SIM AKA: " + ar.exception);
450                 } else {
451                     try {
452                         auth_rsp = (IccIoResult)ar.result;
453                         if (DBG) log("ICC SIM AKA: auth_rsp = " + auth_rsp);
454                     } catch (Exception e) {
455                         loge("Failed to parse ICC SIM AKA contents: " + e);
456                     }
457                 }
458                 synchronized (mLock) {
459                     mLock.notifyAll();
460                 }
461
462                 break;
463
464             default:
465                 super.handleMessage(msg);
466         }
467     }
468
469     protected abstract void onRecordLoaded();
470
471     protected abstract void onAllRecordsLoaded();
472
473     /**
474      * Returns the SpnDisplayRule based on settings on the SIM and the
475      * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
476      * and TS 51.011 10.3.11 for details.
477      *
478      * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
479      * Generally used for GSM/UMTS and the like SIMs.
480      */
481     public abstract int getDisplayRule(String plmn);
482
483     /**
484      * Return true if "Restriction of menu options for manual PLMN selection"
485      * bit is set or EF_CSP data is unavailable, return false otherwise.
486      * Generally used for GSM/UMTS and the like SIMs.
487      */
488     public boolean isCspPlmnEnabled() {
489         return false;
490     }
491
492     /**
493      * Returns the 5 or 6 digit MCC/MNC of the operator that
494      * provided the SIM card. Returns null of SIM is not yet ready
495      * or is not valid for the type of IccCard. Generally used for
496      * GSM/UMTS and the like SIMS
497      */
498     public String getOperatorNumeric() {
499         return null;
500     }
501
502     /**
503      * Get the current Voice call forwarding flag for GSM/UMTS and the like SIMs
504      *
505      * @return true if enabled
506      */
507     public boolean getVoiceCallForwardingFlag() {
508         return false;
509     }
510
511     /**
512      * Set the voice call forwarding flag for GSM/UMTS and the like SIMs
513      *
514      * @param line to enable/disable
515      * @param enable
516      * @param number to which CFU is enabled
517      */
518     public void setVoiceCallForwardingFlag(int line, boolean enable, String number) {
519     }
520
521     /**
522      * Indicates wether SIM is in provisioned state or not.
523      * Overridden only if SIM can be dynamically provisioned via OTA.
524      *
525      * @return true if provisioned
526      */
527     public boolean isProvisioned () {
528         return true;
529     }
530
531     /**
532      * Write string to log file
533      *
534      * @param s is the string to write
535      */
536     protected abstract void log(String s);
537
538     /**
539      * Write error string to log file.
540      *
541      * @param s is the string to write
542      */
543     protected abstract void loge(String s);
544
545     /**
546      * Return an interface to retrieve the ISIM records for IMS, if available.
547      * @return the interface to retrieve the ISIM records, or null if not supported
548      */
549     public IsimRecords getIsimRecords() {
550         return null;
551     }
552
553     public UsimServiceTable getUsimServiceTable() {
554         return null;
555     }
556
557     /**
558      * Returns the response of the SIM application on the UICC to authentication
559      * challenge/response algorithm. The data string and challenge response are
560      * Base64 encoded Strings.
561      * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
562      *
563      * @param authContext parameter P2 that specifies the authentication context per 3GPP TS 31.102 (Section 7.1.2)
564      * @param data authentication challenge data
565      * @return challenge response
566      */
567     public String getIccSimChallengeResponse(int authContext, String data) {
568         if (DBG) log("getIccSimChallengeResponse:");
569
570         try {
571             synchronized(mLock) {
572                 CommandsInterface ci = mCi;
573                 UiccCardApplication parentApp = mParentApp;
574                 if (ci != null && parentApp != null) {
575                     ci.requestIccSimAuthentication(authContext, data,
576                             parentApp.getAid(),
577                             obtainMessage(EVENT_AKA_AUTHENTICATE_DONE));
578                     try {
579                         mLock.wait();
580                     } catch (InterruptedException e) {
581                         loge("getIccSimChallengeResponse: Fail, interrupted"
582                                 + " while trying to request Icc Sim Auth");
583                         return null;
584                     }
585                 } else {
586                     loge( "getIccSimChallengeResponse: "
587                             + "Fail, ci or parentApp is null");
588                     return null;
589                 }
590             }
591         } catch(Exception e) {
592             loge( "getIccSimChallengeResponse: "
593                     + "Fail while trying to request Icc Sim Auth");
594             return null;
595         }
596
597         if (DBG) log("getIccSimChallengeResponse: return auth_rsp");
598
599         return android.util.Base64.encodeToString(auth_rsp.payload, android.util.Base64.NO_WRAP);
600     }
601
602     protected boolean requirePowerOffOnSimRefreshReset() {
603         return mContext.getResources().getBoolean(
604             com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
605     }
606
607     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
608         pw.println("IccRecords: " + this);
609         pw.println(" mDestroyed=" + mDestroyed);
610         pw.println(" mCi=" + mCi);
611         pw.println(" mFh=" + mFh);
612         pw.println(" mParentApp=" + mParentApp);
613         pw.println(" recordsLoadedRegistrants: size=" + mRecordsLoadedRegistrants.size());
614         for (int i = 0; i < mRecordsLoadedRegistrants.size(); i++) {
615             pw.println("  recordsLoadedRegistrants[" + i + "]="
616                     + ((Registrant)mRecordsLoadedRegistrants.get(i)).getHandler());
617         }
618         pw.println(" mImsiReadyRegistrants: size=" + mImsiReadyRegistrants.size());
619         for (int i = 0; i < mImsiReadyRegistrants.size(); i++) {
620             pw.println("  mImsiReadyRegistrants[" + i + "]="
621                     + ((Registrant)mImsiReadyRegistrants.get(i)).getHandler());
622         }
623         pw.println(" mRecordsEventsRegistrants: size=" + mRecordsEventsRegistrants.size());
624         for (int i = 0; i < mRecordsEventsRegistrants.size(); i++) {
625             pw.println("  mRecordsEventsRegistrants[" + i + "]="
626                     + ((Registrant)mRecordsEventsRegistrants.get(i)).getHandler());
627         }
628         pw.println(" mNewSmsRegistrants: size=" + mNewSmsRegistrants.size());
629         for (int i = 0; i < mNewSmsRegistrants.size(); i++) {
630             pw.println("  mNewSmsRegistrants[" + i + "]="
631                     + ((Registrant)mNewSmsRegistrants.get(i)).getHandler());
632         }
633         pw.println(" mNetworkSelectionModeAutomaticRegistrants: size="
634                 + mNetworkSelectionModeAutomaticRegistrants.size());
635         for (int i = 0; i < mNetworkSelectionModeAutomaticRegistrants.size(); i++) {
636             pw.println("  mNetworkSelectionModeAutomaticRegistrants[" + i + "]="
637                     + ((Registrant)mNetworkSelectionModeAutomaticRegistrants.get(i)).getHandler());
638         }
639         pw.println(" mRecordsRequested=" + mRecordsRequested);
640         pw.println(" mRecordsToLoad=" + mRecordsToLoad);
641         pw.println(" mRdnCache=" + mAdnCache);
642         pw.println(" iccid=" + mIccId);
643         pw.println(" mMsisdn=" + mMsisdn);
644         pw.println(" mMsisdnTag=" + mMsisdnTag);
645         pw.println(" mVoiceMailNum=" + mVoiceMailNum);
646         pw.println(" mVoiceMailTag=" + mVoiceMailTag);
647         pw.println(" mNewVoiceMailNum=" + mNewVoiceMailNum);
648         pw.println(" mNewVoiceMailTag=" + mNewVoiceMailTag);
649         pw.println(" mIsVoiceMailFixed=" + mIsVoiceMailFixed);
650         pw.println(" mImsi=" + mImsi);
651         pw.println(" mMncLength=" + mMncLength);
652         pw.println(" mMailboxIndex=" + mMailboxIndex);
653         pw.println(" mSpn=" + mSpn);
654         pw.flush();
655     }
656 }