• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.storage.StorageManager;
27 import android.telephony.TelephonyManager;
28 import android.telephony.Rlog;
29 import android.text.format.Time;
30 
31 import com.android.internal.telephony.CommandsInterface;
32 import com.android.internal.telephony.PhoneConstants;
33 import com.android.internal.telephony.SubscriptionController;
34 
35 import java.io.FileDescriptor;
36 import java.io.PrintWriter;
37 import 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  */
79 public 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 
make(Context c, CommandsInterface[] ci)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 
UiccController(Context c, CommandsInterface []ci)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 
getInstance()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 
getUiccCard(int phoneId)151     public UiccCard getUiccCard(int phoneId) {
152         synchronized (mLock) {
153             if (isValidCardIndex(phoneId)) {
154                 return mUiccCards[phoneId];
155             }
156             return null;
157         }
158     }
159 
getUiccCards()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
getIccRecords(int phoneId, int family)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
getIccFileHandler(int phoneId, int family)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
registerForIccChanged(Handler h, int what, Object obj)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 
unregisterForIccChanged(Handler h)202     public void unregisterForIccChanged(Handler h) {
203         synchronized (mLock) {
204             mIccChangedRegistrants.remove(h);
205         }
206     }
207 
208     @Override
handleMessage(Message msg)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 
getCiIndex(Message msg)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
getUiccCardApplication(int phoneId, int family)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 
onGetIccCardStatusDone(AsyncResult ar, Integer index)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 
onSimRefresh(AsyncResult ar, Integer index)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 
isValidCardIndex(int index)347     private boolean isValidCardIndex(int index) {
348         return (index >= 0 && index < mUiccCards.length);
349     }
350 
log(String string)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.
addCardLog(String data)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 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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