/* * Copyright (C) 2011 The CyanogenMod 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 com.android.internal.telephony.RILConstants.*; import android.content.*; import android.net.*; import android.os.*; import android.telephony.*; import android.text.TextUtils; import android.util.Log; import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; import com.android.internal.telephony.cdma.CdmaInformationRecords; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import com.android.internal.telephony.gsm.SuppServiceNotification; import java.io.*; import java.util.*; /** * Custom SEMC ril * * {@hide} */ public class SemcRIL extends RIL { String mAid = ""; IccHandler mIccHandler; HandlerThread mIccThread; public SemcRIL(Context context, int networkMode, int cdmaSubscription) { super(context, networkMode, cdmaSubscription); mIccHandler = null; } @Override protected RadioState getRadioStateFromInt(int stateInt) { RadioState state; /* RIL_RadioState ril.h */ switch(stateInt) { case 0: state = RadioState.RADIO_OFF; break; case 1: state = RadioState.RADIO_UNAVAILABLE; break; case 2: { if (mIccHandler == null) { mIccThread = new HandlerThread("IccHandler"); mIccThread.start(); mIccHandler = new IccHandler(this, mIccThread.getLooper()); } mIccHandler.run(); state = mPhoneType == RILConstants.CDMA_PHONE ? RadioState.RUIM_NOT_READY : RadioState.SIM_NOT_READY; break; } case 3: state = RadioState.SIM_LOCKED_OR_ABSENT; break; case 4: state = RadioState.SIM_READY; break; case 5: state = RadioState.RUIM_NOT_READY; break; case 6: state = RadioState.RUIM_READY; break; case 7: state = RadioState.RUIM_LOCKED_OR_ABSENT; break; case 8: state = RadioState.NV_NOT_READY; break; case 9: state = RadioState.NV_READY; break; default: throw new RuntimeException( "Unrecognized RIL_RadioState: " + stateInt); } return state; } @Override protected Object responseIccCardStatus(Parcel p) { IccCardApplication ca; IccCardStatus status = new IccCardStatus(); status.setCardState(p.readInt()); status.setUniversalPinState(p.readInt()); status.setGsmUmtsSubscriptionAppIndex(p.readInt()); status.setCdmaSubscriptionAppIndex(p.readInt()); status.setImsSubscriptionAppIndex(p.readInt()); int numApplications = p.readInt(); // limit to maximum allowed applications if (numApplications > IccCardStatus.CARD_MAX_APPS) { numApplications = IccCardStatus.CARD_MAX_APPS; } status.setNumApplications(numApplications); for (int i = 0 ; i < numApplications ; i++) { ca = new IccCardApplication(); ca.app_type = ca.AppTypeFromRILInt(p.readInt()); ca.app_state = ca.AppStateFromRILInt(p.readInt()); ca.perso_substate = ca.PersoSubstateFromRILInt(p.readInt()); ca.aid = p.readString(); ca.app_label = p.readString(); ca.pin1_replaced = p.readInt(); ca.pin1 = ca.PinStateFromRILInt(p.readInt()); ca.pin2 = ca.PinStateFromRILInt(p.readInt()); status.addApplication(ca); } updateIccType(status); return status; } private void updateIccType (IccCardStatus status) { if (status.getNumApplications() > 0) { int appType; if (mPhoneType == RILConstants.CDMA_PHONE) { appType = status.getCdmaSubscriptionAppIndex(); } else { appType = status.getGsmUmtsSubscriptionAppIndex(); } IccCardApplication application = status.getApplication(appType); mAid = application.aid; Log.d(LOG_TAG, "Picked default AID: " + mAid); SystemProperties.set("ril.icctype", Integer.toString(application.app_type.ordinal())); } } @Override protected DataCallState getDataCallState(Parcel p, int version) { DataCallState dataCall = new DataCallState(); dataCall.version = version; if (version < 5) { dataCall.cid = p.readInt(); dataCall.active = p.readInt(); dataCall.type = p.readString(); String addresses = p.readString(); if (!TextUtils.isEmpty(addresses)) { dataCall.addresses = addresses.split(" "); } // DataCallState needs an ifname. Since we don't have one use the name from the ThrottleService resource (default=rmnet0). dataCall.ifname = "rmnet0"; } else { dataCall.status = p.readInt(); dataCall.suggestedRetryTime = p.readInt(); dataCall.cid = p.readInt(); dataCall.active = p.readInt(); dataCall.type = p.readString(); dataCall.ifname = p.readString(); if ((dataCall.status == DataConnection.FailCause.NONE.getErrorCode()) && TextUtils.isEmpty(dataCall.ifname)) { throw new RuntimeException("getDataCallState, no ifname"); } String addresses = p.readString(); if (!TextUtils.isEmpty(addresses)) { dataCall.addresses = addresses.split(" "); } String dnses = p.readString(); if (!TextUtils.isEmpty(dnses)) { dataCall.dnses = dnses.split(" "); } String gateways = p.readString(); if (!TextUtils.isEmpty(gateways)) { dataCall.gateways = gateways.split(" "); } } return dataCall; } @Override public void supplyIccPin(String pin, Message result) { supplyIccPinForApp(pin, mAid, result); } @Override public void changeIccPin(String oldPin, String newPin, Message result) { changeIccPinForApp(oldPin, newPin, mAid, result); } @Override public void supplyIccPin2(String pin, Message result) { supplyIccPin2ForApp(pin, mAid, result); } @Override public void changeIccPin2(String oldPin2, String newPin2, Message result) { changeIccPin2ForApp(oldPin2, newPin2, mAid, result); } @Override public void supplyIccPuk(String puk, String newPin, Message result) { supplyIccPukForApp(puk, newPin, mAid, result); } @Override public void supplyIccPuk2(String puk2, String newPin2, Message result) { supplyIccPuk2ForApp(puk2, newPin2, mAid, result); } @Override public void queryFacilityLock(String facility, String password, int serviceClass, Message response) { queryFacilityLockForApp(facility, password, serviceClass, mAid, response); } @Override public void setFacilityLock (String facility, boolean lockState, String password, int serviceClass, Message response) { setFacilityLockForApp(facility, lockState, password, serviceClass, mAid, response); } @Override public void iccIO (int command, int fileid, String path, int p1, int p2, int p3, String data, String pin2, Message result) { //Note: This RIL request has not been renamed to ICC, // but this request is also valid for SIM and RUIM RILRequest rr = RILRequest.obtain(RIL_REQUEST_SIM_IO, result); rr.mp.writeInt(command); rr.mp.writeInt(fileid); rr.mp.writeString(path); rr.mp.writeInt(p1); rr.mp.writeInt(p2); rr.mp.writeInt(p3); rr.mp.writeString(data); rr.mp.writeString(pin2); rr.mp.writeString(mAid); if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest) + " 0x" + Integer.toHexString(command) + " 0x" + Integer.toHexString(fileid) + " " + " path: " + path + "," + p1 + "," + p2 + "," + p3); send(rr); } @Override public void getIMSI(Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result); rr.mp.writeString(mAid); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } @Override public void setNetworkSelectionModeAutomatic(Message response) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, response); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); rr.mp.writeString(null); rr.mp.writeInt(-1); send(rr); } @Override public void setNetworkSelectionModeManual(String operatorNumeric, Message response) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, response); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + operatorNumeric); rr.mp.writeString(operatorNumeric); rr.mp.writeInt(-1); send(rr); } @Override protected Object responseOperatorInfos(Parcel p) { String strings[] = (String [])responseStrings(p); ArrayList ret; if (strings.length % 5 != 0) { throw new RuntimeException( "RIL_REQUEST_QUERY_AVAILABLE_NETWORKS: invalid response. Got " + strings.length + " strings, expected multible of 5"); } ret = new ArrayList(strings.length / 4); for (int i = 0 ; i < strings.length ; i += 5) { ret.add ( new OperatorInfo( strings[i+0], strings[i+1], strings[i+2], strings[i+3])); } return ret; } @Override public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result); rr.mp.writeString(address); rr.mp.writeInt(clirMode); rr.mp.writeInt(0); // UUS information is absent if (uusInfo == null) { rr.mp.writeInt(0); // UUS information is absent } else { rr.mp.writeInt(1); // UUS information is present rr.mp.writeInt(uusInfo.getType()); rr.mp.writeInt(uusInfo.getDcs()); rr.mp.writeByteArray(uusInfo.getUserData()); } rr.mp.writeInt(255); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } class IccHandler extends Handler implements Runnable { private static final int EVENT_RADIO_ON = 1; private static final int EVENT_ICC_STATUS_CHANGED = 2; private static final int EVENT_GET_ICC_STATUS_DONE = 3; private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 4; private RIL mRil; private boolean mRadioOn = false; public IccHandler (RIL ril, Looper looper) { super (looper); mRil = ril; } public void handleMessage (Message paramMessage) { switch (paramMessage.what) { case EVENT_RADIO_ON: mRadioOn = true; Log.d(LOG_TAG, "Radio on -> Forcing sim status update"); sendMessage(obtainMessage(EVENT_ICC_STATUS_CHANGED)); break; case EVENT_GET_ICC_STATUS_DONE: AsyncResult asyncResult = (AsyncResult) paramMessage.obj; if (asyncResult.exception != null) { Log.e (LOG_TAG, "IccCardStatusDone shouldn't return exceptions!", asyncResult.exception); break; } IccCardStatus status = (IccCardStatus) asyncResult.result; if (status.getNumApplications() == 0) { if (!mRil.getRadioState().isOn()) { break; } if (mPhoneType == RILConstants.CDMA_PHONE) { mRil.setRadioState(CommandsInterface.RadioState.RUIM_LOCKED_OR_ABSENT); } else { mRil.setRadioState(CommandsInterface.RadioState.SIM_LOCKED_OR_ABSENT); } } else { int appIndex = -1; if (mPhoneType == RILConstants.CDMA_PHONE) { appIndex = status.getCdmaSubscriptionAppIndex(); Log.d(LOG_TAG, "This is a CDMA PHONE " + appIndex); } else { appIndex = status.getGsmUmtsSubscriptionAppIndex(); Log.d(LOG_TAG, "This is a GSM PHONE " + appIndex); } IccCardApplication application = status.getApplication(appIndex); IccCardApplication.AppState app_state = application.app_state; IccCardApplication.AppType app_type = application.app_type; switch (app_state) { case APPSTATE_PIN: case APPSTATE_PUK: switch (app_type) { case APPTYPE_SIM: case APPTYPE_USIM: mRil.setRadioState(CommandsInterface.RadioState.SIM_LOCKED_OR_ABSENT); break; case APPTYPE_RUIM: mRil.setRadioState(CommandsInterface.RadioState.RUIM_LOCKED_OR_ABSENT); break; default: Log.e(LOG_TAG, "Currently we don't handle SIMs of type: " + app_type); return; } break; case APPSTATE_READY: switch (app_type) { case APPTYPE_SIM: case APPTYPE_USIM: mRil.setRadioState(CommandsInterface.RadioState.SIM_READY); break; case APPTYPE_RUIM: mRil.setRadioState(CommandsInterface.RadioState.RUIM_READY); break; default: Log.e(LOG_TAG, "Currently we don't handle SIMs of type: " + app_type); return; } break; default: return; } } break; case EVENT_ICC_STATUS_CHANGED: if (mRadioOn) { Log.d(LOG_TAG, "Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus"); mRil.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, paramMessage.obj)); } else { Log.d(LOG_TAG, "Received EVENT_ICC_STATUS_CHANGED while radio is not ON. Ignoring"); } break; case EVENT_RADIO_OFF_OR_UNAVAILABLE: mRadioOn = false; // disposeCards(); // to be verified; default: Log.e(LOG_TAG, " Unknown Event " + paramMessage.what); break; } } public void run () { mRil.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null); Message msg = obtainMessage(EVENT_RADIO_ON); mRil.getIccCardStatus(msg); } } }