/* * Copyright (C) 2016 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 com.android.internal.telephony.dataconnection.DataCallResponse; import com.android.internal.telephony.uicc.IccRefreshResponse; import android.content.Context; import android.os.AsyncResult; import android.os.Message; import android.os.Parcel; import android.os.SystemProperties; import android.provider.Settings; import android.provider.Settings.Global; import android.telephony.TelephonyManager; import android.telephony.Rlog; import com.android.internal.telephony.MtkEccList; /** * Custom wrapper for MTK requests * * {@hide} */ public class MT6753 extends RIL implements CommandsInterface { private static final int RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED = 3015; private static final int RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED = 3024; private static final int RIL_UNSOL_INCOMING_CALL_INDICATION = 3042; private static final int RIL_UNSOL_CALL_INFO_INDICATION = 3049; private static final int RIL_UNSOL_SET_ATTACH_APN = 3073; private static final int RIL_REQUEST_MODEM_POWEROFF = 2010; private static final int RIL_REQUEST_MODEM_POWERON = 2028; private static final int RIL_REQUEST_RESUME_REGISTRATION = 2065; private static final int RIL_REQUEST_SET_CALL_INDICATION = 2086; private static final int RIL_REQUEST_EMERGENCY_DIAL = 2087; private static final int RIL_REQUEST_SET_ECC_SERVICE_CATEGORY = 2088; private static final int RIL_REQUEST_SET_ECC_LIST = 2089; private static final int REFRESH_SESSION_RESET = 6; /* Session reset */ private int[] dataCallCids = { -1, -1, -1, -1, -1 }; private Context mContext; private TelephonyManager mTelephonyManager; private MtkEccList mEccList; public MT6753(Context context, int preferredNetworkType, int cdmaSubscription) { super(context, preferredNetworkType, cdmaSubscription, null); } public MT6753(Context context, int preferredNetworkType, int cdmaSubscription, Integer instanceId) { super(context, preferredNetworkType, cdmaSubscription, instanceId); mContext = context; mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mEccList = new MtkEccList(); } private static String localRequestToString(int request) { switch(request) { case RIL_REQUEST_RESUME_REGISTRATION: return "RIL_REQUEST_RESUME_REGISTRATION"; case RIL_REQUEST_SET_CALL_INDICATION: return "RIL_REQUEST_SET_CALL_INDICATION"; case RIL_REQUEST_EMERGENCY_DIAL: return "RIL_REQUEST_EMERGENCY_DIAL"; case RIL_REQUEST_SET_ECC_SERVICE_CATEGORY: return "RIL_REQUEST_SET_ECC_SERVICE_CATEGORY"; case RIL_REQUEST_SET_ECC_LIST: return "RIL_REQUEST_SET_ECC_LIST"; case RIL_REQUEST_MODEM_POWEROFF: return "RIL_REQUEST_MODEM_POWEROFF"; case RIL_REQUEST_MODEM_POWERON: return "RIL_REQUEST_MODEM_POWERON"; default: return ""; } } @Override protected void processUnsolicited (Parcel p) { Object ret; int dataPosition = p.dataPosition(); // save off position within the Parcel int response = p.readInt(); switch(response) { case RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED: ret = responseRegSuspended(p); break; case RIL_UNSOL_INCOMING_CALL_INDICATION: ret = responseIncomingCallIndication(p); break; case RIL_UNSOL_CALL_INFO_INDICATION: ret = responseCallProgress(p); break; case RIL_UNSOL_SET_ATTACH_APN: ret = responseSetAttachApn(p); break; case RIL_UNSOL_ON_USSD: ret = responseStrings(p); break; case RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED: ret = responseInts(p); break; default: // Rewind the Parcel p.setDataPosition(dataPosition); // Forward responses that we are not overriding to the super class super.processUnsolicited(p); return; } switch(response) { case RIL_UNSOL_INCOMING_CALL_INDICATION: mCallStateRegistrants .notifyRegistrants(new AsyncResult(null, null, null)); break; case RIL_UNSOL_CALL_INFO_INDICATION: if (ret == null) { mCallStateRegistrants .notifyRegistrants(new AsyncResult(null, null, null)); } break; case RIL_UNSOL_ON_USSD: String[] resp = (String[])ret; if (resp.length < 2) { resp = new String[2]; resp[0] = ((String[])ret)[0]; resp[1] = null; } if (resp[0] != null && Integer.parseInt(resp[0]) >= 2 && Integer.parseInt(resp[0]) <=5 ) { // support USSD_SESSION_END and USSD_HANDLED_BY_STK as normal finishes resp[0] = "0"; } if (RILJ_LOGD) unsljLogMore(response, resp[0]); if (mUSSDRegistrant != null) { mUSSDRegistrant.notifyRegistrant( new AsyncResult (null, resp, null)); } break; case RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED: if (((int[])ret)[0] != 4) mVoiceNetworkStateRegistrants .notifyRegistrants(new AsyncResult(null, null, null)); break; } } protected Object responseRegSuspended(Parcel p) { int numInts; int response[]; numInts = p.readInt(); response = new int[numInts]; for (int i = 0 ; i < numInts ; i++) { response[i] = p.readInt(); } RILRequest rr = RILRequest.obtain(RIL_REQUEST_RESUME_REGISTRATION, null); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + localRequestToString(rr.mRequest) + " sessionId " + response[0]); rr.mParcel.writeInt(1); rr.mParcel.writeInt(response[0]); send(rr); return response; } protected Object responseIncomingCallIndication(Parcel p) { String response[]; response = p.readStringArray(); RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_CALL_INDICATION, null); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + localRequestToString(rr.mRequest)); rr.mParcel.writeInt(3); rr.mParcel.writeInt(Integer.parseInt(response[3])); rr.mParcel.writeInt(Integer.parseInt(response[0])); rr.mParcel.writeInt(Integer.parseInt(response[4])); send(rr); return response; } protected Object responseCallProgress(Parcel p) { String response[]; response = p.readStringArray(); if (response.length >= 2) { if (response[1].equals("129")) { // Call termination return null; } } return response; } @Override public void setInitialAttachApn(String apn, String protocol, int authType, String username, String password, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_INITIAL_ATTACH_APN, null); String operatorNumber = mTelephonyManager.getSimOperatorNumericForPhone(mInstanceId); if (RILJ_LOGD) { riljLog("Set RIL_REQUEST_SET_INITIAL_ATTACH_APN"); } rr.mParcel.writeString(apn); rr.mParcel.writeString(protocol); rr.mParcel.writeInt(authType); rr.mParcel.writeString(username); rr.mParcel.writeString(password); rr.mParcel.writeString(operatorNumber); rr.mParcel.writeInt(1); rr.mParcel.writeStringArray(null); if (RILJ_LOGD) { riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + ", apn:" + apn + ", protocol:" + protocol + ", authType:" + authType + ", username:" + username + ", password:" + password + ", operator:" + operatorNumber); } send(rr); } protected Object responseSetAttachApn(Parcel p) { // The stack refuses to attach to LTE unless an IA APN was set, and // will loop until it happens. Set an empty one to unblock. setInitialAttachApn("","",0,"","",null); return null; } @Override protected Object responseSimRefresh(Parcel p) { IccRefreshResponse response = new IccRefreshResponse(); response.refreshResult = p.readInt(); String rawefId = p.readString(); response.efId = rawefId == null ? 0 : Integer.parseInt(rawefId); response.aid = p.readString(); if (response.refreshResult > IccRefreshResponse.REFRESH_RESULT_RESET) { if (response.refreshResult == REFRESH_SESSION_RESET) { response.refreshResult = IccRefreshResponse.REFRESH_RESULT_RESET; } else { response.refreshResult = IccRefreshResponse.REFRESH_RESULT_INIT; } } return response; } @Override public void setupDataCall(String radioTechnology, String profile, String apn, String user, String password, String authType, String protocol, Message result) { int interfaceId=0; RILRequest rr = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result); rr.mParcel.writeInt(8); //bumped by one rr.mParcel.writeString(radioTechnology); rr.mParcel.writeString(profile); rr.mParcel.writeString(apn); rr.mParcel.writeString(user); rr.mParcel.writeString(password); rr.mParcel.writeString(authType); rr.mParcel.writeString(protocol); /* Find the first available interfaceId */ for (int i=0; i < 4; i++) { if (dataCallCids[i] < 0) { interfaceId = i+1; break; } } rr.mParcel.writeString(interfaceId+""); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + radioTechnology + " " + profile + " " + apn + " " + user + " " + password + " " + authType + " " + protocol + " " + interfaceId); send(rr); } @Override public void deactivateDataCall(int cid, int reason, Message result) { for (int i=0; i < 4; i++) { if (dataCallCids[i] == cid) { dataCallCids[i] = -1; break; } } super.deactivateDataCall(cid, reason, result); } @Override public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { if (mEccList.isEmergencyNumberExt(address)) { int serviceCategory = mEccList.getServiceCategoryFromEcc(address); RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_ECC_SERVICE_CATEGORY, null); rr.mParcel.writeInt(1); rr.mParcel.writeInt(serviceCategory); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + localRequestToString(rr.mRequest) + " " + serviceCategory); send(rr); rr = RILRequest.obtain(RIL_REQUEST_EMERGENCY_DIAL, result); rr.mParcel.writeString(address); rr.mParcel.writeInt(clirMode); rr.mParcel.writeInt(0); // UUS information is absent if (uusInfo == null) { rr.mParcel.writeInt(0); // UUS information is absent } else { rr.mParcel.writeInt(1); // UUS information is present rr.mParcel.writeInt(uusInfo.getType()); rr.mParcel.writeInt(uusInfo.getDcs()); rr.mParcel.writeByteArray(uusInfo.getUserData()); } if (RILJ_LOGD) riljLog(rr.serialString() + "> " + localRequestToString(rr.mRequest)); send(rr); } else { super.dial(address, clirMode, uusInfo, result); } } private void refreshEmergencyList() { if (mEccList != null) mEccList.updateEmergencyNumbersProperty(); } @Override public void setRadioPower(boolean on, Message result) { boolean isInApm = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 1; boolean wasInApm = SystemProperties.get("persist.radio.airplane.mode.on").equals("true"); SystemProperties.set("persist.radio.airplane.mode.on", isInApm ? "true" : "false"); if (on && wasInApm && !isInApm) { SystemProperties.set("gsm.ril.eboot", "0"); RILRequest rr = RILRequest.obtain(RIL_REQUEST_MODEM_POWERON, result); if (RILJ_LOGD) { riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); } send(rr); } else if (!on && isInApm) { SystemProperties.set("gsm.ril.eboot", "1"); RILRequest rr = RILRequest.obtain(RIL_REQUEST_MODEM_POWEROFF, result); if (RILJ_LOGD) { riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); } send(rr); } else { super.setRadioPower(on, result); } } // Solicited request handling @Override protected RILRequest processSolicited (Parcel p) { int serial, error; int dataPosition = p.dataPosition(); // save off position within the Parcel serial = p.readInt(); error = p.readInt(); RILRequest rr = null; /* Pre-process the reply before popping it */ synchronized (mRequestList) { RILRequest tr = mRequestList.get(serial); if (tr != null && tr.mSerial == serial) { if (error == 0 || p.dataAvail() > 0) { try {switch (tr.mRequest) { /* Get those we're interested in */ case RIL_REQUEST_EMERGENCY_DIAL: case RIL_REQUEST_SET_ECC_SERVICE_CATEGORY: case RIL_REQUEST_DATA_REGISTRATION_STATE: case RIL_REQUEST_SETUP_DATA_CALL: case RIL_REQUEST_ALLOW_DATA: rr = tr; break; // vendor ril refreshes the properties while generating this answer. Do our own updates afterwards case RIL_REQUEST_GET_SIM_STATUS: refreshEmergencyList(); // no break, we want the superclass to process this }} catch (Throwable thr) { // Exceptions here usually mean invalid RIL responses if (tr.mResult != null) { AsyncResult.forMessage(tr.mResult, null, thr); tr.mResult.sendToTarget(); } return tr; } } } } if (rr == null) { /* Nothing we care about, go up */ p.setDataPosition(dataPosition); // Forward responses that we are not overriding to the super class return super.processSolicited(p); } rr = findAndRemoveRequestFromList(serial); if (rr == null) { return rr; } Object ret = null; if (error == 0 || p.dataAvail() > 0) { switch (rr.mRequest) { case RIL_REQUEST_EMERGENCY_DIAL: ret = responseVoid(p); break; case RIL_REQUEST_SET_ECC_SERVICE_CATEGORY: ret = responseVoid(p); break; case RIL_REQUEST_DATA_REGISTRATION_STATE: ret = fixupPSBearerDataRegistration(p); break; case RIL_REQUEST_SETUP_DATA_CALL: ret = fetchCidFromDataCall(p); break; case RIL_REQUEST_ALLOW_DATA: ret = responseVoid(p); mVoiceNetworkStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null)); break; default: throw new RuntimeException("Shouldn't be here: " + rr.mRequest); } //break; } if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) + " " + retToString(rr.mRequest, ret)); if (rr.mResult != null) { AsyncResult.forMessage(rr.mResult, ret, null); rr.mResult.sendToTarget(); } return rr; } private Object fixupPSBearerDataRegistration(Parcel p) { int num; String response[]; response = p.readStringArray(); if (response.length >= 4 && response[3] != null && Integer.parseInt(response[3]) >= 128) { // extended values >= RIL_RADIO_TECHNOLOGY_MTK (128) == HSPAP (15) response[3] = "15"; } return response; } private Object fetchCidFromDataCall(Parcel p) { DataCallResponse ret = (DataCallResponse)super.responseSetupDataCall(p); if (ret.cid >= 0) { for (int i = 0; i < 4; i++) { if (dataCallCids[i] < 0) { dataCallCids[i] = ret.cid; break; } } } return ret; } @Override public void iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3, String data, String pin2, String aid, Message result) { if (command == 0xc0 && p3 == 0) { Rlog.i("MT6753", "Override the size for the COMMAND_GET_RESPONSE 0 => 15"); p3 = 15; } super.iccIOForApp(command, fileid, path, p1, p2, p3, data, pin2, aid, result); } }