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