UiccCard.java revision 934a24a6aa2b7a981e872fa350648904ed80280a
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    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    /**
498     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
499     */
500    public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
501        return mCarrierPrivilegeRules == null ?
502            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
503            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature, packageName);
504    }
505
506    /**
507     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
508     */
509    public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
510        return mCarrierPrivilegeRules == null ?
511            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
512            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(packageManager, packageName);
513    }
514
515    /**
516     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction}.
517     */
518    public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
519        return mCarrierPrivilegeRules == null ?
520            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
521            mCarrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction(packageManager);
522    }
523
524    /**
525     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPackageNamesForIntent}.
526     */
527    public List<String> getCarrierPackageNamesForIntent(
528            PackageManager packageManager, Intent intent) {
529        return mCarrierPrivilegeRules == null ? null :
530            mCarrierPrivilegeRules.getCarrierPackageNamesForIntent(
531                    packageManager, intent);
532    }
533
534    public boolean setOperatorBrandOverride(String iccId, String brand) {
535        log("setOperatorBrandOverride: " + iccId + " : " + brand);
536        log("current iccId: " + getIccId());
537
538        if (iccId.isEmpty() || !TextUtils.isDigitsOnly(iccId)) {
539            return false;
540        }
541
542        SharedPreferences.Editor spEditor =
543                PreferenceManager.getDefaultSharedPreferences(mContext).edit();
544        String key = OPERATOR_BRAND_OVERRIDE_PREFIX + iccId;
545        if (brand == null) {
546            spEditor.remove(key).commit();
547        } else {
548            spEditor.putString(key, brand).commit();
549        }
550        return true;
551    }
552
553    public String getOperatorBrandOverride() {
554        String iccId = getIccId();
555        if (iccId == null) {
556          return null;
557        }
558        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
559        return sp.getString(OPERATOR_BRAND_OVERRIDE_PREFIX + iccId, null);
560    }
561
562    private String getIccId() {
563        // ICCID should be same across all the apps.
564        for (UiccCardApplication app : mUiccApplications) {
565            if (app != null) {
566                IccRecords ir = app.getIccRecords();
567                if (ir != null && ir.getIccId() != null) {
568                    return ir.getIccId();
569                }
570            }
571        }
572        return null;
573    }
574
575    private void log(String msg) {
576        Rlog.d(LOG_TAG, msg);
577    }
578
579    private void loge(String msg) {
580        Rlog.e(LOG_TAG, msg);
581    }
582
583    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
584        pw.println("UiccCard:");
585        pw.println(" mCi=" + mCi);
586        pw.println(" mDestroyed=" + mDestroyed);
587        pw.println(" mLastRadioState=" + mLastRadioState);
588        pw.println(" mCatService=" + mCatService);
589        pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size());
590        for (int i = 0; i < mAbsentRegistrants.size(); i++) {
591            pw.println("  mAbsentRegistrants[" + i + "]="
592                    + ((Registrant)mAbsentRegistrants.get(i)).getHandler());
593        }
594        pw.println(" mCardState=" + mCardState);
595        pw.println(" mUniversalPinState=" + mUniversalPinState);
596        pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex);
597        pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex);
598        pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
599        pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
600        pw.println(" mUiccApplications: length=" + mUiccApplications.length);
601        for (int i = 0; i < mUiccApplications.length; i++) {
602            if (mUiccApplications[i] == null) {
603                pw.println("  mUiccApplications[" + i + "]=" + null);
604            } else {
605                pw.println("  mUiccApplications[" + i + "]="
606                        + mUiccApplications[i].getType() + " " + mUiccApplications[i]);
607            }
608        }
609        pw.println();
610        // Print details of all applications
611        for (UiccCardApplication app : mUiccApplications) {
612            if (app != null) {
613                app.dump(fd, pw, args);
614                pw.println();
615            }
616        }
617        // Print details of all IccRecords
618        for (UiccCardApplication app : mUiccApplications) {
619            if (app != null) {
620                IccRecords ir = app.getIccRecords();
621                if (ir != null) {
622                    ir.dump(fd, pw, args);
623                    pw.println();
624                }
625            }
626        }
627        pw.flush();
628    }
629}
630