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
17package com.android.internal.telephony.uicc;
18
19import android.content.Context;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Message;
23import android.os.Registrant;
24import android.os.RegistrantList;
25
26import com.android.internal.telephony.CommandsInterface;
27import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
28
29import java.io.FileDescriptor;
30import java.io.PrintWriter;
31import java.util.concurrent.atomic.AtomicBoolean;
32
33/**
34 * {@hide}
35 */
36public 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 int mCountVoiceMessages = 0;
69    protected String mImsi;
70
71    protected int mMncLength = UNINITIALIZED;
72    protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
73
74    protected String mSpn;
75
76    protected String mGid1;
77
78    // ***** Constants
79
80    // Markers for mncLength
81    protected static final int UNINITIALIZED = -1;
82    protected static final int UNKNOWN = 0;
83
84    // Bitmasks for SPN display rules.
85    public static final int SPN_RULE_SHOW_SPN  = 0x01;
86    public static final int SPN_RULE_SHOW_PLMN = 0x02;
87
88    // ***** Event Constants
89    protected static final int EVENT_SET_MSISDN_DONE = 30;
90    public static final int EVENT_MWI = 0; // Message Waiting indication
91    public static final int EVENT_CFI = 1; // Call Forwarding indication
92    public static final int EVENT_SPN = 2; // Service Provider Name
93
94    public static final int EVENT_GET_ICC_RECORD_DONE = 100;
95    protected static final int EVENT_APP_READY = 1;
96
97    @Override
98    public String toString() {
99        return "mDestroyed=" + mDestroyed
100                + " mContext=" + mContext
101                + " mCi=" + mCi
102                + " mFh=" + mFh
103                + " mParentApp=" + mParentApp
104                + " recordsLoadedRegistrants=" + mRecordsLoadedRegistrants
105                + " mImsiReadyRegistrants=" + mImsiReadyRegistrants
106                + " mRecordsEventsRegistrants=" + mRecordsEventsRegistrants
107                + " mNewSmsRegistrants=" + mNewSmsRegistrants
108                + " mNetworkSelectionModeAutomaticRegistrants="
109                        + mNetworkSelectionModeAutomaticRegistrants
110                + " recordsToLoad=" + mRecordsToLoad
111                + " adnCache=" + mAdnCache
112                + " recordsRequested=" + mRecordsRequested
113                + " iccid=" + mIccId
114                + " msisdn=" + mMsisdn
115                + " msisdnTag=" + mMsisdnTag
116                + " voiceMailNum=" + mVoiceMailNum
117                + " voiceMailTag=" + mVoiceMailTag
118                + " newVoiceMailNum=" + mNewVoiceMailNum
119                + " newVoiceMailTag=" + mNewVoiceMailTag
120                + " isVoiceMailFixed=" + mIsVoiceMailFixed
121                + " countVoiceMessages=" + mCountVoiceMessages
122                + " mImsi=" + mImsi
123                + " mncLength=" + mMncLength
124                + " mailboxIndex=" + mMailboxIndex
125                + " spn=" + mSpn;
126
127    }
128
129    /**
130     * Generic ICC record loaded callback. Subclasses can call EF load methods on
131     * {@link IccFileHandler} passing a Message for onLoaded with the what field set to
132     * {@link #EVENT_GET_ICC_RECORD_DONE} and the obj field set to an instance
133     * of this interface. The {@link #handleMessage} method in this class will print a
134     * log message using {@link #getEfName()} and decrement {@link #mRecordsToLoad}.
135     *
136     * If the record load was successful, {@link #onRecordLoaded} will be called with the result.
137     * Otherwise, an error log message will be output by {@link #handleMessage} and
138     * {@link #onRecordLoaded} will not be called.
139     */
140    public interface IccRecordLoaded {
141        String getEfName();
142        void onRecordLoaded(AsyncResult ar);
143    }
144
145    // ***** Constructor
146    public IccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
147        mContext = c;
148        mCi = ci;
149        mFh = app.getIccFileHandler();
150        mParentApp = app;
151    }
152
153    /**
154     * Call when the IccRecords object is no longer going to be used.
155     */
156    public void dispose() {
157        mDestroyed.set(true);
158        mParentApp = null;
159        mFh = null;
160        mCi = null;
161        mContext = null;
162    }
163
164    public abstract void onReady();
165
166    //***** Public Methods
167    public AdnRecordCache getAdnCache() {
168        return mAdnCache;
169    }
170
171    public String getIccId() {
172        return mIccId;
173    }
174
175    public void registerForRecordsLoaded(Handler h, int what, Object obj) {
176        if (mDestroyed.get()) {
177            return;
178        }
179
180        Registrant r = new Registrant(h, what, obj);
181        mRecordsLoadedRegistrants.add(r);
182
183        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
184            r.notifyRegistrant(new AsyncResult(null, null, null));
185        }
186    }
187    public void unregisterForRecordsLoaded(Handler h) {
188        mRecordsLoadedRegistrants.remove(h);
189    }
190
191    public void registerForImsiReady(Handler h, int what, Object obj) {
192        if (mDestroyed.get()) {
193            return;
194        }
195
196        Registrant r = new Registrant(h, what, obj);
197        mImsiReadyRegistrants.add(r);
198
199        if (mImsi != null) {
200            r.notifyRegistrant(new AsyncResult(null, null, null));
201        }
202    }
203    public void unregisterForImsiReady(Handler h) {
204        mImsiReadyRegistrants.remove(h);
205    }
206
207    public void registerForRecordsEvents(Handler h, int what, Object obj) {
208        Registrant r = new Registrant (h, what, obj);
209        mRecordsEventsRegistrants.add(r);
210    }
211    public void unregisterForRecordsEvents(Handler h) {
212        mRecordsEventsRegistrants.remove(h);
213    }
214
215    public void registerForNewSms(Handler h, int what, Object obj) {
216        Registrant r = new Registrant (h, what, obj);
217        mNewSmsRegistrants.add(r);
218    }
219    public void unregisterForNewSms(Handler h) {
220        mNewSmsRegistrants.remove(h);
221    }
222
223    public void registerForNetworkSelectionModeAutomatic(
224            Handler h, int what, Object obj) {
225        Registrant r = new Registrant (h, what, obj);
226        mNetworkSelectionModeAutomaticRegistrants.add(r);
227    }
228    public void unregisterForNetworkSelectionModeAutomatic(Handler h) {
229        mNetworkSelectionModeAutomaticRegistrants.remove(h);
230    }
231
232    /**
233     * Get the International Mobile Subscriber ID (IMSI) on a SIM
234     * for GSM, UMTS and like networks. Default is null if IMSI is
235     * not supported or unavailable.
236     *
237     * @return null if SIM is not yet ready or unavailable
238     */
239    public String getIMSI() {
240        return null;
241    }
242
243    /**
244     * Imsi could be set by ServiceStateTrackers in case of cdma
245     * @param imsi
246     */
247    public void setImsi(String imsi) {
248        mImsi = imsi;
249        mImsiReadyRegistrants.notifyRegistrants();
250    }
251
252    public String getMsisdnNumber() {
253        return mMsisdn;
254    }
255
256    /**
257     * Get the Group Identifier Level 1 (GID1) on a SIM for GSM.
258     * @return null if SIM is not yet ready
259     */
260    public String getGid1() {
261        return null;
262    }
263
264    /**
265     * Set subscriber number to SIM record
266     *
267     * The subscriber number is stored in EF_MSISDN (TS 51.011)
268     *
269     * When the operation is complete, onComplete will be sent to its handler
270     *
271     * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
272     * @param number dailing nubmer (up to 20 digits)
273     *        if the number starts with '+', then set to international TOA
274     * @param onComplete
275     *        onComplete.obj will be an AsyncResult
276     *        ((AsyncResult)onComplete.obj).exception == null on success
277     *        ((AsyncResult)onComplete.obj).exception != null on fail
278     */
279    public void setMsisdnNumber(String alphaTag, String number,
280            Message onComplete) {
281
282        mMsisdn = number;
283        mMsisdnTag = alphaTag;
284
285        if (DBG) log("Set MSISDN: " + mMsisdnTag +" " + mMsisdn);
286
287
288        AdnRecord adn = new AdnRecord(mMsisdnTag, mMsisdn);
289
290        new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
291                obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
292    }
293
294    public String getMsisdnAlphaTag() {
295        return mMsisdnTag;
296    }
297
298    public String getVoiceMailNumber() {
299        return mVoiceMailNum;
300    }
301
302    /**
303     * Return Service Provider Name stored in SIM (EF_SPN=0x6F46) or in RUIM (EF_RUIM_SPN=0x6F41)
304     * @return null if SIM is not yet ready or no RUIM entry
305     */
306    public String getServiceProviderName() {
307        return mSpn;
308    }
309
310    /**
311     * Set voice mail number to SIM record
312     *
313     * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
314     * EF_MAILBOX_CPHS (CPHS 4.2)
315     *
316     * If EF_MBDN is available, store the voice mail number to EF_MBDN
317     *
318     * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
319     *
320     * So the voice mail number will be stored in both EFs if both are available
321     *
322     * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
323     *
324     * When the operation is complete, onComplete will be sent to its handler
325     *
326     * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
327     * @param voiceNumber dailing nubmer (upto 20 digits)
328     *        if the number is start with '+', then set to international TOA
329     * @param onComplete
330     *        onComplete.obj will be an AsyncResult
331     *        ((AsyncResult)onComplete.obj).exception == null on success
332     *        ((AsyncResult)onComplete.obj).exception != null on fail
333     */
334    public abstract void setVoiceMailNumber(String alphaTag, String voiceNumber,
335            Message onComplete);
336
337    public String getVoiceMailAlphaTag() {
338        return mVoiceMailTag;
339    }
340
341    /**
342     * Sets the SIM voice message waiting indicator records
343     * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
344     * @param countWaiting The number of messages waiting, if known. Use
345     *                     -1 to indicate that an unknown number of
346     *                      messages are waiting
347     */
348    public abstract void setVoiceMessageWaiting(int line, int countWaiting);
349
350    /** @return  true if there are messages waiting, false otherwise. */
351    public boolean getVoiceMessageWaiting() {
352        return mCountVoiceMessages != 0;
353    }
354
355    /**
356     * Returns number of voice messages waiting, if available
357     * If not available (eg, on an older CPHS SIM) -1 is returned if
358     * getVoiceMessageWaiting() is true
359     */
360    public int getVoiceMessageCount() {
361        return mCountVoiceMessages;
362    }
363
364    /**
365     * Called by STK Service when REFRESH is received.
366     * @param fileChanged indicates whether any files changed
367     * @param fileList if non-null, a list of EF files that changed
368     */
369    public abstract void onRefresh(boolean fileChanged, int[] fileList);
370
371    /**
372     * Called by subclasses (SimRecords and RuimRecords) whenever
373     * IccRefreshResponse.REFRESH_RESULT_INIT event received
374     */
375    protected void onIccRefreshInit() {
376        mAdnCache.reset();
377        if (mParentApp.getState() == AppState.APPSTATE_READY) {
378            // This will cause files to be reread
379            sendMessage(obtainMessage(EVENT_APP_READY));
380        }
381    }
382
383    public boolean getRecordsLoaded() {
384        if (mRecordsToLoad == 0 && mRecordsRequested == true) {
385            return true;
386        } else {
387            return false;
388        }
389    }
390
391    //***** Overridden from Handler
392    @Override
393    public void handleMessage(Message msg) {
394        switch (msg.what) {
395            case EVENT_GET_ICC_RECORD_DONE:
396                try {
397                    AsyncResult ar = (AsyncResult) msg.obj;
398                    IccRecordLoaded recordLoaded = (IccRecordLoaded) ar.userObj;
399                    if (DBG) log(recordLoaded.getEfName() + " LOADED");
400
401                    if (ar.exception != null) {
402                        loge("Record Load Exception: " + ar.exception);
403                    } else {
404                        recordLoaded.onRecordLoaded(ar);
405                    }
406                }catch (RuntimeException exc) {
407                    // I don't want these exceptions to be fatal
408                    loge("Exception parsing SIM record: " + exc);
409                } finally {
410                    // Count up record load responses even if they are fails
411                    onRecordLoaded();
412                }
413                break;
414
415            default:
416                super.handleMessage(msg);
417        }
418    }
419
420    protected abstract void onRecordLoaded();
421
422    protected abstract void onAllRecordsLoaded();
423
424    /**
425     * Returns the SpnDisplayRule based on settings on the SIM and the
426     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
427     * and TS 51.011 10.3.11 for details.
428     *
429     * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
430     * Generally used for GSM/UMTS and the like SIMs.
431     */
432    public abstract int getDisplayRule(String plmn);
433
434    /**
435     * Return true if "Restriction of menu options for manual PLMN selection"
436     * bit is set or EF_CSP data is unavailable, return false otherwise.
437     * Generally used for GSM/UMTS and the like SIMs.
438     */
439    public boolean isCspPlmnEnabled() {
440        return false;
441    }
442
443    /**
444     * Returns the 5 or 6 digit MCC/MNC of the operator that
445     * provided the SIM card. Returns null of SIM is not yet ready
446     * or is not valid for the type of IccCard. Generally used for
447     * GSM/UMTS and the like SIMS
448     */
449    public String getOperatorNumeric() {
450        return null;
451    }
452
453    /**
454     * Get the current Voice call forwarding flag for GSM/UMTS and the like SIMs
455     *
456     * @return true if enabled
457     */
458    public boolean getVoiceCallForwardingFlag() {
459        return false;
460    }
461
462    /**
463     * Set the voice call forwarding flag for GSM/UMTS and the like SIMs
464     *
465     * @param line to enable/disable
466     * @param enable
467     * @param number to which CFU is enabled
468     */
469    public void setVoiceCallForwardingFlag(int line, boolean enable, String number) {
470    }
471
472    /**
473     * Indicates wether SIM is in provisioned state or not.
474     * Overridden only if SIM can be dynamically provisioned via OTA.
475     *
476     * @return true if provisioned
477     */
478    public boolean isProvisioned () {
479        return true;
480    }
481
482    /**
483     * Write string to log file
484     *
485     * @param s is the string to write
486     */
487    protected abstract void log(String s);
488
489    /**
490     * Write error string to log file.
491     *
492     * @param s is the string to write
493     */
494    protected abstract void loge(String s);
495
496    /**
497     * Return an interface to retrieve the ISIM records for IMS, if available.
498     * @return the interface to retrieve the ISIM records, or null if not supported
499     */
500    public IsimRecords getIsimRecords() {
501        return null;
502    }
503
504    public UsimServiceTable getUsimServiceTable() {
505        return null;
506    }
507
508    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
509        pw.println("IccRecords: " + this);
510        pw.println(" mDestroyed=" + mDestroyed);
511        pw.println(" mCi=" + mCi);
512        pw.println(" mFh=" + mFh);
513        pw.println(" mParentApp=" + mParentApp);
514        pw.println(" recordsLoadedRegistrants: size=" + mRecordsLoadedRegistrants.size());
515        for (int i = 0; i < mRecordsLoadedRegistrants.size(); i++) {
516            pw.println("  recordsLoadedRegistrants[" + i + "]="
517                    + ((Registrant)mRecordsLoadedRegistrants.get(i)).getHandler());
518        }
519        pw.println(" mImsiReadyRegistrants: size=" + mImsiReadyRegistrants.size());
520        for (int i = 0; i < mImsiReadyRegistrants.size(); i++) {
521            pw.println("  mImsiReadyRegistrants[" + i + "]="
522                    + ((Registrant)mImsiReadyRegistrants.get(i)).getHandler());
523        }
524        pw.println(" mRecordsEventsRegistrants: size=" + mRecordsEventsRegistrants.size());
525        for (int i = 0; i < mRecordsEventsRegistrants.size(); i++) {
526            pw.println("  mRecordsEventsRegistrants[" + i + "]="
527                    + ((Registrant)mRecordsEventsRegistrants.get(i)).getHandler());
528        }
529        pw.println(" mNewSmsRegistrants: size=" + mNewSmsRegistrants.size());
530        for (int i = 0; i < mNewSmsRegistrants.size(); i++) {
531            pw.println("  mNewSmsRegistrants[" + i + "]="
532                    + ((Registrant)mNewSmsRegistrants.get(i)).getHandler());
533        }
534        pw.println(" mNetworkSelectionModeAutomaticRegistrants: size="
535                + mNetworkSelectionModeAutomaticRegistrants.size());
536        for (int i = 0; i < mNetworkSelectionModeAutomaticRegistrants.size(); i++) {
537            pw.println("  mNetworkSelectionModeAutomaticRegistrants[" + i + "]="
538                    + ((Registrant)mNetworkSelectionModeAutomaticRegistrants.get(i)).getHandler());
539        }
540        pw.println(" mRecordsRequested=" + mRecordsRequested);
541        pw.println(" mRecordsToLoad=" + mRecordsToLoad);
542        pw.println(" mRdnCache=" + mAdnCache);
543        pw.println(" iccid=" + mIccId);
544        pw.println(" mMsisdn=" + mMsisdn);
545        pw.println(" mMsisdnTag=" + mMsisdnTag);
546        pw.println(" mVoiceMailNum=" + mVoiceMailNum);
547        pw.println(" mVoiceMailTag=" + mVoiceMailTag);
548        pw.println(" mNewVoiceMailNum=" + mNewVoiceMailNum);
549        pw.println(" mNewVoiceMailTag=" + mNewVoiceMailTag);
550        pw.println(" mIsVoiceMailFixed=" + mIsVoiceMailFixed);
551        pw.println(" mCountVoiceMessages=" + mCountVoiceMessages);
552        pw.println(" mImsi=" + mImsi);
553        pw.println(" mMncLength=" + mMncLength);
554        pw.println(" mMailboxIndex=" + mMailboxIndex);
555        pw.println(" mSpn=" + mSpn);
556        pw.flush();
557    }
558}
559