UiccCard.java revision 6bc4098827f3070a44b5e51508b455d7c7be9c07
1/*
2 * Copyright (C) 2006, 2012 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 static android.Manifest.permission.READ_PHONE_STATE;
20import android.app.ActivityManagerNative;
21import android.app.AlertDialog;
22import android.content.Context;
23import android.content.DialogInterface;
24import android.content.Intent;
25import android.content.SharedPreferences;
26import android.content.pm.PackageManager;
27import android.content.pm.Signature;
28import android.content.res.Resources;
29import android.os.AsyncResult;
30import android.os.Handler;
31import android.os.Message;
32import android.os.PowerManager;
33import android.os.Registrant;
34import android.os.RegistrantList;
35import android.preference.PreferenceManager;
36import android.telephony.Rlog;
37import android.telephony.TelephonyManager;
38import android.text.TextUtils;
39import android.view.WindowManager;
40
41import com.android.internal.telephony.CommandsInterface;
42import com.android.internal.telephony.PhoneBase;
43import com.android.internal.telephony.CommandsInterface.RadioState;
44import com.android.internal.telephony.IccCardConstants.State;
45import com.android.internal.telephony.gsm.GSMPhone;
46import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
47import com.android.internal.telephony.uicc.IccCardStatus.CardState;
48import com.android.internal.telephony.uicc.IccCardStatus.PinState;
49import com.android.internal.telephony.cat.CatService;
50import com.android.internal.telephony.cdma.CDMALTEPhone;
51import com.android.internal.telephony.cdma.CDMAPhone;
52import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
53
54import android.os.SystemProperties;
55
56import com.android.internal.R;
57
58import java.io.FileDescriptor;
59import java.io.PrintWriter;
60import java.util.List;
61
62/**
63 * {@hide}
64 */
65public class UiccCard {
66    protected static final String LOG_TAG = "UiccCard";
67    protected static final boolean DBG = true;
68
69    private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
70
71    private final Object mLock = new Object();
72    private CardState mCardState;
73    private PinState mUniversalPinState;
74    private int mGsmUmtsSubscriptionAppIndex;
75    private int mCdmaSubscriptionAppIndex;
76    private int mImsSubscriptionAppIndex;
77    private UiccCardApplication[] mUiccApplications =
78            new UiccCardApplication[IccCardStatus.CARD_MAX_APPS];
79    private Context mContext;
80    private CommandsInterface mCi;
81    private CatService mCatService;
82    private boolean mDestroyed = false; //set to true once this card is commanded to be disposed of.
83    private RadioState mLastRadioState =  RadioState.RADIO_UNAVAILABLE;
84    private UiccCarrierPrivilegeRules mCarrierPrivilegeRules;
85
86    private RegistrantList mAbsentRegistrants = new RegistrantList();
87
88    private static final int EVENT_CARD_REMOVED = 13;
89    private static final int EVENT_CARD_ADDED = 14;
90    private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 15;
91    private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 16;
92    private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 17;
93    private static final int EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE = 18;
94    private static final int EVENT_SIM_IO_DONE = 19;
95
96    private int mSlotId;
97
98    public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
99        if (DBG) log("Creating");
100        mCardState = ics.mCardState;
101        update(c, ci, ics);
102    }
103
104    public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int slotId) {
105        mCardState = ics.mCardState;
106        mSlotId = slotId;
107        update(c, ci, ics);
108    }
109
110    protected UiccCard() {
111    }
112
113    public void dispose() {
114        synchronized (mLock) {
115            if (DBG) log("Disposing card");
116            if (mCatService != null) mCatService.dispose();
117            for (UiccCardApplication app : mUiccApplications) {
118                if (app != null) {
119                    app.dispose();
120                }
121            }
122            mCatService = null;
123            mUiccApplications = null;
124            mCarrierPrivilegeRules = null;
125        }
126    }
127
128    public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
129        synchronized (mLock) {
130            if (mDestroyed) {
131                loge("Updated after destroyed! Fix me!");
132                return;
133            }
134            CardState oldState = mCardState;
135            mCardState = ics.mCardState;
136            mUniversalPinState = ics.mUniversalPinState;
137            mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
138            mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
139            mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
140            mContext = c;
141            mCi = ci;
142            //update applications
143            if (DBG) log(ics.mApplications.length + " applications");
144            for ( int i = 0; i < mUiccApplications.length; i++) {
145                if (mUiccApplications[i] == null) {
146                    //Create newly added Applications
147                    if (i < ics.mApplications.length) {
148                        mUiccApplications[i] = new UiccCardApplication(this,
149                                ics.mApplications[i], mContext, mCi);
150                    }
151                } else if (i >= ics.mApplications.length) {
152                    //Delete removed applications
153                    mUiccApplications[i].dispose();
154                    mUiccApplications[i] = null;
155                } else {
156                    //Update the rest
157                    mUiccApplications[i].update(ics.mApplications[i], mContext, mCi);
158                }
159            }
160
161            createAndUpdateCatService();
162
163            // Reload the carrier privilege rules if necessary.
164            log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + mCardState);
165            if (mCarrierPrivilegeRules == null && mCardState == CardState.CARDSTATE_PRESENT) {
166                mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this);
167            } else if (mCarrierPrivilegeRules != null && mCardState != CardState.CARDSTATE_PRESENT) {
168                mCarrierPrivilegeRules = null;
169            }
170
171            sanitizeApplicationIndexes();
172
173            RadioState radioState = mCi.getRadioState();
174            if (DBG) log("update: radioState=" + radioState + " mLastRadioState="
175                    + mLastRadioState);
176            // No notifications while radio is off or we just powering up
177            if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
178                if (oldState != CardState.CARDSTATE_ABSENT &&
179                        mCardState == CardState.CARDSTATE_ABSENT) {
180                    if (DBG) log("update: notify card removed");
181                    mAbsentRegistrants.notifyRegistrants();
182                    mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
183                } else if (oldState == CardState.CARDSTATE_ABSENT &&
184                        mCardState != CardState.CARDSTATE_ABSENT) {
185                    if (DBG) log("update: notify card added");
186                    mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
187                }
188            }
189            mLastRadioState = radioState;
190        }
191    }
192
193    protected void createAndUpdateCatService() {
194        if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
195            // Initialize or Reinitialize CatService
196            if (mCatService == null) {
197                mCatService = CatService.getInstance(mCi, mContext, this, mSlotId);
198            } else {
199                ((CatService)mCatService).update(mCi, mContext, this);
200            }
201        } else {
202            if (mCatService != null) {
203                mCatService.dispose();
204            }
205            mCatService = null;
206        }
207    }
208
209    public CatService getCatService() {
210        return mCatService;
211    }
212
213    @Override
214    protected void finalize() {
215        if (DBG) log("UiccCard finalized");
216    }
217
218    /**
219     * This function makes sure that application indexes are valid
220     * and resets invalid indexes. (This should never happen, but in case
221     * RIL misbehaves we need to manage situation gracefully)
222     */
223    private void sanitizeApplicationIndexes() {
224        mGsmUmtsSubscriptionAppIndex =
225                checkIndex(mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM);
226        mCdmaSubscriptionAppIndex =
227                checkIndex(mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM);
228        mImsSubscriptionAppIndex =
229                checkIndex(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null);
230    }
231
232    private int checkIndex(int index, AppType expectedAppType, AppType altExpectedAppType) {
233        if (mUiccApplications == null || index >= mUiccApplications.length) {
234            loge("App index " + index + " is invalid since there are no applications");
235            return -1;
236        }
237
238        if (index < 0) {
239            // This is normal. (i.e. no application of this type)
240            return -1;
241        }
242
243        if (mUiccApplications[index].getType() != expectedAppType &&
244            mUiccApplications[index].getType() != altExpectedAppType) {
245            loge("App index " + index + " is invalid since it's not " +
246                    expectedAppType + " and not " + altExpectedAppType);
247            return -1;
248        }
249
250        // Seems to be valid
251        return index;
252    }
253
254    /**
255     * Notifies handler of any transition into State.ABSENT
256     */
257    public void registerForAbsent(Handler h, int what, Object obj) {
258        synchronized (mLock) {
259            Registrant r = new Registrant (h, what, obj);
260
261            mAbsentRegistrants.add(r);
262
263            if (mCardState == CardState.CARDSTATE_ABSENT) {
264                r.notifyRegistrant();
265            }
266        }
267    }
268
269    public void unregisterForAbsent(Handler h) {
270        synchronized (mLock) {
271            mAbsentRegistrants.remove(h);
272        }
273    }
274
275    private void onIccSwap(boolean isAdded) {
276
277        boolean isHotSwapSupported = mContext.getResources().getBoolean(
278                com.android.internal.R.bool.config_hotswapCapable);
279
280        if (isHotSwapSupported) {
281            log("onIccSwap: isHotSwapSupported is true, don't prompt for rebooting");
282            return;
283        }
284        log("onIccSwap: isHotSwapSupported is false, prompt for rebooting");
285
286        synchronized (mLock) {
287            // TODO: Here we assume the device can't handle SIM hot-swap
288            //      and has to reboot. We may want to add a property,
289            //      e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
290            //      hot-swap.
291            DialogInterface.OnClickListener listener = null;
292
293
294            // TODO: SimRecords is not reset while SIM ABSENT (only reset while
295            //       Radio_off_or_not_available). Have to reset in both both
296            //       added or removed situation.
297            listener = new DialogInterface.OnClickListener() {
298                @Override
299                public void onClick(DialogInterface dialog, int which) {
300                    synchronized (mLock) {
301                        if (which == DialogInterface.BUTTON_POSITIVE) {
302                            if (DBG) log("Reboot due to SIM swap");
303                            PowerManager pm = (PowerManager) mContext
304                                    .getSystemService(Context.POWER_SERVICE);
305                            pm.reboot("SIM is added.");
306                        }
307                    }
308                }
309
310            };
311
312            Resources r = Resources.getSystem();
313
314            String title = (isAdded) ? r.getString(R.string.sim_added_title) :
315                r.getString(R.string.sim_removed_title);
316            String message = (isAdded) ? r.getString(R.string.sim_added_message) :
317                r.getString(R.string.sim_removed_message);
318            String buttonTxt = r.getString(R.string.sim_restart_button);
319
320            AlertDialog dialog = new AlertDialog.Builder(mContext)
321            .setTitle(title)
322            .setMessage(message)
323            .setPositiveButton(buttonTxt, listener)
324            .create();
325            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
326            dialog.show();
327        }
328    }
329
330    protected Handler mHandler = new Handler() {
331        @Override
332        public void handleMessage(Message msg){
333            if (mDestroyed) {
334                loge("Received message " + msg + "[" + msg.what
335                        + "] while being destroyed. Ignoring.");
336                return;
337            }
338
339            switch (msg.what) {
340                case EVENT_CARD_REMOVED:
341                    onIccSwap(false);
342                    break;
343                case EVENT_CARD_ADDED:
344                    onIccSwap(true);
345                    break;
346                case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
347                case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
348                case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE:
349                case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE:
350                case EVENT_SIM_IO_DONE:
351                    AsyncResult ar = (AsyncResult)msg.obj;
352                    if (ar.exception != null) {
353                       if (DBG)
354                         log("Error in SIM access with exception" + ar.exception);
355                    }
356                    AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception);
357                    ((Message)ar.userObj).sendToTarget();
358                    break;
359                default:
360                    loge("Unknown Event " + msg.what);
361            }
362        }
363    };
364
365    public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
366        synchronized (mLock) {
367            for (int i = 0 ; i < mUiccApplications.length; i++) {
368                if (mUiccApplications[i] != null && mUiccApplications[i].getType() == type) {
369                    return true;
370                }
371            }
372            return false;
373        }
374    }
375
376    public CardState getCardState() {
377        synchronized (mLock) {
378            return mCardState;
379        }
380    }
381
382    public PinState getUniversalPinState() {
383        synchronized (mLock) {
384            return mUniversalPinState;
385        }
386    }
387
388    public UiccCardApplication getApplication(int family) {
389        synchronized (mLock) {
390            int index = IccCardStatus.CARD_MAX_APPS;
391            switch (family) {
392                case UiccController.APP_FAM_3GPP:
393                    index = mGsmUmtsSubscriptionAppIndex;
394                    break;
395                case UiccController.APP_FAM_3GPP2:
396                    index = mCdmaSubscriptionAppIndex;
397                    break;
398                case UiccController.APP_FAM_IMS:
399                    index = mImsSubscriptionAppIndex;
400                    break;
401            }
402            if (index >= 0 && index < mUiccApplications.length) {
403                return mUiccApplications[index];
404            }
405            return null;
406        }
407    }
408
409    public UiccCardApplication getApplicationIndex(int index) {
410        synchronized (mLock) {
411            if (index >= 0 && index < mUiccApplications.length) {
412                return mUiccApplications[index];
413            }
414            return null;
415        }
416    }
417
418    /**
419     * Returns the SIM application of the specified type.
420     *
421     * @param type ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
422     * @return application corresponding to type or a null if no match found
423     */
424    public UiccCardApplication getApplicationByType(int type) {
425        synchronized (mLock) {
426            for (int i = 0 ; i < mUiccApplications.length; i++) {
427                if (mUiccApplications[i] != null &&
428                        mUiccApplications[i].getType().ordinal() == type) {
429                    return mUiccApplications[i];
430                }
431            }
432            return null;
433        }
434    }
435
436    /**
437     * Exposes {@link CommandsInterface.iccOpenLogicalChannel}
438     */
439    public void iccOpenLogicalChannel(String AID, Message response) {
440        mCi.iccOpenLogicalChannel(AID,
441                mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
442    }
443
444    /**
445     * Exposes {@link CommandsInterface.iccCloseLogicalChannel}
446     */
447    public void iccCloseLogicalChannel(int channel, Message response) {
448        mCi.iccCloseLogicalChannel(channel,
449                mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
450    }
451
452    /**
453     * Exposes {@link CommandsInterface.iccTransmitApduLogicalChannel}
454     */
455    public void iccTransmitApduLogicalChannel(int channel, int cla, int command,
456            int p1, int p2, int p3, String data, Message response) {
457        mCi.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3,
458                data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE, response));
459    }
460
461    /**
462     * Exposes {@link CommandsInterface.iccTransmitApduBasicChannel}
463     */
464    public void iccTransmitApduBasicChannel(int cla, int command,
465            int p1, int p2, int p3, String data, Message response) {
466        mCi.iccTransmitApduBasicChannel(cla, command, p1, p2, p3,
467                data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE, response));
468    }
469
470    /**
471     * Exposes {@link CommandsInterface.iccIO}
472     */
473    public void iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
474            String pathID, Message response) {
475        mCi.iccIO(command, fileID, pathID, p1, p2, p3, null, null,
476                mHandler.obtainMessage(EVENT_SIM_IO_DONE, response));
477    }
478
479    /**
480     * Exposes {@link CommandsInterface.sendEnvelopeWithStatus}
481     */
482    public void sendEnvelopeWithStatus(String contents, Message response) {
483        mCi.sendEnvelopeWithStatus(contents, response);
484    }
485
486    /* Returns number of applications on this card */
487    public int getNumApplications() {
488        int count = 0;
489        for (UiccCardApplication a : mUiccApplications) {
490            if (a != null) {
491                count++;
492            }
493        }
494        return count;
495    }
496
497    public int getSlotId() {
498        return mSlotId;
499    }
500
501    /**
502     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
503     */
504    public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
505        return mCarrierPrivilegeRules == null ?
506            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
507            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature, packageName);
508    }
509
510    /**
511     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
512     */
513    public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
514        return mCarrierPrivilegeRules == null ?
515            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
516            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(packageManager, packageName);
517    }
518
519    /**
520     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction}.
521     */
522    public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
523        return mCarrierPrivilegeRules == null ?
524            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
525            mCarrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction(packageManager);
526    }
527
528    /**
529     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPackageNamesForIntent}.
530     */
531    public List<String> getCarrierPackageNamesForIntent(
532            PackageManager packageManager, Intent intent) {
533        return mCarrierPrivilegeRules == null ? null :
534            mCarrierPrivilegeRules.getCarrierPackageNamesForIntent(
535                    packageManager, intent);
536    }
537
538    public boolean setOperatorBrandOverride(String iccId, String brand) {
539        log("setOperatorBrandOverride: " + iccId + " : " + brand);
540        log("current iccId: " + getIccId());
541
542        if (iccId.isEmpty() || !TextUtils.isDigitsOnly(iccId)) {
543            return false;
544        }
545
546        SharedPreferences.Editor spEditor =
547                PreferenceManager.getDefaultSharedPreferences(mContext).edit();
548        String key = OPERATOR_BRAND_OVERRIDE_PREFIX + iccId;
549        if (brand == null) {
550            spEditor.remove(key).commit();
551        } else {
552            spEditor.putString(key, brand).commit();
553        }
554        return true;
555    }
556
557    public String getOperatorBrandOverride() {
558        String iccId = getIccId();
559        if (iccId == null) {
560          return null;
561        }
562        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
563        return sp.getString(OPERATOR_BRAND_OVERRIDE_PREFIX + iccId, null);
564    }
565
566    private String getIccId() {
567        // ICCID should be same across all the apps.
568        for (UiccCardApplication app : mUiccApplications) {
569            if (app != null) {
570                IccRecords ir = app.getIccRecords();
571                if (ir != null && ir.getIccId() != null) {
572                    return ir.getIccId();
573                }
574            }
575        }
576        return null;
577    }
578
579    private void log(String msg) {
580        Rlog.d(LOG_TAG, msg);
581    }
582
583    private void loge(String msg) {
584        Rlog.e(LOG_TAG, msg);
585    }
586
587    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
588        pw.println("UiccCard:");
589        pw.println(" mCi=" + mCi);
590        pw.println(" mDestroyed=" + mDestroyed);
591        pw.println(" mLastRadioState=" + mLastRadioState);
592        pw.println(" mCatService=" + mCatService);
593        pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size());
594        for (int i = 0; i < mAbsentRegistrants.size(); i++) {
595            pw.println("  mAbsentRegistrants[" + i + "]="
596                    + ((Registrant)mAbsentRegistrants.get(i)).getHandler());
597        }
598        pw.println(" mCardState=" + mCardState);
599        pw.println(" mUniversalPinState=" + mUniversalPinState);
600        pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex);
601        pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex);
602        pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
603        pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
604        pw.println(" mUiccApplications: length=" + mUiccApplications.length);
605        for (int i = 0; i < mUiccApplications.length; i++) {
606            if (mUiccApplications[i] == null) {
607                pw.println("  mUiccApplications[" + i + "]=" + null);
608            } else {
609                pw.println("  mUiccApplications[" + i + "]="
610                        + mUiccApplications[i].getType() + " " + mUiccApplications[i]);
611            }
612        }
613        pw.println();
614        // Print details of all applications
615        for (UiccCardApplication app : mUiccApplications) {
616            if (app != null) {
617                app.dump(fd, pw, args);
618                pw.println();
619            }
620        }
621        // Print details of all IccRecords
622        for (UiccCardApplication app : mUiccApplications) {
623            if (app != null) {
624                IccRecords ir = app.getIccRecords();
625                if (ir != null) {
626                    ir.dump(fd, pw, args);
627                    pw.println();
628                }
629            }
630        }
631        pw.flush();
632    }
633}
634