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