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.util.Log;
26
27import com.android.internal.telephony.CommandsInterface;
28import com.android.internal.telephony.IccCardStatus;
29import com.android.internal.telephony.IccFileHandler;
30import com.android.internal.telephony.IccRecords;
31import com.android.internal.telephony.PhoneConstants;
32import com.android.internal.telephony.UiccCard;
33import com.android.internal.telephony.UiccCardApplication;
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.IccCardProxy}
74 */
75public class UiccController extends Handler {
76    private static final boolean DBG = true;
77    private static final String LOG_TAG = "RIL_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
86    private static final Object mLock = new Object();
87    private static UiccController mInstance;
88
89    private Context mContext;
90    private CommandsInterface mCi;
91    private UiccCard mUiccCard;
92
93    private RegistrantList mIccChangedRegistrants = new RegistrantList();
94
95    public static UiccController make(Context c, CommandsInterface ci) {
96        synchronized (mLock) {
97            if (mInstance != null) {
98                throw new RuntimeException("UiccController.make() should only be called once");
99            }
100            mInstance = new UiccController(c, ci);
101            return mInstance;
102        }
103    }
104
105    public static UiccController getInstance() {
106        synchronized (mLock) {
107            if (mInstance == null) {
108                throw new RuntimeException(
109                        "UiccController.getInstance can't be called before make()");
110            }
111            return mInstance;
112        }
113    }
114
115    public UiccCard getUiccCard() {
116        synchronized (mLock) {
117            return mUiccCard;
118        }
119    }
120
121    // Easy to use API
122    public UiccCardApplication getUiccCardApplication(int family) {
123        synchronized (mLock) {
124            if (mUiccCard != null) {
125                return mUiccCard.getApplication(family);
126            }
127            return null;
128        }
129    }
130
131    // Easy to use API
132    public IccRecords getIccRecords(int family) {
133        synchronized (mLock) {
134            if (mUiccCard != null) {
135                UiccCardApplication app = mUiccCard.getApplication(family);
136                if (app != null) {
137                    return app.getIccRecords();
138                }
139            }
140            return null;
141        }
142    }
143
144    // Easy to use API
145    public IccFileHandler getIccFileHandler(int family) {
146        synchronized (mLock) {
147            if (mUiccCard != null) {
148                UiccCardApplication app = mUiccCard.getApplication(family);
149                if (app != null) {
150                    return app.getIccFileHandler();
151                }
152            }
153            return null;
154        }
155    }
156
157    //Notifies when card status changes
158    public void registerForIccChanged(Handler h, int what, Object obj) {
159        synchronized (mLock) {
160            Registrant r = new Registrant (h, what, obj);
161            mIccChangedRegistrants.add(r);
162            //Notify registrant right after registering, so that it will get the latest ICC status,
163            //otherwise which may not happen until there is an actual change in ICC status.
164            r.notifyRegistrant();
165        }
166    }
167
168    public void unregisterForIccChanged(Handler h) {
169        synchronized (mLock) {
170            mIccChangedRegistrants.remove(h);
171        }
172    }
173
174    @Override
175    public void handleMessage (Message msg) {
176        synchronized (mLock) {
177            switch (msg.what) {
178                case EVENT_ICC_STATUS_CHANGED:
179                    if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
180                    mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
181                    break;
182                case EVENT_GET_ICC_STATUS_DONE:
183                    if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
184                    AsyncResult ar = (AsyncResult)msg.obj;
185                    onGetIccCardStatusDone(ar);
186                    break;
187                default:
188                    Log.e(LOG_TAG, " Unknown Event " + msg.what);
189            }
190        }
191    }
192
193    private UiccController(Context c, CommandsInterface ci) {
194        if (DBG) log("Creating UiccController");
195        mContext = c;
196        mCi = ci;
197        mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null);
198        // TODO remove this once modem correctly notifies the unsols
199        mCi.registerForOn(this, EVENT_ICC_STATUS_CHANGED, null);
200    }
201
202    private synchronized void onGetIccCardStatusDone(AsyncResult ar) {
203        if (ar.exception != null) {
204            Log.e(LOG_TAG,"Error getting ICC status. "
205                    + "RIL_REQUEST_GET_ICC_STATUS should "
206                    + "never return an error", ar.exception);
207            return;
208        }
209
210        IccCardStatus status = (IccCardStatus)ar.result;
211
212        if (mUiccCard == null) {
213            //Create new card
214            mUiccCard = new UiccCard(mContext, mCi, status);
215        } else {
216            //Update already existing card
217            mUiccCard.update(mContext, mCi , status);
218        }
219
220        if (DBG) log("Notifying IccChangedRegistrants");
221        mIccChangedRegistrants.notifyRegistrants();
222    }
223
224    private void log(String string) {
225        Log.d(LOG_TAG, string);
226    }
227}
228