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