OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / telephony / java / com / android / internal / telephony / IccCard.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;
18
19 import static android.Manifest.permission.READ_PHONE_STATE;
20 import android.app.ActivityManagerNative;
21 import android.content.Intent;
22 import android.os.AsyncResult;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.os.Registrant;
26 import android.os.RegistrantList;
27 import android.util.Log;
28
29 import com.android.internal.telephony.PhoneBase;
30 import com.android.internal.telephony.CommandsInterface.RadioState;
31
32 /**
33  * {@hide}
34  */
35 public abstract class IccCard {
36     protected String mLogTag;
37     protected boolean mDbg;
38
39     private IccCardStatus mIccCardStatus = null;
40     protected State mState = null;
41     protected PhoneBase mPhone;
42     private RegistrantList mAbsentRegistrants = new RegistrantList();
43     private RegistrantList mPinLockedRegistrants = new RegistrantList();
44     private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
45
46     private boolean mDesiredPinLocked;
47     private boolean mDesiredFdnEnabled;
48     private boolean mIccPinLocked = true; // Default to locked
49     private boolean mIccFdnEnabled = false; // Default to disabled.
50                                             // Will be updated when SIM_READY.
51
52
53     /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
54     static public final String INTENT_KEY_ICC_STATE = "ss";
55     /* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */
56     static public final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
57     /* ABSENT means ICC is missing */
58     static public final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
59     /* LOCKED means ICC is locked by pin or by network */
60     static public final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
61     /* READY means ICC is ready to access */
62     static public final String INTENT_VALUE_ICC_READY = "READY";
63     /* IMSI means ICC IMSI is ready in property */
64     static public final String INTENT_VALUE_ICC_IMSI = "IMSI";
65     /* LOADED means all ICC records, including IMSI, are loaded */
66     static public final String INTENT_VALUE_ICC_LOADED = "LOADED";
67     /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
68     static public final String INTENT_KEY_LOCKED_REASON = "reason";
69     /* PIN means ICC is locked on PIN1 */
70     static public final String INTENT_VALUE_LOCKED_ON_PIN = "PIN";
71     /* PUK means ICC is locked on PUK1 */
72     static public final String INTENT_VALUE_LOCKED_ON_PUK = "PUK";
73     /* NETWORK means ICC is locked on NETWORK PERSONALIZATION */
74     static public final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK";
75
76     protected static final int EVENT_ICC_LOCKED_OR_ABSENT = 1;
77     private static final int EVENT_GET_ICC_STATUS_DONE = 2;
78     protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3;
79     private static final int EVENT_PINPUK_DONE = 4;
80     private static final int EVENT_REPOLL_STATUS_DONE = 5;
81     protected static final int EVENT_ICC_READY = 6;
82     private static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7;
83     private static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8;
84     private static final int EVENT_CHANGE_ICC_PASSWORD_DONE = 9;
85     private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
86     private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
87
88     /*
89       UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
90       PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
91       turns to READY
92      */
93     public enum State {
94         UNKNOWN,
95         ABSENT,
96         PIN_REQUIRED,
97         PUK_REQUIRED,
98         NETWORK_LOCKED,
99         READY,
100         NOT_READY;
101
102         public boolean isPinLocked() {
103             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
104         }
105     }
106
107     public State getState() {
108         if (mState == null) {
109             switch(mPhone.mCM.getRadioState()) {
110                 /* This switch block must not return anything in
111                  * State.isLocked() or State.ABSENT.
112                  * If it does, handleSimStatus() may break
113                  */
114                 case RADIO_OFF:
115                 case RADIO_UNAVAILABLE:
116                 case SIM_NOT_READY:
117                 case RUIM_NOT_READY:
118                     return State.UNKNOWN;
119                 case SIM_LOCKED_OR_ABSENT:
120                 case RUIM_LOCKED_OR_ABSENT:
121                     //this should be transient-only
122                     return State.UNKNOWN;
123                 case SIM_READY:
124                 case RUIM_READY:
125                 case NV_READY:
126                     return State.READY;
127                 case NV_NOT_READY:
128                     return State.ABSENT;
129             }
130         } else {
131             return mState;
132         }
133
134         Log.e(mLogTag, "IccCard.getState(): case should never be reached");
135         return State.UNKNOWN;
136     }
137
138     public IccCard(PhoneBase phone, String logTag, Boolean dbg) {
139         mPhone = phone;
140         mLogTag = logTag;
141         mDbg = dbg;
142     }
143
144     abstract public void dispose();
145
146     protected void finalize() {
147         if(mDbg) Log.d(mLogTag, "IccCard finalized");
148     }
149
150     /**
151      * Notifies handler of any transition into State.ABSENT
152      */
153     public void registerForAbsent(Handler h, int what, Object obj) {
154         Registrant r = new Registrant (h, what, obj);
155
156         mAbsentRegistrants.add(r);
157
158         if (getState() == State.ABSENT) {
159             r.notifyRegistrant();
160         }
161     }
162
163     public void unregisterForAbsent(Handler h) {
164         mAbsentRegistrants.remove(h);
165     }
166
167     /**
168      * Notifies handler of any transition into State.NETWORK_LOCKED
169      */
170     public void registerForNetworkLocked(Handler h, int what, Object obj) {
171         Registrant r = new Registrant (h, what, obj);
172
173         mNetworkLockedRegistrants.add(r);
174
175         if (getState() == State.NETWORK_LOCKED) {
176             r.notifyRegistrant();
177         }
178     }
179
180     public void unregisterForNetworkLocked(Handler h) {
181         mNetworkLockedRegistrants.remove(h);
182     }
183
184     /**
185      * Notifies handler of any transition into State.isPinLocked()
186      */
187     public void registerForLocked(Handler h, int what, Object obj) {
188         Registrant r = new Registrant (h, what, obj);
189
190         mPinLockedRegistrants.add(r);
191
192         if (getState().isPinLocked()) {
193             r.notifyRegistrant();
194         }
195     }
196
197     public void unregisterForLocked(Handler h) {
198         mPinLockedRegistrants.remove(h);
199     }
200
201
202     /**
203      * Supply the ICC PIN to the ICC
204      *
205      * When the operation is complete, onComplete will be sent to its
206      * Handler.
207      *
208      * onComplete.obj will be an AsyncResult
209      *
210      * ((AsyncResult)onComplete.obj).exception == null on success
211      * ((AsyncResult)onComplete.obj).exception != null on fail
212      *
213      * If the supplied PIN is incorrect:
214      * ((AsyncResult)onComplete.obj).exception != null
215      * && ((AsyncResult)onComplete.obj).exception
216      *       instanceof com.android.internal.telephony.gsm.CommandException)
217      * && ((CommandException)(((AsyncResult)onComplete.obj).exception))
218      *          .getCommandError() == CommandException.Error.PASSWORD_INCORRECT
219      *
220      *
221      */
222
223     public void supplyPin (String pin, Message onComplete) {
224         mPhone.mCM.supplyIccPin(pin, mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
225     }
226
227     public void supplyPuk (String puk, String newPin, Message onComplete) {
228         mPhone.mCM.supplyIccPuk(puk, newPin,
229                 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
230     }
231
232     public void supplyPin2 (String pin2, Message onComplete) {
233         mPhone.mCM.supplyIccPin2(pin2,
234                 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
235     }
236
237     public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
238         mPhone.mCM.supplyIccPuk2(puk2, newPin2,
239                 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
240     }
241
242     public void supplyNetworkDepersonalization (String pin, Message onComplete) {
243         if(mDbg) log("Network Despersonalization: " + pin);
244         mPhone.mCM.supplyNetworkDepersonalization(pin,
245                 mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
246     }
247
248     /**
249      * Check whether ICC pin lock is enabled
250      * This is a sync call which returns the cached pin enabled state
251      *
252      * @return true for ICC locked enabled
253      *         false for ICC locked disabled
254      */
255     public boolean getIccLockEnabled() {
256         return mIccPinLocked;
257      }
258
259     /**
260      * Check whether ICC fdn (fixed dialing number) is enabled
261      * This is a sync call which returns the cached pin enabled state
262      *
263      * @return true for ICC fdn enabled
264      *         false for ICC fdn disabled
265      */
266      public boolean getIccFdnEnabled() {
267         return mIccFdnEnabled;
268      }
269
270      /**
271       * Set the ICC pin lock enabled or disabled
272       * When the operation is complete, onComplete will be sent to its handler
273       *
274       * @param enabled "true" for locked "false" for unlocked.
275       * @param password needed to change the ICC pin state, aka. Pin1
276       * @param onComplete
277       *        onComplete.obj will be an AsyncResult
278       *        ((AsyncResult)onComplete.obj).exception == null on success
279       *        ((AsyncResult)onComplete.obj).exception != null on fail
280       */
281      public void setIccLockEnabled (boolean enabled,
282              String password, Message onComplete) {
283          int serviceClassX;
284          serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
285                  CommandsInterface.SERVICE_CLASS_DATA +
286                  CommandsInterface.SERVICE_CLASS_FAX;
287
288          mDesiredPinLocked = enabled;
289
290          mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
291                  enabled, password, serviceClassX,
292                  mHandler.obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
293      }
294
295      /**
296       * Set the ICC fdn enabled or disabled
297       * When the operation is complete, onComplete will be sent to its handler
298       *
299       * @param enabled "true" for locked "false" for unlocked.
300       * @param password needed to change the ICC fdn enable, aka Pin2
301       * @param onComplete
302       *        onComplete.obj will be an AsyncResult
303       *        ((AsyncResult)onComplete.obj).exception == null on success
304       *        ((AsyncResult)onComplete.obj).exception != null on fail
305       */
306      public void setIccFdnEnabled (boolean enabled,
307              String password, Message onComplete) {
308          int serviceClassX;
309          serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
310                  CommandsInterface.SERVICE_CLASS_DATA +
311                  CommandsInterface.SERVICE_CLASS_FAX +
312                  CommandsInterface.SERVICE_CLASS_SMS;
313
314          mDesiredFdnEnabled = enabled;
315
316          mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD,
317                  enabled, password, serviceClassX,
318                  mHandler.obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
319      }
320
321      /**
322       * Change the ICC password used in ICC pin lock
323       * When the operation is complete, onComplete will be sent to its handler
324       *
325       * @param oldPassword is the old password
326       * @param newPassword is the new password
327       * @param onComplete
328       *        onComplete.obj will be an AsyncResult
329       *        ((AsyncResult)onComplete.obj).exception == null on success
330       *        ((AsyncResult)onComplete.obj).exception != null on fail
331       */
332      public void changeIccLockPassword(String oldPassword, String newPassword,
333              Message onComplete) {
334          if(mDbg) log("Change Pin1 old: " + oldPassword + " new: " + newPassword);
335          mPhone.mCM.changeIccPin(oldPassword, newPassword,
336                  mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
337
338      }
339
340      /**
341       * Change the ICC password used in ICC fdn enable
342       * When the operation is complete, onComplete will be sent to its handler
343       *
344       * @param oldPassword is the old password
345       * @param newPassword is the new password
346       * @param onComplete
347       *        onComplete.obj will be an AsyncResult
348       *        ((AsyncResult)onComplete.obj).exception == null on success
349       *        ((AsyncResult)onComplete.obj).exception != null on fail
350       */
351      public void changeIccFdnPassword(String oldPassword, String newPassword,
352              Message onComplete) {
353          if(mDbg) log("Change Pin2 old: " + oldPassword + " new: " + newPassword);
354          mPhone.mCM.changeIccPin2(oldPassword, newPassword,
355                  mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
356
357      }
358
359
360     /**
361      * Returns service provider name stored in ICC card.
362      * If there is no service provider name associated or the record is not
363      * yet available, null will be returned <p>
364      *
365      * Please use this value when display Service Provider Name in idle mode <p>
366      *
367      * Usage of this provider name in the UI is a common carrier requirement.
368      *
369      * Also available via Android property "gsm.sim.operator.alpha"
370      *
371      * @return Service Provider Name stored in ICC card
372      *         null if no service provider name associated or the record is not
373      *         yet available
374      *
375      */
376     public abstract String getServiceProviderName();
377
378     protected void updateStateProperty() {
379         mPhone.setSystemProperty(TelephonyProperties.PROPERTY_SIM_STATE, getState().toString());
380     }
381
382     private void getIccCardStatusDone(AsyncResult ar) {
383         if (ar.exception != null) {
384             Log.e(mLogTag,"Error getting ICC status. "
385                     + "RIL_REQUEST_GET_ICC_STATUS should "
386                     + "never return an error", ar.exception);
387             return;
388         }
389         handleIccCardStatus((IccCardStatus) ar.result);
390     }
391
392     private void handleIccCardStatus(IccCardStatus newCardStatus) {
393         boolean transitionedIntoPinLocked;
394         boolean transitionedIntoAbsent;
395         boolean transitionedIntoNetworkLocked;
396
397         State oldState, newState;
398
399         oldState = mState;
400         mIccCardStatus = newCardStatus;
401         newState = getIccCardState();
402         mState = newState;
403
404         updateStateProperty();
405
406         transitionedIntoPinLocked = (
407                  (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED)
408               || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED));
409         transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
410         transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
411                 && newState == State.NETWORK_LOCKED);
412
413         if (transitionedIntoPinLocked) {
414             if(mDbg) log("Notify SIM pin or puk locked.");
415             mPinLockedRegistrants.notifyRegistrants();
416             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
417                     (newState == State.PIN_REQUIRED) ?
418                        INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
419         } else if (transitionedIntoAbsent) {
420             if(mDbg) log("Notify SIM missing.");
421             mAbsentRegistrants.notifyRegistrants();
422             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null);
423         } else if (transitionedIntoNetworkLocked) {
424             if(mDbg) log("Notify SIM network locked.");
425             mNetworkLockedRegistrants.notifyRegistrants();
426             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
427                   INTENT_VALUE_LOCKED_NETWORK);
428         }
429     }
430
431     /**
432      * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
433      * @param ar is asyncResult of Query_Facility_Locked
434      */
435     private void onQueryFdnEnabled(AsyncResult ar) {
436         if(ar.exception != null) {
437             if(mDbg) log("Error in querying facility lock:" + ar.exception);
438             return;
439         }
440
441         int[] ints = (int[])ar.result;
442         if(ints.length != 0) {
443             mIccFdnEnabled = (0!=ints[0]);
444             if(mDbg) log("Query facility lock : "  + mIccFdnEnabled);
445         } else {
446             Log.e(mLogTag, "[IccCard] Bogus facility lock response");
447         }
448     }
449
450     /**
451      * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
452      * @param ar is asyncResult of Query_Facility_Locked
453      */
454     private void onQueryFacilityLock(AsyncResult ar) {
455         if(ar.exception != null) {
456             if (mDbg) log("Error in querying facility lock:" + ar.exception);
457             return;
458         }
459
460         int[] ints = (int[])ar.result;
461         if(ints.length != 0) {
462             mIccPinLocked = (0!=ints[0]);
463             if(mDbg) log("Query facility lock : "  + mIccPinLocked);
464         } else {
465             Log.e(mLogTag, "[IccCard] Bogus facility lock response");
466         }
467     }
468
469     public void broadcastIccStateChangedIntent(String value, String reason) {
470         Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
471         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
472         intent.putExtra(Phone.PHONE_NAME_KEY, mPhone.getPhoneName());
473         intent.putExtra(INTENT_KEY_ICC_STATE, value);
474         intent.putExtra(INTENT_KEY_LOCKED_REASON, reason);
475         if(mDbg) log("Broadcasting intent ACTION_SIM_STATE_CHANGED " +  value
476                 + " reason " + reason);
477         ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE);
478     }
479
480     protected Handler mHandler = new Handler() {
481         @Override
482         public void handleMessage(Message msg){
483             AsyncResult ar;
484             int serviceClassX;
485
486             serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
487                             CommandsInterface.SERVICE_CLASS_DATA +
488                             CommandsInterface.SERVICE_CLASS_FAX;
489
490             switch (msg.what) {
491                 case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
492                     mState = null;
493                     updateStateProperty();
494                     broadcastIccStateChangedIntent(INTENT_VALUE_ICC_NOT_READY, null);
495                     break;
496                 case EVENT_ICC_READY:
497                     //TODO: put facility read in SIM_READY now, maybe in REG_NW
498                     mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
499                     mPhone.mCM.queryFacilityLock (
500                             CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
501                             obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
502                     mPhone.mCM.queryFacilityLock (
503                             CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX,
504                             obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE));
505                     break;
506                 case EVENT_ICC_LOCKED_OR_ABSENT:
507                     mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
508                     mPhone.mCM.queryFacilityLock (
509                             CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
510                             obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
511                     break;
512                 case EVENT_GET_ICC_STATUS_DONE:
513                     ar = (AsyncResult)msg.obj;
514
515                     getIccCardStatusDone(ar);
516                     break;
517                 case EVENT_PINPUK_DONE:
518                     // a PIN/PUK/PIN2/PUK2/Network Personalization
519                     // request has completed. ar.userObj is the response Message
520                     // Repoll before returning
521                     ar = (AsyncResult)msg.obj;
522                     // TODO should abstract these exceptions
523                     AsyncResult.forMessage(((Message)ar.userObj)).exception
524                                                         = ar.exception;
525                     mPhone.mCM.getIccCardStatus(
526                         obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj));
527                     break;
528                 case EVENT_REPOLL_STATUS_DONE:
529                     // Finished repolling status after PIN operation
530                     // ar.userObj is the response messaeg
531                     // ar.userObj.obj is already an AsyncResult with an
532                     // appropriate exception filled in if applicable
533
534                     ar = (AsyncResult)msg.obj;
535                     getIccCardStatusDone(ar);
536                     ((Message)ar.userObj).sendToTarget();
537                     break;
538                 case EVENT_QUERY_FACILITY_LOCK_DONE:
539                     ar = (AsyncResult)msg.obj;
540                     onQueryFacilityLock(ar);
541                     break;
542                 case EVENT_QUERY_FACILITY_FDN_DONE:
543                     ar = (AsyncResult)msg.obj;
544                     onQueryFdnEnabled(ar);
545                     break;
546                 case EVENT_CHANGE_FACILITY_LOCK_DONE:
547                     ar = (AsyncResult)msg.obj;
548                     if (ar.exception == null) {
549                         mIccPinLocked = mDesiredPinLocked;
550                         if (mDbg) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " +
551                                 "mIccPinLocked= " + mIccPinLocked);
552                     } else {
553                         Log.e(mLogTag, "Error change facility lock with exception "
554                             + ar.exception);
555                     }
556                     AsyncResult.forMessage(((Message)ar.userObj)).exception
557                                                         = ar.exception;
558                     ((Message)ar.userObj).sendToTarget();
559                     break;
560                 case EVENT_CHANGE_FACILITY_FDN_DONE:
561                     ar = (AsyncResult)msg.obj;
562
563                     if (ar.exception == null) {
564                         mIccFdnEnabled = mDesiredFdnEnabled;
565                         if (mDbg) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
566                                 "mIccFdnEnabled=" + mIccFdnEnabled);
567                     } else {
568                         Log.e(mLogTag, "Error change facility fdn with exception "
569                                 + ar.exception);
570                     }
571                     AsyncResult.forMessage(((Message)ar.userObj)).exception
572                                                         = ar.exception;
573                     ((Message)ar.userObj).sendToTarget();
574                     break;
575                 case EVENT_CHANGE_ICC_PASSWORD_DONE:
576                     ar = (AsyncResult)msg.obj;
577                     if(ar.exception != null) {
578                         Log.e(mLogTag, "Error in change sim password with exception"
579                             + ar.exception);
580                     }
581                     AsyncResult.forMessage(((Message)ar.userObj)).exception
582                                                         = ar.exception;
583                     ((Message)ar.userObj).sendToTarget();
584                     break;
585                 default:
586                     Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
587             }
588         }
589     };
590
591     public State getIccCardState() {
592         if (mIccCardStatus == null) {
593             Log.e(mLogTag, "[IccCard] IccCardStatus is null");
594             return IccCard.State.ABSENT;
595         }
596
597         // this is common for all radio technologies
598         if (!mIccCardStatus.getCardState().isCardPresent()) {
599             return IccCard.State.ABSENT;
600         }
601
602         RadioState currentRadioState = mPhone.mCM.getRadioState();
603         // check radio technology
604         if( currentRadioState == RadioState.RADIO_OFF         ||
605             currentRadioState == RadioState.RADIO_UNAVAILABLE ||
606             currentRadioState == RadioState.SIM_NOT_READY     ||
607             currentRadioState == RadioState.RUIM_NOT_READY    ||
608             currentRadioState == RadioState.NV_NOT_READY      ||
609             currentRadioState == RadioState.NV_READY) {
610             return IccCard.State.NOT_READY;
611         }
612
613         if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT  ||
614             currentRadioState == RadioState.SIM_READY             ||
615             currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
616             currentRadioState == RadioState.RUIM_READY) {
617
618             int index;
619
620             // check for CDMA radio technology
621             if (currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
622                 currentRadioState == RadioState.RUIM_READY) {
623                 index = mIccCardStatus.getCdmaSubscriptionAppIndex();
624             }
625             else {
626                 index = mIccCardStatus.getGsmUmtsSubscriptionAppIndex();
627             }
628
629             IccCardApplication app = mIccCardStatus.getApplication(index);
630
631             if (app == null) {
632                 Log.e(mLogTag, "[IccCard] Subscription Application in not present");
633                 return IccCard.State.ABSENT;
634             }
635
636             // check if PIN required
637             if (app.app_state.isPinRequired()) {
638                 return IccCard.State.PIN_REQUIRED;
639             }
640             if (app.app_state.isPukRequired()) {
641                 return IccCard.State.PUK_REQUIRED;
642             }
643             if (app.app_state.isSubscriptionPersoEnabled()) {
644                 return IccCard.State.NETWORK_LOCKED;
645             }
646             if (app.app_state.isAppReady()) {
647                 return IccCard.State.READY;
648             }
649             if (app.app_state.isAppNotReady()) {
650                 return IccCard.State.NOT_READY;
651             }
652             return IccCard.State.NOT_READY;
653         }
654
655         return IccCard.State.ABSENT;
656     }
657
658
659     public boolean isApplicationOnIcc(IccCardApplication.AppType type) {
660         if (mIccCardStatus == null) return false;
661
662         for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) {
663             IccCardApplication app = mIccCardStatus.getApplication(i);
664             if (app != null && app.app_type == type) {
665                 return true;
666             }
667         }
668         return false;
669     }
670
671     /**
672      * @return true if a ICC card is present
673      */
674     public boolean hasIccCard() {
675         boolean isIccPresent;
676         if (mPhone.getPhoneName().equals("GSM")) {
677             return mIccCardStatus.getCardState().isCardPresent();
678         } else {
679             // TODO: Make work with a CDMA device with a RUIM card.
680             return false;
681         }
682     }
683
684     private void log(String msg) {
685         Log.d(mLogTag, "[IccCard] " + msg);
686     }
687 }