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.Rlog;
26
27import com.android.internal.telephony.CommandsInterface;
28
29import java.io.FileDescriptor;
30import java.io.PrintWriter;
31
32/**
33 * This class is responsible for keeping all knowledge about
34 * Universal Integrated Circuit Card (UICC), also know as SIM's,
35 * in the system. It is also used as API to get appropriate
36 * applications to pass them to phone and service trackers.
37 *
38 * UiccController is created with the call to make() function.
39 * UiccController is a singleton and make() must only be called once
40 * and throws an exception if called multiple times.
41 *
42 * Once created UiccController registers with RIL for "on" and "unsol_sim_status_changed"
43 * notifications. When such notification arrives UiccController will call
44 * getIccCardStatus (GET_SIM_STATUS). Based on the response of GET_SIM_STATUS
45 * request appropriate tree of uicc objects will be created.
46 *
47 * Following is class diagram for uicc classes:
48 *
49 *                       UiccController
50 *                            #
51 *                            |
52 *                        UiccCard
53 *                          #   #
54 *                          |   ------------------
55 *                    UiccCardApplication    CatService
56 *                      #            #
57 *                      |            |
58 *                 IccRecords    IccFileHandler
59 *                 ^ ^ ^           ^ ^ ^ ^ ^
60 *    SIMRecords---- | |           | | | | ---SIMFileHandler
61 *    RuimRecords----- |           | | | ----RuimFileHandler
62 *    IsimUiccRecords---           | | -----UsimFileHandler
63 *                                 | ------CsimFileHandler
64 *                                 ----IsimFileHandler
65 *
66 * Legend: # stands for Composition
67 *         ^ stands for Generalization
68 *
69 * See also {@link com.android.internal.telephony.IccCard}
70 * and {@link com.android.internal.telephony.uicc.IccCardProxy}
71 */
72public class UiccController extends Handler {
73    private static final boolean DBG = true;
74    private static final String LOG_TAG = "UiccController";
75
76    public static final int APP_FAM_3GPP =  1;
77    public static final int APP_FAM_3GPP2 = 2;
78    public static final int APP_FAM_IMS   = 3;
79
80    private static final int EVENT_ICC_STATUS_CHANGED = 1;
81    private static final int EVENT_GET_ICC_STATUS_DONE = 2;
82
83    private static final Object mLock = new Object();
84    private static UiccController mInstance;
85
86    private Context mContext;
87    private CommandsInterface mCi;
88    private UiccCard mUiccCard;
89
90    private RegistrantList mIccChangedRegistrants = new RegistrantList();
91
92    public static UiccController make(Context c, CommandsInterface ci) {
93        synchronized (mLock) {
94            if (mInstance != null) {
95                throw new RuntimeException("UiccController.make() should only be called once");
96            }
97            mInstance = new UiccController(c, ci);
98            return mInstance;
99        }
100    }
101
102    public static UiccController getInstance() {
103        synchronized (mLock) {
104            if (mInstance == null) {
105                throw new RuntimeException(
106                        "UiccController.getInstance can't be called before make()");
107            }
108            return mInstance;
109        }
110    }
111
112    public UiccCard getUiccCard() {
113        synchronized (mLock) {
114            return mUiccCard;
115        }
116    }
117
118    // Easy to use API
119    public UiccCardApplication getUiccCardApplication(int family) {
120        synchronized (mLock) {
121            if (mUiccCard != null) {
122                return mUiccCard.getApplication(family);
123            }
124            return null;
125        }
126    }
127
128    // Easy to use API
129    public IccRecords getIccRecords(int family) {
130        synchronized (mLock) {
131            if (mUiccCard != null) {
132                UiccCardApplication app = mUiccCard.getApplication(family);
133                if (app != null) {
134                    return app.getIccRecords();
135                }
136            }
137            return null;
138        }
139    }
140
141    // Easy to use API
142    public IccFileHandler getIccFileHandler(int family) {
143        synchronized (mLock) {
144            if (mUiccCard != null) {
145                UiccCardApplication app = mUiccCard.getApplication(family);
146                if (app != null) {
147                    return app.getIccFileHandler();
148                }
149            }
150            return null;
151        }
152    }
153
154    //Notifies when card status changes
155    public void registerForIccChanged(Handler h, int what, Object obj) {
156        synchronized (mLock) {
157            Registrant r = new Registrant (h, what, obj);
158            mIccChangedRegistrants.add(r);
159            //Notify registrant right after registering, so that it will get the latest ICC status,
160            //otherwise which may not happen until there is an actual change in ICC status.
161            r.notifyRegistrant();
162        }
163    }
164
165    public void unregisterForIccChanged(Handler h) {
166        synchronized (mLock) {
167            mIccChangedRegistrants.remove(h);
168        }
169    }
170
171    @Override
172    public void handleMessage (Message msg) {
173        synchronized (mLock) {
174            switch (msg.what) {
175                case EVENT_ICC_STATUS_CHANGED:
176                    if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
177                    mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
178                    break;
179                case EVENT_GET_ICC_STATUS_DONE:
180                    if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
181                    AsyncResult ar = (AsyncResult)msg.obj;
182                    onGetIccCardStatusDone(ar);
183                    break;
184                default:
185                    Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
186            }
187        }
188    }
189
190    private UiccController(Context c, CommandsInterface ci) {
191        if (DBG) log("Creating UiccController");
192        mContext = c;
193        mCi = ci;
194        mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null);
195        // TODO remove this once modem correctly notifies the unsols
196        mCi.registerForOn(this, EVENT_ICC_STATUS_CHANGED, null);
197    }
198
199    private synchronized void onGetIccCardStatusDone(AsyncResult ar) {
200        if (ar.exception != null) {
201            Rlog.e(LOG_TAG,"Error getting ICC status. "
202                    + "RIL_REQUEST_GET_ICC_STATUS should "
203                    + "never return an error", ar.exception);
204            return;
205        }
206
207        IccCardStatus status = (IccCardStatus)ar.result;
208
209        if (mUiccCard == null) {
210            //Create new card
211            mUiccCard = new UiccCard(mContext, mCi, status);
212        } else {
213            //Update already existing card
214            mUiccCard.update(mContext, mCi , status);
215        }
216
217        if (DBG) log("Notifying IccChangedRegistrants");
218        mIccChangedRegistrants.notifyRegistrants();
219    }
220
221    private void log(String string) {
222        Rlog.d(LOG_TAG, string);
223    }
224
225    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
226        pw.println("UiccController: " + this);
227        pw.println(" mContext=" + mContext);
228        pw.println(" mInstance=" + mInstance);
229        pw.println(" mCi=" + mCi);
230        pw.println(" mUiccCard=" + mUiccCard);
231        pw.println(" mIccChangedRegistrants: size=" + mIccChangedRegistrants.size());
232        for (int i = 0; i < mIccChangedRegistrants.size(); i++) {
233            pw.println("  mIccChangedRegistrants[" + i + "]="
234                    + ((Registrant)mIccChangedRegistrants.get(i)).getHandler());
235        }
236        pw.println();
237        pw.flush();
238        if (mUiccCard != null) {
239            mUiccCard.dump(fd, pw, args);
240        }
241    }
242}
243