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