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