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