/*
* Copyright (C) 2011-2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony.uicc;
import android.content.Context;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
import android.util.Log;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccCardStatus;
import com.android.internal.telephony.IccFileHandler;
import com.android.internal.telephony.IccRecords;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.UiccCard;
import com.android.internal.telephony.UiccCardApplication;
/**
* This class is responsible for keeping all knowledge about
* Universal Integrated Circuit Card (UICC), also know as SIM's,
* in the system. It is also used as API to get appropriate
* applications to pass them to phone and service trackers.
*
* UiccController is created with the call to make() function.
* UiccController is a singleton and make() must only be called once
* and throws an exception if called multiple times.
*
* Once created UiccController registers with RIL for "on" and "unsol_sim_status_changed"
* notifications. When such notification arrives UiccController will call
* getIccCardStatus (GET_SIM_STATUS). Based on the response of GET_SIM_STATUS
* request appropriate tree of uicc objects will be created.
*
* Following is class diagram for uicc classes:
*
* UiccController
* #
* |
* UiccCard
* # #
* | ------------------
* UiccCardApplication CatService
* # #
* | |
* IccRecords IccFileHandler
* ^ ^ ^ ^ ^ ^ ^ ^
* SIMRecords---- | | | | | | ---SIMFileHandler
* RuimRecords----- | | | | ----RuimFileHandler
* IsimUiccRecords--- | | -----UsimFileHandler
* | ------CsimFileHandler
* ----IsimFileHandler
*
* Legend: # stands for Composition
* ^ stands for Generalization
*
* See also {@link com.android.internal.telephony.IccCard}
* and {@link com.android.internal.telephony.IccCardProxy}
*/
public class UiccController extends Handler {
private static final boolean DBG = true;
private static final String LOG_TAG = "RIL_UiccController";
public static final int APP_FAM_3GPP = 1;
public static final int APP_FAM_3GPP2 = 2;
public static final int APP_FAM_IMS = 3;
private static final int EVENT_ICC_STATUS_CHANGED = 1;
private static final int EVENT_GET_ICC_STATUS_DONE = 2;
private static final Object mLock = new Object();
private static UiccController mInstance;
private Context mContext;
private CommandsInterface mCi;
private UiccCard mUiccCard;
private RegistrantList mIccChangedRegistrants = new RegistrantList();
public static UiccController make(Context c, CommandsInterface ci) {
synchronized (mLock) {
if (mInstance != null) {
throw new RuntimeException("UiccController.make() should only be called once");
}
mInstance = new UiccController(c, ci);
return mInstance;
}
}
public static UiccController getInstance() {
synchronized (mLock) {
if (mInstance == null) {
throw new RuntimeException(
"UiccController.getInstance can't be called before make()");
}
return mInstance;
}
}
public UiccCard getUiccCard() {
synchronized (mLock) {
return mUiccCard;
}
}
// Easy to use API
public UiccCardApplication getUiccCardApplication(int family) {
synchronized (mLock) {
if (mUiccCard != null) {
return mUiccCard.getApplication(family);
}
return null;
}
}
// Easy to use API
public IccRecords getIccRecords(int family) {
synchronized (mLock) {
if (mUiccCard != null) {
UiccCardApplication app = mUiccCard.getApplication(family);
if (app != null) {
return app.getIccRecords();
}
}
return null;
}
}
// Easy to use API
public IccFileHandler getIccFileHandler(int family) {
synchronized (mLock) {
if (mUiccCard != null) {
UiccCardApplication app = mUiccCard.getApplication(family);
if (app != null) {
return app.getIccFileHandler();
}
}
return null;
}
}
//Notifies when card status changes
public void registerForIccChanged(Handler h, int what, Object obj) {
synchronized (mLock) {
Registrant r = new Registrant (h, what, obj);
mIccChangedRegistrants.add(r);
//Notify registrant right after registering, so that it will get the latest ICC status,
//otherwise which may not happen until there is an actual change in ICC status.
r.notifyRegistrant();
}
}
public void unregisterForIccChanged(Handler h) {
synchronized (mLock) {
mIccChangedRegistrants.remove(h);
}
}
@Override
public void handleMessage (Message msg) {
synchronized (mLock) {
switch (msg.what) {
case EVENT_ICC_STATUS_CHANGED:
if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
break;
case EVENT_GET_ICC_STATUS_DONE:
if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
AsyncResult ar = (AsyncResult)msg.obj;
onGetIccCardStatusDone(ar);
break;
default:
Log.e(LOG_TAG, " Unknown Event " + msg.what);
}
}
}
private UiccController(Context c, CommandsInterface ci) {
if (DBG) log("Creating UiccController");
mContext = c;
mCi = ci;
mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null);
// TODO remove this once modem correctly notifies the unsols
mCi.registerForOn(this, EVENT_ICC_STATUS_CHANGED, null);
}
private synchronized void onGetIccCardStatusDone(AsyncResult ar) {
if (ar.exception != null) {
Log.e(LOG_TAG,"Error getting ICC status. "
+ "RIL_REQUEST_GET_ICC_STATUS should "
+ "never return an error", ar.exception);
return;
}
IccCardStatus status = (IccCardStatus)ar.result;
if (mUiccCard == null) {
//Create new card
mUiccCard = new UiccCard(mContext, mCi, status);
} else {
//Update already existing card
mUiccCard.update(mContext, mCi , status);
}
if (DBG) log("Notifying IccChangedRegistrants");
mIccChangedRegistrants.notifyRegistrants();
}
private void log(String string) {
Log.d(LOG_TAG, string);
}
}