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