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