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