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;
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.gsm.UsimServiceTable;
27import com.android.internal.telephony.ims.IsimRecords;
28
29import java.util.concurrent.atomic.AtomicBoolean;
30
31/**
32 * {@hide}
33 */
34public abstract class IccRecords extends Handler implements IccConstants {
35
36    protected static final boolean DBG = true;
37    // ***** Instance Variables
38    protected AtomicBoolean mDestroyed = new AtomicBoolean(false);
39    protected Context mContext;
40    protected CommandsInterface mCi;
41    protected IccFileHandler mFh;
42    protected UiccCardApplication mParentApp;
43
44    protected RegistrantList recordsLoadedRegistrants = new RegistrantList();
45    protected RegistrantList mImsiReadyRegistrants = new RegistrantList();
46    protected RegistrantList mRecordsEventsRegistrants = new RegistrantList();
47    protected RegistrantList mNewSmsRegistrants = new RegistrantList();
48    protected RegistrantList mNetworkSelectionModeAutomaticRegistrants = new RegistrantList();
49
50    protected int recordsToLoad;  // number of pending load requests
51
52    protected AdnRecordCache adnCache;
53
54    // ***** Cached SIM State; cleared on channel close
55
56    protected boolean recordsRequested = false; // true if we've made requests for the sim records
57
58    public String iccid;
59    protected String msisdn = null;  // My mobile number
60    protected String msisdnTag = null;
61    protected String voiceMailNum = null;
62    protected String voiceMailTag = null;
63    protected String newVoiceMailNum = null;
64    protected String newVoiceMailTag = null;
65    protected boolean isVoiceMailFixed = false;
66    protected int countVoiceMessages = 0;
67    protected String mImsi;
68
69    protected int mncLength = UNINITIALIZED;
70    protected int mailboxIndex = 0; // 0 is no mailbox dailing number associated
71
72    protected String spn;
73
74    // ***** Constants
75
76    // Markers for mncLength
77    protected static final int UNINITIALIZED = -1;
78    protected static final int UNKNOWN = 0;
79
80    // Bitmasks for SPN display rules.
81    protected static final int SPN_RULE_SHOW_SPN  = 0x01;
82    protected static final int SPN_RULE_SHOW_PLMN = 0x02;
83
84    // ***** Event Constants
85    protected static final int EVENT_SET_MSISDN_DONE = 30;
86    public static final int EVENT_MWI = 0; // Message Waiting indication
87    public static final int EVENT_CFI = 1; // Call Forwarding indication
88    public static final int EVENT_SPN = 2; // Service Provider Name
89
90    public static final int EVENT_GET_ICC_RECORD_DONE = 100;
91
92    /**
93     * Generic ICC record loaded callback. Subclasses can call EF load methods on
94     * {@link IccFileHandler} passing a Message for onLoaded with the what field set to
95     * {@link #EVENT_GET_ICC_RECORD_DONE} and the obj field set to an instance
96     * of this interface. The {@link #handleMessage} method in this class will print a
97     * log message using {@link #getEfName()} and decrement {@link #recordsToLoad}.
98     *
99     * If the record load was successful, {@link #onRecordLoaded} will be called with the result.
100     * Otherwise, an error log message will be output by {@link #handleMessage} and
101     * {@link #onRecordLoaded} will not be called.
102     */
103    public interface IccRecordLoaded {
104        String getEfName();
105        void onRecordLoaded(AsyncResult ar);
106    }
107
108    // ***** Constructor
109    public IccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
110        mContext = c;
111        mCi = ci;
112        mFh = app.getIccFileHandler();
113        mParentApp = app;
114    }
115
116    /**
117     * Call when the IccRecords object is no longer going to be used.
118     */
119    public void dispose() {
120        mDestroyed.set(true);
121        mParentApp = null;
122        mFh = null;
123        mCi = null;
124        mContext = null;
125    }
126
127    public abstract void onReady();
128
129    //***** Public Methods
130    public AdnRecordCache getAdnCache() {
131        return adnCache;
132    }
133
134    public void registerForRecordsLoaded(Handler h, int what, Object obj) {
135        if (mDestroyed.get()) {
136            return;
137        }
138
139        Registrant r = new Registrant(h, what, obj);
140        recordsLoadedRegistrants.add(r);
141
142        if (recordsToLoad == 0 && recordsRequested == true) {
143            r.notifyRegistrant(new AsyncResult(null, null, null));
144        }
145    }
146    public void unregisterForRecordsLoaded(Handler h) {
147        recordsLoadedRegistrants.remove(h);
148    }
149
150    public void registerForImsiReady(Handler h, int what, Object obj) {
151        if (mDestroyed.get()) {
152            return;
153        }
154
155        Registrant r = new Registrant(h, what, obj);
156        mImsiReadyRegistrants.add(r);
157
158        if (mImsi != null) {
159            r.notifyRegistrant(new AsyncResult(null, null, null));
160        }
161    }
162    public void unregisterForImsiReady(Handler h) {
163        mImsiReadyRegistrants.remove(h);
164    }
165
166    public void registerForRecordsEvents(Handler h, int what, Object obj) {
167        Registrant r = new Registrant (h, what, obj);
168        mRecordsEventsRegistrants.add(r);
169    }
170    public void unregisterForRecordsEvents(Handler h) {
171        mRecordsEventsRegistrants.remove(h);
172    }
173
174    public void registerForNewSms(Handler h, int what, Object obj) {
175        Registrant r = new Registrant (h, what, obj);
176        mNewSmsRegistrants.add(r);
177    }
178    public void unregisterForNewSms(Handler h) {
179        mNewSmsRegistrants.remove(h);
180    }
181
182    public void registerForNetworkSelectionModeAutomatic(
183            Handler h, int what, Object obj) {
184        Registrant r = new Registrant (h, what, obj);
185        mNetworkSelectionModeAutomaticRegistrants.add(r);
186    }
187    public void unregisterForNetworkSelectionModeAutomatic(Handler h) {
188        mNetworkSelectionModeAutomaticRegistrants.remove(h);
189    }
190
191    /**
192     * Get the International Mobile Subscriber ID (IMSI) on a SIM
193     * for GSM, UMTS and like networks. Default is null if IMSI is
194     * not supported or unavailable.
195     *
196     * @return null if SIM is not yet ready or unavailable
197     */
198    public String getIMSI() {
199        return null;
200    }
201
202    /**
203     * Imsi could be set by ServiceStateTrackers in case of cdma
204     * @param imsi
205     */
206    public void setImsi(String imsi) {
207        this.mImsi = imsi;
208        mImsiReadyRegistrants.notifyRegistrants();
209    }
210
211    public String getMsisdnNumber() {
212        return msisdn;
213    }
214
215    /**
216     * Set subscriber number to SIM record
217     *
218     * The subscriber number is stored in EF_MSISDN (TS 51.011)
219     *
220     * When the operation is complete, onComplete will be sent to its handler
221     *
222     * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
223     * @param number dailing nubmer (up to 20 digits)
224     *        if the number starts with '+', then set to international TOA
225     * @param onComplete
226     *        onComplete.obj will be an AsyncResult
227     *        ((AsyncResult)onComplete.obj).exception == null on success
228     *        ((AsyncResult)onComplete.obj).exception != null on fail
229     */
230    public void setMsisdnNumber(String alphaTag, String number,
231            Message onComplete) {
232
233        msisdn = number;
234        msisdnTag = alphaTag;
235
236        if(DBG) log("Set MSISDN: " + msisdnTag +" " + msisdn);
237
238
239        AdnRecord adn = new AdnRecord(msisdnTag, msisdn);
240
241        new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
242                obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
243    }
244
245    public String getMsisdnAlphaTag() {
246        return msisdnTag;
247    }
248
249    public String getVoiceMailNumber() {
250        return voiceMailNum;
251    }
252
253    /**
254     * Return Service Provider Name stored in SIM (EF_SPN=0x6F46) or in RUIM (EF_RUIM_SPN=0x6F41)
255     * @return null if SIM is not yet ready or no RUIM entry
256     */
257    public String getServiceProviderName() {
258        return spn;
259    }
260
261    /**
262     * Set voice mail number to SIM record
263     *
264     * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
265     * EF_MAILBOX_CPHS (CPHS 4.2)
266     *
267     * If EF_MBDN is available, store the voice mail number to EF_MBDN
268     *
269     * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
270     *
271     * So the voice mail number will be stored in both EFs if both are available
272     *
273     * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
274     *
275     * When the operation is complete, onComplete will be sent to its handler
276     *
277     * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
278     * @param voiceNumber dailing nubmer (upto 20 digits)
279     *        if the number is start with '+', then set to international TOA
280     * @param onComplete
281     *        onComplete.obj will be an AsyncResult
282     *        ((AsyncResult)onComplete.obj).exception == null on success
283     *        ((AsyncResult)onComplete.obj).exception != null on fail
284     */
285    public abstract void setVoiceMailNumber(String alphaTag, String voiceNumber,
286            Message onComplete);
287
288    public String getVoiceMailAlphaTag() {
289        return voiceMailTag;
290    }
291
292    /**
293     * Sets the SIM voice message waiting indicator records
294     * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
295     * @param countWaiting The number of messages waiting, if known. Use
296     *                     -1 to indicate that an unknown number of
297     *                      messages are waiting
298     */
299    public abstract void setVoiceMessageWaiting(int line, int countWaiting);
300
301    /** @return  true if there are messages waiting, false otherwise. */
302    public boolean getVoiceMessageWaiting() {
303        return countVoiceMessages != 0;
304    }
305
306    /**
307     * Returns number of voice messages waiting, if available
308     * If not available (eg, on an older CPHS SIM) -1 is returned if
309     * getVoiceMessageWaiting() is true
310     */
311    public int getVoiceMessageCount() {
312        return countVoiceMessages;
313    }
314
315    /**
316     * Called by STK Service when REFRESH is received.
317     * @param fileChanged indicates whether any files changed
318     * @param fileList if non-null, a list of EF files that changed
319     */
320    public abstract void onRefresh(boolean fileChanged, int[] fileList);
321
322
323    public boolean getRecordsLoaded() {
324        if (recordsToLoad == 0 && recordsRequested == true) {
325            return true;
326        } else {
327            return false;
328        }
329    }
330
331    //***** Overridden from Handler
332    @Override
333    public void handleMessage(Message msg) {
334        switch (msg.what) {
335            case EVENT_GET_ICC_RECORD_DONE:
336                try {
337                    AsyncResult ar = (AsyncResult) msg.obj;
338                    IccRecordLoaded recordLoaded = (IccRecordLoaded) ar.userObj;
339                    if (DBG) log(recordLoaded.getEfName() + " LOADED");
340
341                    if (ar.exception != null) {
342                        loge("Record Load Exception: " + ar.exception);
343                    } else {
344                        recordLoaded.onRecordLoaded(ar);
345                    }
346                }catch (RuntimeException exc) {
347                    // I don't want these exceptions to be fatal
348                    loge("Exception parsing SIM record: " + exc);
349                } finally {
350                    // Count up record load responses even if they are fails
351                    onRecordLoaded();
352                }
353                break;
354
355            default:
356                super.handleMessage(msg);
357        }
358    }
359
360    protected abstract void onRecordLoaded();
361
362    protected abstract void onAllRecordsLoaded();
363
364    /**
365     * Returns the SpnDisplayRule based on settings on the SIM and the
366     * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
367     * and TS 51.011 10.3.11 for details.
368     *
369     * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
370     * Generally used for GSM/UMTS and the like SIMs.
371     */
372    public abstract int getDisplayRule(String plmn);
373
374    /**
375     * Return true if "Restriction of menu options for manual PLMN selection"
376     * bit is set or EF_CSP data is unavailable, return false otherwise.
377     * Generally used for GSM/UMTS and the like SIMs.
378     */
379    public boolean isCspPlmnEnabled() {
380        return false;
381    }
382
383    /**
384     * Returns the 5 or 6 digit MCC/MNC of the operator that
385     * provided the SIM card. Returns null of SIM is not yet ready
386     * or is not valid for the type of IccCard. Generally used for
387     * GSM/UMTS and the like SIMS
388     */
389    public String getOperatorNumeric() {
390        return null;
391    }
392
393    /**
394     * Get the current Voice call forwarding flag for GSM/UMTS and the like SIMs
395     *
396     * @return true if enabled
397     */
398    public boolean getVoiceCallForwardingFlag() {
399        return false;
400    }
401
402    /**
403     * Set the voice call forwarding flag for GSM/UMTS and the like SIMs
404     *
405     * @param line to enable/disable
406     * @param enable
407     */
408    public void setVoiceCallForwardingFlag(int line, boolean enable) {
409    }
410
411    /**
412     * Indicates wether SIM is in provisioned state or not.
413     * Overridden only if SIM can be dynamically provisioned via OTA.
414     *
415     * @return true if provisioned
416     */
417    public boolean isProvisioned () {
418        return true;
419    }
420
421    /**
422     * Write string to log file
423     *
424     * @param s is the string to write
425     */
426    protected abstract void log(String s);
427
428    /**
429     * Write error string to log file.
430     *
431     * @param s is the string to write
432     */
433    protected abstract void loge(String s);
434
435    /**
436     * Return an interface to retrieve the ISIM records for IMS, if available.
437     * @return the interface to retrieve the ISIM records, or null if not supported
438     */
439    public IsimRecords getIsimRecords() {
440        return null;
441    }
442
443    public UsimServiceTable getUsimServiceTable() {
444        return null;
445    }
446}
447