UiccController.java revision 5d8e4457b03d166aa249989916b66a85df898516
1/*
2 * Copyright (C) 2011-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 android.content.Context;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Message;
23import android.os.Registrant;
24import android.os.RegistrantList;
25import android.telephony.TelephonyManager;
26import android.telephony.Rlog;
27
28import com.android.internal.telephony.CommandsInterface;
29import com.android.internal.telephony.PhoneConstants;
30import com.android.internal.telephony.SubscriptionController;
31
32import java.io.FileDescriptor;
33import java.io.PrintWriter;
34
35/**
36 * This class is responsible for keeping all knowledge about
37 * Universal Integrated Circuit Card (UICC), also know as SIM's,
38 * in the system. It is also used as API to get appropriate
39 * applications to pass them to phone and service trackers.
40 *
41 * UiccController is created with the call to make() function.
42 * UiccController is a singleton and make() must only be called once
43 * and throws an exception if called multiple times.
44 *
45 * Once created UiccController registers with RIL for "on" and "unsol_sim_status_changed"
46 * notifications. When such notification arrives UiccController will call
47 * getIccCardStatus (GET_SIM_STATUS). Based on the response of GET_SIM_STATUS
48 * request appropriate tree of uicc objects will be created.
49 *
50 * Following is class diagram for uicc classes:
51 *
52 *                       UiccController
53 *                            #
54 *                            |
55 *                        UiccCard
56 *                          #   #
57 *                          |   ------------------
58 *                    UiccCardApplication    CatService
59 *                      #            #
60 *                      |            |
61 *                 IccRecords    IccFileHandler
62 *                 ^ ^ ^           ^ ^ ^ ^ ^
63 *    SIMRecords---- | |           | | | | ---SIMFileHandler
64 *    RuimRecords----- |           | | | ----RuimFileHandler
65 *    IsimUiccRecords---           | | -----UsimFileHandler
66 *                                 | ------CsimFileHandler
67 *                                 ----IsimFileHandler
68 *
69 * Legend: # stands for Composition
70 *         ^ stands for Generalization
71 *
72 * See also {@link com.android.internal.telephony.IccCard}
73 * and {@link com.android.internal.telephony.uicc.IccCardProxy}
74 */
75public class UiccController extends Handler {
76    private static final boolean DBG = true;
77    private static final String LOG_TAG = "UiccController";
78
79    public static final int APP_FAM_3GPP =  1;
80    public static final int APP_FAM_3GPP2 = 2;
81    public static final int APP_FAM_IMS   = 3;
82
83    private static final int EVENT_ICC_STATUS_CHANGED = 1;
84    private static final int EVENT_GET_ICC_STATUS_DONE = 2;
85    private static final int EVENT_RADIO_UNAVAILABLE = 3;
86
87    private CommandsInterface[] mCis;
88    private UiccCard[] mUiccCards = new UiccCard[TelephonyManager.getDefault().getPhoneCount()];
89
90    private static final Object mLock = new Object();
91    private static UiccController mInstance;
92
93    private Context mContext;
94/*
95    private CommandsInterface mCi;
96    private UiccCard mUiccCard;
97*/
98
99    protected RegistrantList mIccChangedRegistrants = new RegistrantList();
100
101/*
102    public static UiccController make(Context c, CommandsInterface ci) {
103        synchronized (mLock) {
104            if (mInstance != null) {
105                throw new RuntimeException("UiccController.make() should only be called once");
106            }
107            mInstance = new UiccController(c, ci);
108            return mInstance;
109        }
110    }
111*/
112
113    public static UiccController make(Context c, CommandsInterface[] ci) {
114        synchronized (mLock) {
115            if (mInstance != null) {
116                throw new RuntimeException("MSimUiccController.make() should only be called once");
117            }
118            mInstance = new UiccController(c, ci);
119            return (UiccController)mInstance;
120        }
121    }
122
123    private UiccController(Context c, CommandsInterface []ci) {
124        if (DBG) log("Creating UiccController");
125        mContext = c;
126        mCis = ci;
127        for (int i = 0; i < mCis.length; i++) {
128            Integer index = new Integer(i);
129            mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
130            // TODO remove this once modem correctly notifies the unsols
131            mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
132            mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index);
133        }
134    }
135
136    public static UiccController getInstance() {
137        synchronized (mLock) {
138            if (mInstance == null) {
139                throw new RuntimeException(
140                        "UiccController.getInstance can't be called before make()");
141            }
142            return mInstance;
143        }
144    }
145
146    public UiccCard getUiccCard() {
147        return getUiccCard(SubscriptionController.getInstance().getPhoneId(SubscriptionController.getInstance().getDefaultSubId()));
148    }
149
150    public UiccCard getUiccCard(int slotId) {
151        synchronized (mLock) {
152            if (isValidCardIndex(slotId)) {
153                return mUiccCards[slotId];
154            }
155            return null;
156        }
157    }
158
159    public UiccCard[] getUiccCards() {
160        // Return cloned array since we don't want to give out reference
161        // to internal data structure.
162        synchronized (mLock) {
163            return mUiccCards.clone();
164        }
165    }
166
167    // Easy to use API
168    public UiccCardApplication getUiccCardApplication(int family) {
169        return getUiccCardApplication(SubscriptionController.getInstance().getPhoneId(SubscriptionController.getInstance().getDefaultSubId()), family);
170    }
171
172/*
173    // Easy to use API
174    public IccRecords getIccRecords(int family) {
175        synchronized (mLock) {
176            if (mUiccCard != null) {
177                UiccCardApplication app = mUiccCard.getApplication(family);
178                if (app != null) {
179                    return app.getIccRecords();
180                }
181            }
182            return null;
183        }
184    }
185*/
186
187    // Easy to use API
188    public IccRecords getIccRecords(int slotId, int family) {
189        synchronized (mLock) {
190            UiccCardApplication app = getUiccCardApplication(slotId, family);
191            if (app != null) {
192                return app.getIccRecords();
193            }
194            return null;
195        }
196    }
197
198/*
199    // Easy to use API
200    public IccFileHandler getIccFileHandler(int family) {
201        synchronized (mLock) {
202            if (mUiccCard != null) {
203                UiccCardApplication app = mUiccCard.getApplication(family);
204                if (app != null) {
205                    return app.getIccFileHandler();
206                }
207            }
208            return null;
209        }
210    }
211*/
212
213    // Easy to use API
214    public IccFileHandler getIccFileHandler(int slotId, int family) {
215        synchronized (mLock) {
216            UiccCardApplication app = getUiccCardApplication(slotId, family);
217            if (app != null) {
218                return app.getIccFileHandler();
219            }
220            return null;
221        }
222    }
223
224
225    //Notifies when card status changes
226    public void registerForIccChanged(Handler h, int what, Object obj) {
227        synchronized (mLock) {
228            Registrant r = new Registrant (h, what, obj);
229            mIccChangedRegistrants.add(r);
230            //Notify registrant right after registering, so that it will get the latest ICC status,
231            //otherwise which may not happen until there is an actual change in ICC status.
232            r.notifyRegistrant();
233        }
234    }
235
236    public void unregisterForIccChanged(Handler h) {
237        synchronized (mLock) {
238            mIccChangedRegistrants.remove(h);
239        }
240    }
241
242    @Override
243    public void handleMessage (Message msg) {
244        synchronized (mLock) {
245            Integer index = getCiIndex(msg);
246
247            if (index < 0 || index >= mCis.length) {
248                Rlog.e(LOG_TAG, "Invalid index : " + index + " received with event " + msg.what);
249                return;
250            }
251
252            switch (msg.what) {
253                case EVENT_ICC_STATUS_CHANGED:
254                    if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
255                    mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
256                    break;
257                case EVENT_GET_ICC_STATUS_DONE:
258                    if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
259                    AsyncResult ar = (AsyncResult)msg.obj;
260                    onGetIccCardStatusDone(ar, index);
261                    break;
262                case EVENT_RADIO_UNAVAILABLE:
263                    if (DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card");
264                    if (mUiccCards[index] != null) {
265                        mUiccCards[index].dispose();
266                    }
267                    mUiccCards[index] = null;
268                    mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
269                    break;
270                default:
271                    Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
272            }
273        }
274    }
275
276    private Integer getCiIndex(Message msg) {
277        AsyncResult ar;
278        Integer index = new Integer(PhoneConstants.DEFAULT_CARD_INDEX);
279
280        /*
281         * The events can be come in two ways. By explicitly sending it using
282         * sendMessage, in this case the user object passed is msg.obj and from
283         * the CommandsInterface, in this case the user object is msg.obj.userObj
284         */
285        if (msg != null) {
286            if (msg.obj != null && msg.obj instanceof Integer) {
287                index = (Integer)msg.obj;
288            } else if(msg.obj != null && msg.obj instanceof AsyncResult) {
289                ar = (AsyncResult)msg.obj;
290                if (ar.userObj != null && ar.userObj instanceof Integer) {
291                    index = (Integer)ar.userObj;
292                }
293            }
294        }
295        return index;
296    }
297
298/*
299    private UiccController(Context c, CommandsInterface ci) {
300        if (DBG) log("Creating UiccController");
301        mContext = c;
302        mCi = ci;
303        mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null);
304        // This is needed so that we query for sim status in the case when we boot in APM
305        mCi.registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, null);
306    }
307*/
308
309    // Easy to use API
310    public UiccCardApplication getUiccCardApplication(int slotId, int family) {
311        synchronized (mLock) {
312            if (isValidCardIndex(slotId)) {
313                UiccCard c = mUiccCards[slotId];
314                if (c != null) {
315                    return mUiccCards[slotId].getApplication(family);
316                }
317            }
318            return null;
319        }
320    }
321
322    private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
323        if (ar.exception != null) {
324            Rlog.e(LOG_TAG,"Error getting ICC status. "
325                    + "RIL_REQUEST_GET_ICC_STATUS should "
326                    + "never return an error", ar.exception);
327            return;
328        }
329        if (!isValidCardIndex(index)) {
330            Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);
331            return;
332        }
333
334        IccCardStatus status = (IccCardStatus)ar.result;
335
336        if (mUiccCards[index] == null) {
337            //Create new card
338            mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index);
339
340/*
341            // Update the UiccCard in base class, so that if someone calls
342            // UiccManager.getUiccCard(), it will return the default card.
343            if (index == PhoneConstants.DEFAULT_CARD_INDEX) {
344                mUiccCard = mUiccCards[index];
345            }
346*/
347        } else {
348            //Update already existing card
349            mUiccCards[index].update(mContext, mCis[index] , status);
350        }
351
352        if (DBG) log("Notifying IccChangedRegistrants");
353        mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
354
355    }
356
357    private boolean isValidCardIndex(int index) {
358        return (index >= 0 && index < mUiccCards.length);
359    }
360
361    private void log(String string) {
362        Rlog.d(LOG_TAG, string);
363    }
364
365
366    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
367        pw.println("UiccController: " + this);
368        pw.println(" mContext=" + mContext);
369        pw.println(" mInstance=" + mInstance);
370//        pw.println(" mCi=" + mCi);
371//        pw.println(" mUiccCard=" + mUiccCard);
372        pw.println(" mIccChangedRegistrants: size=" + mIccChangedRegistrants.size());
373        for (int i = 0; i < mIccChangedRegistrants.size(); i++) {
374            pw.println("  mIccChangedRegistrants[" + i + "]="
375                    + ((Registrant)mIccChangedRegistrants.get(i)).getHandler());
376        }
377        pw.println();
378        pw.flush();
379//        for (int i = 0; i < mUiccCards.length; i++) {
380//            mUiccCards[i].dump(fd, pw, args);
381//        }
382    }
383}
384