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