blob: fcf2f929b1c8e8bc4f76251c42e65606e59a51f0 [file] [log] [blame]
/*
* Copyright (C) 2006 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;
import static android.Manifest.permission.READ_PHONE_STATE;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.Registrant;
import android.os.RegistrantList;
import android.util.Log;
import android.view.WindowManager;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.CommandsInterface.RadioState;
import com.android.internal.telephony.gsm.SIMFileHandler;
import com.android.internal.telephony.gsm.SIMRecords;
import com.android.internal.telephony.cat.CatService;
import com.android.internal.telephony.cdma.CDMALTEPhone;
import com.android.internal.telephony.cdma.CdmaLteUiccFileHandler;
import com.android.internal.telephony.cdma.CdmaLteUiccRecords;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.RuimFileHandler;
import com.android.internal.telephony.cdma.RuimRecords;
import android.os.SystemProperties;
import com.android.internal.R;
/**
* {@hide}
*/
public class IccCard {
protected String mLogTag;
protected boolean mDbg;
protected IccCardStatus mIccCardStatus = null;
protected State mState = null;
private final Object mStateMonitor = new Object();
protected boolean is3gpp = true;
protected boolean isSubscriptionFromIccCard = true;
protected CdmaSubscriptionSourceManager mCdmaSSM = null;
protected PhoneBase mPhone;
private IccRecords mIccRecords;
private IccFileHandler mIccFileHandler;
private CatService mCatService;
private RegistrantList mAbsentRegistrants = new RegistrantList();
private RegistrantList mPinLockedRegistrants = new RegistrantList();
private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
protected RegistrantList mReadyRegistrants = new RegistrantList();
protected RegistrantList mRuimReadyRegistrants = new RegistrantList();
private boolean mDesiredPinLocked;
private boolean mDesiredFdnEnabled;
private boolean mIccPinLocked = true; // Default to locked
private boolean mIccFdnEnabled = false; // Default to disabled.
// Will be updated when SIM_READY.
/* Parameter is3gpp's values to be passed to constructor */
public final static boolean CARD_IS_3GPP = true;
public final static boolean CARD_IS_NOT_3GPP = false;
/* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
static public final String INTENT_KEY_ICC_STATE = "ss";
/* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */
static public final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
/* ABSENT means ICC is missing */
static public final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
/* LOCKED means ICC is locked by pin or by network */
static public final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
/* READY means ICC is ready to access */
static public final String INTENT_VALUE_ICC_READY = "READY";
/* IMSI means ICC IMSI is ready in property */
static public final String INTENT_VALUE_ICC_IMSI = "IMSI";
/* LOADED means all ICC records, including IMSI, are loaded */
static public final String INTENT_VALUE_ICC_LOADED = "LOADED";
/* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
static public final String INTENT_KEY_LOCKED_REASON = "reason";
/* PIN means ICC is locked on PIN1 */
static public final String INTENT_VALUE_LOCKED_ON_PIN = "PIN";
/* PUK means ICC is locked on PUK1 */
static public final String INTENT_VALUE_LOCKED_ON_PUK = "PUK";
/* NETWORK means ICC is locked on NETWORK PERSONALIZATION */
static public final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK";
/* PERM_DISABLED means ICC is permanently disabled due to puk fails */
static public final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
protected static final int EVENT_ICC_LOCKED = 1;
private static final int EVENT_GET_ICC_STATUS_DONE = 2;
protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3;
private static final int EVENT_PINPUK_DONE = 4;
private static final int EVENT_REPOLL_STATUS_DONE = 5;
protected static final int EVENT_ICC_READY = 6;
private static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7;
private static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8;
private static final int EVENT_CHANGE_ICC_PASSWORD_DONE = 9;
private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
private static final int EVENT_ICC_STATUS_CHANGED = 12;
private static final int EVENT_CARD_REMOVED = 13;
private static final int EVENT_CARD_ADDED = 14;
protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 15;
protected static final int EVENT_RADIO_ON = 16;
/*
UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
turns to READY
*/
public enum State {
UNKNOWN,
ABSENT,
PIN_REQUIRED,
PUK_REQUIRED,
NETWORK_LOCKED,
READY,
NOT_READY,
PERM_DISABLED;
public boolean isPinLocked() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
}
public boolean iccCardExist() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
|| (this == NETWORK_LOCKED) || (this == READY)
|| (this == PERM_DISABLED));
}
}
public State getState() {
if (mState == null) {
switch(mPhone.mCM.getRadioState()) {
/* This switch block must not return anything in
* State.isLocked() or State.ABSENT.
* If it does, handleSimStatus() may break
*/
case RADIO_OFF:
case RADIO_UNAVAILABLE:
return State.UNKNOWN;
default:
if (!is3gpp && !isSubscriptionFromIccCard) {
// CDMA can get subscription from NV. In that case,
// subscription is ready as soon as Radio is ON.
return State.READY;
}
}
} else {
return mState;
}
return State.UNKNOWN;
}
public IccCard(PhoneBase phone, String logTag, Boolean is3gpp, Boolean dbg) {
mLogTag = logTag;
mDbg = dbg;
if (mDbg) log("[IccCard] Creating card type " + (is3gpp ? "3gpp" : "3gpp2"));
mPhone = phone;
this.is3gpp = is3gpp;
mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(mPhone.getContext(),
mPhone.mCM, mHandler, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
if (phone.mCM.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE
&& phone instanceof CDMALTEPhone) {
mIccFileHandler = new CdmaLteUiccFileHandler(this, "", mPhone.mCM);
mIccRecords = new CdmaLteUiccRecords(this, mPhone.mContext, mPhone.mCM);
} else {
// Correct aid will be set later (when GET_SIM_STATUS returns)
mIccFileHandler = is3gpp ? new SIMFileHandler(this, "", mPhone.mCM) :
new RuimFileHandler(this, "", mPhone.mCM);
mIccRecords = is3gpp ? new SIMRecords(this, mPhone.mContext, mPhone.mCM) :
new RuimRecords(this, mPhone.mContext, mPhone.mCM);
}
mCatService = CatService.getInstance(mPhone.mCM, mIccRecords,
mPhone.mContext, mIccFileHandler, this);
mPhone.mCM.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
mPhone.mCM.registerForOn(mHandler, EVENT_RADIO_ON, null);
mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_STATUS_CHANGED, null);
}
public void dispose() {
if (mDbg) log("[IccCard] Disposing card type " + (is3gpp ? "3gpp" : "3gpp2"));
mPhone.mCM.unregisterForIccStatusChanged(mHandler);
mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
mPhone.mCM.unregisterForOn(mHandler);
mCatService.dispose();
mCdmaSSM.dispose(mHandler);
mIccRecords.dispose();
mIccFileHandler.dispose();
}
protected void finalize() {
if (mDbg) log("[IccCard] Finalized card type " + (is3gpp ? "3gpp" : "3gpp2"));
}
public IccRecords getIccRecords() {
return mIccRecords;
}
public IccFileHandler getIccFileHandler() {
return mIccFileHandler;
}
/**
* Notifies handler of any transition into State.ABSENT
*/
public void registerForAbsent(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
mAbsentRegistrants.add(r);
if (getState() == State.ABSENT) {
r.notifyRegistrant();
}
}
public void unregisterForAbsent(Handler h) {
mAbsentRegistrants.remove(h);
}
/**
* Notifies handler of any transition into State.NETWORK_LOCKED
*/
public void registerForNetworkLocked(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
mNetworkLockedRegistrants.add(r);
if (getState() == State.NETWORK_LOCKED) {
r.notifyRegistrant();
}
}
public void unregisterForNetworkLocked(Handler h) {
mNetworkLockedRegistrants.remove(h);
}
/**
* Notifies handler of any transition into State.isPinLocked()
*/
public void registerForLocked(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
mPinLockedRegistrants.add(r);
if (getState().isPinLocked()) {
r.notifyRegistrant();
}
}
public void unregisterForLocked(Handler h) {
mPinLockedRegistrants.remove(h);
}
public void registerForReady(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
synchronized (mStateMonitor) {
mReadyRegistrants.add(r);
if (getState() == State.READY) {
r.notifyRegistrant(new AsyncResult(null, null, null));
}
}
}
public void unregisterForReady(Handler h) {
synchronized (mStateMonitor) {
mReadyRegistrants.remove(h);
}
}
public State getRuimState() {
if(mIccCardStatus != null) {
return getAppState(mIccCardStatus.getCdmaSubscriptionAppIndex());
} else {
return State.UNKNOWN;
}
}
public void registerForRuimReady(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
synchronized (mStateMonitor) {
mRuimReadyRegistrants.add(r);
if (getState() == State.READY && getRuimState() == State.READY ) {
r.notifyRegistrant(new AsyncResult(null, null, null));
}
}
}
public void unregisterForRuimReady(Handler h) {
synchronized (mStateMonitor) {
mRuimReadyRegistrants.remove(h);
}
}
/**
* Supply the ICC PIN to the ICC
*
* When the operation is complete, onComplete will be sent to its
* Handler.
*
* onComplete.obj will be an AsyncResult
*
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*
* If the supplied PIN is incorrect:
* ((AsyncResult)onComplete.obj).exception != null
* && ((AsyncResult)onComplete.obj).exception
* instanceof com.android.internal.telephony.gsm.CommandException)
* && ((CommandException)(((AsyncResult)onComplete.obj).exception))
* .getCommandError() == CommandException.Error.PASSWORD_INCORRECT
*
*
*/
public void supplyPin (String pin, Message onComplete) {
mPhone.mCM.supplyIccPin(pin, mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
}
public void supplyPuk (String puk, String newPin, Message onComplete) {
mPhone.mCM.supplyIccPuk(puk, newPin,
mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
}
public void supplyPin2 (String pin2, Message onComplete) {
mPhone.mCM.supplyIccPin2(pin2,
mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
}
public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
mPhone.mCM.supplyIccPuk2(puk2, newPin2,
mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
}
public void supplyNetworkDepersonalization (String pin, Message onComplete) {
mPhone.mCM.supplyNetworkDepersonalization(pin,
mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
}
/**
* Check whether ICC pin lock is enabled
* This is a sync call which returns the cached pin enabled state
*
* @return true for ICC locked enabled
* false for ICC locked disabled
*/
public boolean getIccLockEnabled() {
return mIccPinLocked;
}
/**
* Check whether ICC fdn (fixed dialing number) is enabled
* This is a sync call which returns the cached pin enabled state
*
* @return true for ICC fdn enabled
* false for ICC fdn disabled
*/
public boolean getIccFdnEnabled() {
return mIccFdnEnabled;
}
/**
* Set the ICC pin lock enabled or disabled
* When the operation is complete, onComplete will be sent to its handler
*
* @param enabled "true" for locked "false" for unlocked.
* @param password needed to change the ICC pin state, aka. Pin1
* @param onComplete
* onComplete.obj will be an AsyncResult
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
public void setIccLockEnabled (boolean enabled,
String password, Message onComplete) {
int serviceClassX;
serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
CommandsInterface.SERVICE_CLASS_DATA +
CommandsInterface.SERVICE_CLASS_FAX;
mDesiredPinLocked = enabled;
mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
enabled, password, serviceClassX,
mHandler.obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
}
/**
* Set the ICC fdn enabled or disabled
* When the operation is complete, onComplete will be sent to its handler
*
* @param enabled "true" for locked "false" for unlocked.
* @param password needed to change the ICC fdn enable, aka Pin2
* @param onComplete
* onComplete.obj will be an AsyncResult
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
public void setIccFdnEnabled (boolean enabled,
String password, Message onComplete) {
int serviceClassX;
serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
CommandsInterface.SERVICE_CLASS_DATA +
CommandsInterface.SERVICE_CLASS_FAX +
CommandsInterface.SERVICE_CLASS_SMS;
mDesiredFdnEnabled = enabled;
mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD,
enabled, password, serviceClassX,
mHandler.obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
}
/**
* Change the ICC password used in ICC pin lock
* When the operation is complete, onComplete will be sent to its handler
*
* @param oldPassword is the old password
* @param newPassword is the new password
* @param onComplete
* onComplete.obj will be an AsyncResult
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
public void changeIccLockPassword(String oldPassword, String newPassword,
Message onComplete) {
mPhone.mCM.changeIccPin(oldPassword, newPassword,
mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
}
/**
* Change the ICC password used in ICC fdn enable
* When the operation is complete, onComplete will be sent to its handler
*
* @param oldPassword is the old password
* @param newPassword is the new password
* @param onComplete
* onComplete.obj will be an AsyncResult
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
public void changeIccFdnPassword(String oldPassword, String newPassword,
Message onComplete) {
mPhone.mCM.changeIccPin2(oldPassword, newPassword,
mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
}
/**
* Returns service provider name stored in ICC card.
* If there is no service provider name associated or the record is not
* yet available, null will be returned <p>
*
* Please use this value when display Service Provider Name in idle mode <p>
*
* Usage of this provider name in the UI is a common carrier requirement.
*
* Also available via Android property "gsm.sim.operator.alpha"
*
* @return Service Provider Name stored in ICC card
* null if no service provider name associated or the record is not
* yet available
*
*/
public String getServiceProviderName () {
return mPhone.mIccRecords.getServiceProviderName();
}
protected void updateStateProperty() {
mPhone.setSystemProperty(TelephonyProperties.PROPERTY_SIM_STATE, getState().toString());
}
private void getIccCardStatusDone(AsyncResult ar) {
if (ar.exception != null) {
Log.e(mLogTag,"Error getting ICC status. "
+ "RIL_REQUEST_GET_ICC_STATUS should "
+ "never return an error", ar.exception);
return;
}
handleIccCardStatus((IccCardStatus) ar.result);
}
private void handleIccCardStatus(IccCardStatus newCardStatus) {
boolean transitionedIntoPinLocked;
boolean transitionedIntoAbsent;
boolean transitionedIntoNetworkLocked;
boolean transitionedIntoPermBlocked;
boolean isIccCardRemoved;
boolean isIccCardAdded;
State oldState, newState;
State oldRuimState = getRuimState();
oldState = mState;
mIccCardStatus = newCardStatus;
newState = getIccCardState();
synchronized (mStateMonitor) {
mState = newState;
updateStateProperty();
if (oldState != State.READY && newState == State.READY) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_ICC_READY));
mReadyRegistrants.notifyRegistrants();
} else if (newState.isPinLocked()) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_ICC_LOCKED));
}
if (oldRuimState != State.READY && getRuimState() == State.READY) {
mRuimReadyRegistrants.notifyRegistrants();
}
}
transitionedIntoPinLocked = (
(oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED)
|| (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED));
transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
&& newState == State.NETWORK_LOCKED);
transitionedIntoPermBlocked = (oldState != State.PERM_DISABLED
&& newState == State.PERM_DISABLED);
isIccCardRemoved = (oldState != null &&
oldState.iccCardExist() && newState == State.ABSENT);
isIccCardAdded = (oldState == State.ABSENT &&
newState != null && newState.iccCardExist());
if (transitionedIntoPinLocked) {
if (mDbg) log("Notify SIM pin or puk locked.");
mPinLockedRegistrants.notifyRegistrants();
broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
(newState == State.PIN_REQUIRED) ?
INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
} else if (transitionedIntoAbsent) {
if (mDbg) log("Notify SIM missing.");
mAbsentRegistrants.notifyRegistrants();
broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null);
} else if (transitionedIntoNetworkLocked) {
if (mDbg) log("Notify SIM network locked.");
mNetworkLockedRegistrants.notifyRegistrants();
broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
INTENT_VALUE_LOCKED_NETWORK);
} else if (transitionedIntoPermBlocked) {
if (mDbg) log("Notify SIM permanently disabled.");
broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT,
INTENT_VALUE_ABSENT_ON_PERM_DISABLED);
}
if (isIccCardRemoved) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
} else if (isIccCardAdded) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
}
// Call onReady Record(s) on the IccCard becomes ready (not NV)
if (oldState != State.READY && newState == State.READY &&
(is3gpp || isSubscriptionFromIccCard)) {
if (!(mIccFileHandler instanceof CdmaLteUiccFileHandler)) {
// CdmaLteUicc File Handler deals with both USIM and CSIM.
// Do not lock onto one AID for now.
mIccFileHandler.setAid(getAid());
}
mIccRecords.onReady();
}
}
private void onIccSwap(boolean isAdded) {
// TODO: Here we assume the device can't handle SIM hot-swap
// and has to reboot. We may want to add a property,
// e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
// hot-swap.
DialogInterface.OnClickListener listener = null;
// TODO: SimRecords is not reset while SIM ABSENT (only reset while
// Radio_off_or_not_available). Have to reset in both both
// added or removed situation.
listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
if (mDbg) log("Reboot due to SIM swap");
PowerManager pm = (PowerManager) mPhone.getContext()
.getSystemService(Context.POWER_SERVICE);
pm.reboot("SIM is added.");
}
}
};
Resources r = Resources.getSystem();
String title = (isAdded) ? r.getString(R.string.sim_added_title) :
r.getString(R.string.sim_removed_title);
String message = (isAdded) ? r.getString(R.string.sim_added_message) :
r.getString(R.string.sim_removed_message);
String buttonTxt = r.getString(R.string.sim_restart_button);
AlertDialog dialog = new AlertDialog.Builder(mPhone.getContext())
.setTitle(title)
.setMessage(message)
.setPositiveButton(buttonTxt, listener)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
}
/**
* Interperate EVENT_QUERY_FACILITY_LOCK_DONE
* @param ar is asyncResult of Query_Facility_Locked
*/
private void onQueryFdnEnabled(AsyncResult ar) {
if(ar.exception != null) {
if(mDbg) log("Error in querying facility lock:" + ar.exception);
return;
}
int[] ints = (int[])ar.result;
if(ints.length != 0) {
mIccFdnEnabled = (0!=ints[0]);
if(mDbg) log("Query facility lock : " + mIccFdnEnabled);
} else {
Log.e(mLogTag, "[IccCard] Bogus facility lock response");
}
}
/**
* Interperate EVENT_QUERY_FACILITY_LOCK_DONE
* @param ar is asyncResult of Query_Facility_Locked
*/
private void onQueryFacilityLock(AsyncResult ar) {
if(ar.exception != null) {
if (mDbg) log("Error in querying facility lock:" + ar.exception);
return;
}
int[] ints = (int[])ar.result;
if(ints.length != 0) {
mIccPinLocked = (0!=ints[0]);
if(mDbg) log("Query facility lock : " + mIccPinLocked);
} else {
Log.e(mLogTag, "[IccCard] Bogus facility lock response");
}
}
public void broadcastIccStateChangedIntent(String value, String reason) {
Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(Phone.PHONE_NAME_KEY, mPhone.getPhoneName());
intent.putExtra(INTENT_KEY_ICC_STATE, value);
intent.putExtra(INTENT_KEY_LOCKED_REASON, reason);
if(mDbg) log("Broadcasting intent ACTION_SIM_STATE_CHANGED " + value
+ " reason " + reason);
ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE);
}
protected Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg){
AsyncResult ar;
int serviceClassX;
serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
CommandsInterface.SERVICE_CLASS_DATA +
CommandsInterface.SERVICE_CLASS_FAX;
if (!mPhone.mIsTheCurrentActivePhone) {
Log.e(mLogTag, "Received message " + msg + "[" + msg.what
+ "] while being destroyed. Ignoring.");
return;
}
switch (msg.what) {
case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
mState = null;
updateStateProperty();
broadcastIccStateChangedIntent(INTENT_VALUE_ICC_NOT_READY, null);
break;
case EVENT_RADIO_ON:
if (!is3gpp) {
handleCdmaSubscriptionSource();
}
mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
break;
case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
handleCdmaSubscriptionSource();
break;
case EVENT_ICC_READY:
if(isSubscriptionFromIccCard) {
mPhone.mCM.queryFacilityLock (
CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
mPhone.mCM.queryFacilityLock (
CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX,
obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE));
}
break;
case EVENT_ICC_LOCKED:
mPhone.mCM.queryFacilityLock (
CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
break;
case EVENT_GET_ICC_STATUS_DONE:
ar = (AsyncResult)msg.obj;
getIccCardStatusDone(ar);
break;
case EVENT_PINPUK_DONE:
// a PIN/PUK/PIN2/PUK2/Network Personalization
// request has completed. ar.userObj is the response Message
// Repoll before returning
ar = (AsyncResult)msg.obj;
// TODO should abstract these exceptions
AsyncResult.forMessage(((Message)ar.userObj)).exception
= ar.exception;
mPhone.mCM.getIccCardStatus(
obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj));
break;
case EVENT_REPOLL_STATUS_DONE:
// Finished repolling status after PIN operation
// ar.userObj is the response messaeg
// ar.userObj.obj is already an AsyncResult with an
// appropriate exception filled in if applicable
ar = (AsyncResult)msg.obj;
getIccCardStatusDone(ar);
((Message)ar.userObj).sendToTarget();
break;
case EVENT_QUERY_FACILITY_LOCK_DONE:
ar = (AsyncResult)msg.obj;
onQueryFacilityLock(ar);
break;
case EVENT_QUERY_FACILITY_FDN_DONE:
ar = (AsyncResult)msg.obj;
onQueryFdnEnabled(ar);
break;
case EVENT_CHANGE_FACILITY_LOCK_DONE:
ar = (AsyncResult)msg.obj;
if (ar.exception == null) {
mIccPinLocked = mDesiredPinLocked;
if (mDbg) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " +
"mIccPinLocked= " + mIccPinLocked);
} else {
Log.e(mLogTag, "Error change facility lock with exception "
+ ar.exception);
}
AsyncResult.forMessage(((Message)ar.userObj)).exception
= ar.exception;
((Message)ar.userObj).sendToTarget();
break;
case EVENT_CHANGE_FACILITY_FDN_DONE:
ar = (AsyncResult)msg.obj;
if (ar.exception == null) {
mIccFdnEnabled = mDesiredFdnEnabled;
if (mDbg) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
"mIccFdnEnabled=" + mIccFdnEnabled);
} else {
Log.e(mLogTag, "Error change facility fdn with exception "
+ ar.exception);
}
AsyncResult.forMessage(((Message)ar.userObj)).exception
= ar.exception;
((Message)ar.userObj).sendToTarget();
break;
case EVENT_CHANGE_ICC_PASSWORD_DONE:
ar = (AsyncResult)msg.obj;
if(ar.exception != null) {
Log.e(mLogTag, "Error in change sim password with exception"
+ ar.exception);
}
AsyncResult.forMessage(((Message)ar.userObj)).exception
= ar.exception;
((Message)ar.userObj).sendToTarget();
break;
case EVENT_ICC_STATUS_CHANGED:
Log.d(mLogTag, "Received Event EVENT_ICC_STATUS_CHANGED");
mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
break;
case EVENT_CARD_REMOVED:
onIccSwap(false);
break;
case EVENT_CARD_ADDED:
onIccSwap(true);
break;
default:
Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
}
}
};
private void handleCdmaSubscriptionSource() {
if(mCdmaSSM != null) {
int newSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource();
Log.d(mLogTag, "Received Cdma subscription source: " + newSubscriptionSource);
boolean isNewSubFromRuim =
(newSubscriptionSource == CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_RUIM);
if (isNewSubFromRuim != isSubscriptionFromIccCard) {
isSubscriptionFromIccCard = isNewSubFromRuim;
// Parse the Stored IccCardStatus Message to set mState correctly.
handleIccCardStatus(mIccCardStatus);
}
}
}
public State getIccCardState() {
if(!is3gpp && !isSubscriptionFromIccCard) {
// CDMA can get subscription from NV. In that case,
// subscription is ready as soon as Radio is ON.
return State.READY;
}
if (mIccCardStatus == null) {
Log.e(mLogTag, "[IccCard] IccCardStatus is null");
return IccCard.State.ABSENT;
}
// this is common for all radio technologies
if (!mIccCardStatus.getCardState().isCardPresent()) {
return IccCard.State.ABSENT;
}
RadioState currentRadioState = mPhone.mCM.getRadioState();
// check radio technology
if( currentRadioState == RadioState.RADIO_OFF ||
currentRadioState == RadioState.RADIO_UNAVAILABLE) {
return IccCard.State.NOT_READY;
}
if( currentRadioState == RadioState.RADIO_ON ) {
State csimState =
getAppState(mIccCardStatus.getCdmaSubscriptionAppIndex());
State usimState =
getAppState(mIccCardStatus.getGsmUmtsSubscriptionAppIndex());
if(mDbg) log("USIM=" + usimState + " CSIM=" + csimState);
if (mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
// UICC card contains both USIM and CSIM
// Return consolidated status
return getConsolidatedState(csimState, usimState, csimState);
}
// check for CDMA radio technology
if (!is3gpp) {
return csimState;
}
return usimState;
}
return IccCard.State.ABSENT;
}
private State getAppState(int appIndex) {
IccCardApplication app;
if (appIndex >= 0 && appIndex < IccCardStatus.CARD_MAX_APPS) {
app = mIccCardStatus.getApplication(appIndex);
} else {
Log.e(mLogTag, "[IccCard] Invalid Subscription Application index:" + appIndex);
return IccCard.State.ABSENT;
}
if (app == null) {
Log.e(mLogTag, "[IccCard] Subscription Application in not present");
return IccCard.State.ABSENT;
}
// check if PIN required
if (app.pin1.isPermBlocked()) {
return IccCard.State.PERM_DISABLED;
}
if (app.app_state.isPinRequired()) {
return IccCard.State.PIN_REQUIRED;
}
if (app.app_state.isPukRequired()) {
return IccCard.State.PUK_REQUIRED;
}
if (app.app_state.isSubscriptionPersoEnabled()) {
return IccCard.State.NETWORK_LOCKED;
}
if (app.app_state.isAppReady()) {
return IccCard.State.READY;
}
if (app.app_state.isAppNotReady()) {
return IccCard.State.NOT_READY;
}
return IccCard.State.NOT_READY;
}
private State getConsolidatedState(State left, State right, State preferredState) {
// Check if either is absent.
if (right == IccCard.State.ABSENT) return left;
if (left == IccCard.State.ABSENT) return right;
// Only if both are ready, return ready
if ((left == IccCard.State.READY) && (right == IccCard.State.READY)) {
return State.READY;
}
// Case one is ready, but the other is not.
if (((right == IccCard.State.NOT_READY) && (left == IccCard.State.READY)) ||
((left == IccCard.State.NOT_READY) && (right == IccCard.State.READY))) {
return IccCard.State.NOT_READY;
}
// At this point, the other state is assumed to be one of locked state
if (right == IccCard.State.NOT_READY) return left;
if (left == IccCard.State.NOT_READY) return right;
// At this point, FW currently just assumes the status will be
// consistent across the applications...
return preferredState;
}
public boolean isApplicationOnIcc(IccCardApplication.AppType type) {
if (mIccCardStatus == null) return false;
for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) {
IccCardApplication app = mIccCardStatus.getApplication(i);
if (app != null && app.app_type == type) {
return true;
}
}
return false;
}
/**
* @return true if a ICC card is present
*/
public boolean hasIccCard() {
if (mIccCardStatus == null) {
return false;
} else {
// Returns ICC card status for both GSM and CDMA mode
return mIccCardStatus.getCardState().isCardPresent();
}
}
private void log(String msg) {
Log.d(mLogTag, "[IccCard] " + msg);
}
protected int getCurrentApplicationIndex() {
if (is3gpp) {
return mIccCardStatus.getGsmUmtsSubscriptionAppIndex();
} else {
return mIccCardStatus.getCdmaSubscriptionAppIndex();
}
}
public String getAid() {
String aid = "";
if (mIccCardStatus == null) {
return aid;
}
int appIndex = getCurrentApplicationIndex();
if (appIndex >= 0 && appIndex < IccCardStatus.CARD_MAX_APPS) {
IccCardApplication app = mIccCardStatus.getApplication(appIndex);
if (app != null) {
aid = app.aid;
} else {
Log.e(mLogTag, "[IccCard] getAid: no current application index=" + appIndex);
}
} else {
Log.e(mLogTag, "[IccCard] getAid: Invalid Subscription Application index=" + appIndex);
}
return aid;
}
}