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