• 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 CommandsInterface[] mCis;
93     private UiccCard[] mUiccCards = new UiccCard[TelephonyManager.getDefault().getPhoneCount()];
94 
95     private static final Object mLock = new Object();
96     private static UiccController mInstance;
97 
98     private Context mContext;
99 
100     protected RegistrantList mIccChangedRegistrants = new RegistrantList();
101 
102     private UiccStateChangedLauncher mLauncher;
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 is unencrypted or has been decrypted or FBE is supported,
127             // i.e. not in cryptkeeper bounce, read SIM when radio state isavailable.
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 (!StorageManager.inCryptKeeperBounce()) {
131                 mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
132             } else {
133                 mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
134             }
135             mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index);
136             mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, index);
137         }
138 
139         mLauncher = new UiccStateChangedLauncher(c, this);
140     }
141 
getInstance()142     public static UiccController getInstance() {
143         synchronized (mLock) {
144             if (mInstance == null) {
145                 throw new RuntimeException(
146                         "UiccController.getInstance can't be called before make()");
147             }
148             return mInstance;
149         }
150     }
151 
getUiccCard(int phoneId)152     public UiccCard getUiccCard(int phoneId) {
153         synchronized (mLock) {
154             if (isValidCardIndex(phoneId)) {
155                 return mUiccCards[phoneId];
156             }
157             return null;
158         }
159     }
160 
getUiccCards()161     public UiccCard[] getUiccCards() {
162         // Return cloned array since we don't want to give out reference
163         // to internal data structure.
164         synchronized (mLock) {
165             return mUiccCards.clone();
166         }
167     }
168 
169     // Easy to use API
getIccRecords(int phoneId, int family)170     public IccRecords getIccRecords(int phoneId, int family) {
171         synchronized (mLock) {
172             UiccCardApplication app = getUiccCardApplication(phoneId, family);
173             if (app != null) {
174                 return app.getIccRecords();
175             }
176             return null;
177         }
178     }
179 
180     // Easy to use API
getIccFileHandler(int phoneId, int family)181     public IccFileHandler getIccFileHandler(int phoneId, int family) {
182         synchronized (mLock) {
183             UiccCardApplication app = getUiccCardApplication(phoneId, family);
184             if (app != null) {
185                 return app.getIccFileHandler();
186             }
187             return null;
188         }
189     }
190 
191 
192     //Notifies when card status changes
registerForIccChanged(Handler h, int what, Object obj)193     public void registerForIccChanged(Handler h, int what, Object obj) {
194         synchronized (mLock) {
195             Registrant r = new Registrant (h, what, obj);
196             mIccChangedRegistrants.add(r);
197             //Notify registrant right after registering, so that it will get the latest ICC status,
198             //otherwise which may not happen until there is an actual change in ICC status.
199             r.notifyRegistrant();
200         }
201     }
202 
unregisterForIccChanged(Handler h)203     public void unregisterForIccChanged(Handler h) {
204         synchronized (mLock) {
205             mIccChangedRegistrants.remove(h);
206         }
207     }
208 
209     @Override
handleMessage(Message msg)210     public void handleMessage (Message msg) {
211         synchronized (mLock) {
212             Integer index = getCiIndex(msg);
213 
214             if (index < 0 || index >= mCis.length) {
215                 Rlog.e(LOG_TAG, "Invalid index : " + index + " received with event " + msg.what);
216                 return;
217             }
218 
219             AsyncResult ar = (AsyncResult)msg.obj;
220             switch (msg.what) {
221                 case EVENT_ICC_STATUS_CHANGED:
222                     if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
223                     mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
224                     break;
225                 case EVENT_GET_ICC_STATUS_DONE:
226                     if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
227                     onGetIccCardStatusDone(ar, index);
228                     break;
229                 case EVENT_RADIO_UNAVAILABLE:
230                     if (DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card");
231                     if (mUiccCards[index] != null) {
232                         mUiccCards[index].dispose();
233                     }
234                     mUiccCards[index] = null;
235                     mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
236                     break;
237                 case EVENT_SIM_REFRESH:
238                     if (DBG) log("Received EVENT_SIM_REFRESH");
239                     onSimRefresh(ar, index);
240                     break;
241                 default:
242                     Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
243             }
244         }
245     }
246 
getCiIndex(Message msg)247     private Integer getCiIndex(Message msg) {
248         AsyncResult ar;
249         Integer index = new Integer(PhoneConstants.DEFAULT_CARD_INDEX);
250 
251         /*
252          * The events can be come in two ways. By explicitly sending it using
253          * sendMessage, in this case the user object passed is msg.obj and from
254          * the CommandsInterface, in this case the user object is msg.obj.userObj
255          */
256         if (msg != null) {
257             if (msg.obj != null && msg.obj instanceof Integer) {
258                 index = (Integer)msg.obj;
259             } else if(msg.obj != null && msg.obj instanceof AsyncResult) {
260                 ar = (AsyncResult)msg.obj;
261                 if (ar.userObj != null && ar.userObj instanceof Integer) {
262                     index = (Integer)ar.userObj;
263                 }
264             }
265         }
266         return index;
267     }
268 
269     // Easy to use API
getUiccCardApplication(int phoneId, int family)270     public UiccCardApplication getUiccCardApplication(int phoneId, int family) {
271         synchronized (mLock) {
272             if (isValidCardIndex(phoneId)) {
273                 UiccCard c = mUiccCards[phoneId];
274                 if (c != null) {
275                     return mUiccCards[phoneId].getApplication(family);
276                 }
277             }
278             return null;
279         }
280     }
281 
onGetIccCardStatusDone(AsyncResult ar, Integer index)282     private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
283         if (ar.exception != null) {
284             Rlog.e(LOG_TAG,"Error getting ICC status. "
285                     + "RIL_REQUEST_GET_ICC_STATUS should "
286                     + "never return an error", ar.exception);
287             return;
288         }
289         if (!isValidCardIndex(index)) {
290             Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);
291             return;
292         }
293 
294         IccCardStatus status = (IccCardStatus)ar.result;
295 
296         if (mUiccCards[index] == null) {
297             //Create new card
298             mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index);
299         } else {
300             //Update already existing card
301             mUiccCards[index].update(mContext, mCis[index] , status);
302         }
303 
304         if (DBG) log("Notifying IccChangedRegistrants");
305         mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
306 
307     }
308 
onSimRefresh(AsyncResult ar, Integer index)309     private void onSimRefresh(AsyncResult ar, Integer index) {
310         if (ar.exception != null) {
311             Rlog.e(LOG_TAG, "Sim REFRESH with exception: " + ar.exception);
312             return;
313         }
314 
315         if (!isValidCardIndex(index)) {
316             Rlog.e(LOG_TAG,"onSimRefresh: invalid index : " + index);
317             return;
318         }
319 
320         IccRefreshResponse resp = (IccRefreshResponse) ar.result;
321         Rlog.d(LOG_TAG, "onSimRefresh: " + resp);
322 
323         if (mUiccCards[index] == null) {
324             Rlog.e(LOG_TAG,"onSimRefresh: refresh on null card : " + index);
325             return;
326         }
327 
328         if (resp.refreshResult != IccRefreshResponse.REFRESH_RESULT_RESET) {
329           Rlog.d(LOG_TAG, "Ignoring non reset refresh: " + resp);
330           return;
331         }
332 
333         Rlog.d(LOG_TAG, "Handling refresh reset: " + resp);
334 
335         boolean changed = mUiccCards[index].resetAppWithAid(resp.aid);
336         if (changed) {
337             boolean requirePowerOffOnSimRefreshReset = mContext.getResources().getBoolean(
338                 com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
339             if (requirePowerOffOnSimRefreshReset) {
340                 mCis[index].setRadioPower(false, null);
341             } else {
342                 mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
343             }
344             mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
345         }
346     }
347 
isValidCardIndex(int index)348     private boolean isValidCardIndex(int index) {
349         return (index >= 0 && index < mUiccCards.length);
350     }
351 
log(String string)352     private void log(String string) {
353         Rlog.d(LOG_TAG, string);
354     }
355 
356     // TODO: This is hacky. We need a better way of saving the logs.
addCardLog(String data)357     public void addCardLog(String data) {
358         Time t = new Time();
359         t.setToNow();
360         mCardLogs.addLast(t.format("%m-%d %H:%M:%S") + " " + data);
361         if (mCardLogs.size() > MAX_PROACTIVE_COMMANDS_TO_LOG) {
362             mCardLogs.removeFirst();
363         }
364     }
365 
dump(FileDescriptor fd, PrintWriter pw, String[] args)366     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
367         pw.println("UiccController: " + this);
368         pw.println(" mContext=" + mContext);
369         pw.println(" mInstance=" + mInstance);
370         pw.println(" mIccChangedRegistrants: size=" + mIccChangedRegistrants.size());
371         for (int i = 0; i < mIccChangedRegistrants.size(); i++) {
372             pw.println("  mIccChangedRegistrants[" + i + "]="
373                     + ((Registrant)mIccChangedRegistrants.get(i)).getHandler());
374         }
375         pw.println();
376         pw.flush();
377         pw.println(" mUiccCards: size=" + mUiccCards.length);
378         for (int i = 0; i < mUiccCards.length; i++) {
379             if (mUiccCards[i] == null) {
380                 pw.println("  mUiccCards[" + i + "]=null");
381             } else {
382                 pw.println("  mUiccCards[" + i + "]=" + mUiccCards[i]);
383                 mUiccCards[i].dump(fd, pw, args);
384             }
385         }
386         pw.println("mCardLogs: ");
387         for (int i = 0; i < mCardLogs.size(); ++i) {
388             pw.println("  " + mCardLogs.get(i));
389         }
390     }
391 }
392