diff --git a/MotoNrEnabler/Android.bp b/MotoNrEnabler/Android.bp new file mode 100644 index 0000000..b6ef9a4 --- /dev/null +++ b/MotoNrEnabler/Android.bp @@ -0,0 +1,18 @@ +// +// SPDX-FileCopyrightText: 2024 The LineageOS Project +// SPDX-License-Identifier: Apache-2.0 +// + +android_app { + name: "MotoNrEnabler", + + srcs: [ + "src/**/*.aidl", + "src/**/*.kt", + ], + libs: ["telephony-common"], + + certificate: "platform", + platform_apis: true, + system_ext_specific: true, +} diff --git a/MotoNrEnabler/AndroidManifest.xml b/MotoNrEnabler/AndroidManifest.xml new file mode 100644 index 0000000..d62896e --- /dev/null +++ b/MotoNrEnabler/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + diff --git a/MotoNrEnabler/src/com/qualcomm/qcrilmsgtunnel/IQcrilMsgTunnel.aidl b/MotoNrEnabler/src/com/qualcomm/qcrilmsgtunnel/IQcrilMsgTunnel.aidl new file mode 100644 index 0000000..a53f219 --- /dev/null +++ b/MotoNrEnabler/src/com/qualcomm/qcrilmsgtunnel/IQcrilMsgTunnel.aidl @@ -0,0 +1,5 @@ +package com.qualcomm.qcrilmsgtunnel; + +interface IQcrilMsgTunnel { + int sendOemRilRequestRaw(in byte[] request, out byte[] response, in int sub); +} diff --git a/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/BootCompletedReceiver.kt b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/BootCompletedReceiver.kt new file mode 100644 index 0000000..50191ab --- /dev/null +++ b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/BootCompletedReceiver.kt @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2024 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.motorola.nrenabler + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log + +class BootCompletedReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + Log.d(TAG, "Starting") + context.startService(Intent(context, NrEnablerService::class.java)) + } + + companion object { + private const val TAG = "MotoNrEnabler" + } +} diff --git a/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/NrEnablerService.kt b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/NrEnablerService.kt new file mode 100644 index 0000000..4aea5a3 --- /dev/null +++ b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/NrEnablerService.kt @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2024 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.motorola.nrenabler + +import android.app.Service +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Handler +import android.os.IBinder +import android.telephony.CarrierConfigManager +import android.telephony.SubscriptionInfo +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import android.util.Log +import java.util.concurrent.atomic.AtomicBoolean + +class NrEnablerService : Service() { + private lateinit var motoExtService: QcomMotoExtTelephonyService + private val handler by lazy { Handler(mainLooper) } + private val workingInProgress = AtomicBoolean(false) + + private val repeatWorkOnNRModeAndDSSIfFail = object : Runnable { + override fun run() { + if (workingInProgress.getAndSet(true)) + return + if (!workOnNRModeAndDSS()) { + Log.v(TAG, "workOnNRModeAndDSS failed, retry after 5s") + handler.removeCallbacks(this) + handler.postDelayed(this, 5000) + } + workingInProgress.set(false) + } + } + + private val broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (!workingInProgress.get()) { + handler.post(repeatWorkOnNRModeAndDSSIfFail) + } + } + } + + override fun onCreate() { + motoExtService = QcomMotoExtTelephonyService(this) + registerReceiver( + broadcastReceiver, IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) + ) + } + + private fun workOnNRModeAndDSS(): Boolean { + val activeSubs = + getSystemService(SubscriptionManager::class.java)?.getActiveSubscriptionInfoList() + if (activeSubs.isNullOrEmpty()) { + Log.v(TAG, "workOnNRModeAndDSS: no active sub.") + return true + } + for (aSubInfo in activeSubs) { + val phoneId = SubscriptionManager.getPhoneId(aSubInfo.subscriptionId) + if (!validatePhoneId(phoneId)) { + Log.e(TAG, "Invalid phoneId: $phoneId") + return false + } + + // Moto sets them based on carrier config, but we unconditionally + // enable NR and DSS here because maintaining carrier config is + // intractable for us. + Log.v(TAG, "workOnNRModeAndDSS: setNrModeDisabled for phone ${phoneId}") + if (!motoExtService.setNrModeDisabled(phoneId, NrMode.AUTO)) { + return false + } + Log.v(TAG, "workOnNRModeAndDSS: setDSSEnabled for phone ${phoneId}") + if (!motoExtService.setDSSEnabled(phoneId, 1.toByte())) { + return false + } + } + return true + } + + private fun validatePhoneId(phoneId: Int): Boolean { + val phoneCount = getSystemService(TelephonyManager::class.java).activeModemCount + return phoneId in 0 until phoneCount + } + + override fun onBind(intent: Intent?): IBinder? = null + + companion object { + private const val TAG = "MotoNrEnabler" + } +} diff --git a/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/NrMode.kt b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/NrMode.kt new file mode 100644 index 0000000..26534e1 --- /dev/null +++ b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/NrMode.kt @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2024 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.motorola.nrenabler + +enum class NrMode(private val id: Int) { + AUTO(0), + DISABLE_SA(1), + DISABLE_NSA(2); + + fun toInt(): Int { + return id + } + + companion object { + fun fromInt(id: Int): NrMode? { + for (en in NrMode.values()) { + if (en.id == id) { + return en + } + } + return null + } + } +} diff --git a/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomMotoExtTelephonyService.kt b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomMotoExtTelephonyService.kt new file mode 100644 index 0000000..5b1e6b8 --- /dev/null +++ b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomMotoExtTelephonyService.kt @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2024 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.motorola.nrenabler + +import android.content.Context +import android.util.Log +import com.android.internal.telephony.PhoneFactory +import java.nio.ByteBuffer + +class QcomMotoExtTelephonyService(private val context: Context) { + private val qcrilMsgTunnelConnector = QcrilMsgTunnelConnector(context) + + fun setNrModeDisabled(phoneId: Int, mode: NrMode): Boolean { + val nrModeInModem = getNrModeDisabled(phoneId) + Log.v(TAG, "nrModeInModem = $nrModeInModem") + if (mode == nrModeInModem) { + Log.d( + TAG, + "setNrModeDisabled equals nrModeInModem:$nrModeInModem, ignore set for phoneID:$phoneId" + ) + return true + } + val data = ByteArray(9) + val buf = ByteBuffer.wrap(data) + buf.order(QcomOemConstants.getByteOrderByRequestId(QcomOemConstants.OEM_RIL_REQUEST_SET_NR_DISABLE_MODE)) + buf.putInt(QcomOemConstants.OEM_RIL_REQUEST_SET_NR_DISABLE_MODE).putInt(1) + .put(mode.toInt().toByte()) + return qcrilMsgTunnelConnector.invokeOemRilRequestRawForPhone(phoneId, data, null) >= 0 + } + + private fun getNrModeDisabled(phoneId: Int): NrMode? { + val data = ByteArray(8) + val respData = ByteArray(1) + val buf = ByteBuffer.wrap(data) + buf.order(QcomOemConstants.getByteOrderByRequestId(QcomOemConstants.OEM_RIL_REQUEST_GET_NR_DISABLE_MODE)) + buf.putInt(QcomOemConstants.OEM_RIL_REQUEST_GET_NR_DISABLE_MODE) + if (qcrilMsgTunnelConnector.invokeOemRilRequestRawForPhone(phoneId, data, respData) >= 0) { + return NrMode.fromInt(respData[0].toInt()) + } + return null + } + + private fun getDSSEnabled(phoneId: Int): Byte { + val rdeNv = + qcrilMsgTunnelConnector.getRdeNvValueByElementId(phoneId, QcomNvInfo.RDE_EFS_DSS_I) + return (rdeNv?.dataObj as QcomNvInfo.NvGenericDataType?)?.data?.get(0) ?: 2.toByte() + } + + fun setDSSEnabled(phoneId: Int, enabled: Byte): Boolean { + val prev = getDSSEnabled(phoneId) + Log.v(TAG, "previous DSS mode = $prev") + if (prev == enabled) { + Log.d(TAG, "Skip setDSSEnabled as no change.") + return true + } + return qcrilMsgTunnelConnector.setRdeNvValue(phoneId, QcomNvInfo.RDE_EFS_DSS_I, enabled) + } + + companion object { + private const val TAG = "MotoNrEnabler: QcomMotoExtTelephonyService" + } +} diff --git a/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomNvInfo.kt b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomNvInfo.kt new file mode 100644 index 0000000..55abde8 --- /dev/null +++ b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomNvInfo.kt @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2024 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.motorola.nrenabler + +import android.util.Log +import java.nio.ByteBuffer +import java.nio.ByteOrder + +object QcomNvInfo { + private const val TAG = "MotoNrEnabler: QcomNvInfo" + const val RDE_EFS_DSS_I = 10030 + + interface NvDataType { + fun serialize(buf: ByteBuffer) + fun size(): Int + } + + class RdeNvValue { + var elementId = 0 + var recordNum = 0 + var offset = 0 + var length = 0 + var dataObj: NvDataType? = null + val size: Int + get() { + return (dataObj?.size() ?: 1) + 16 + } + } + + class NvGenericDataType : NvDataType { + var data: ByteArray? = null + + constructor() + constructor(byte: Byte) { + data = byteArrayOf(byte) + } + + override fun serialize(buf: ByteBuffer) { + data?.let { + buf.put(it) + } + } + + override fun size(): Int { + return data?.size ?: 0 + } + } + + fun getRdeByteOrder(): ByteOrder { + return QcomOemConstants.getByteOrderByRequestId(QcomOemConstants.OEM_RIL_REQUEST_CDMA_GET_RDE_ITEM) + } + + fun getRdeNvName(elementId: Int): String { + return when (elementId) { + RDE_EFS_DSS_I -> "RDE_EFS_DSS_I" + else -> { + Log.w(TAG, "unknown RDE element ID: $elementId") + "" + } + } + } +} diff --git a/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomNvUtils.kt b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomNvUtils.kt new file mode 100644 index 0000000..c28952f --- /dev/null +++ b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomNvUtils.kt @@ -0,0 +1,217 @@ +/* + * SPDX-FileCopyrightText: 2024 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.motorola.nrenabler + +import android.util.Log +import java.nio.BufferUnderflowException +import java.nio.ByteBuffer + + +object QcomNvUtils { + private const val TAG = "MotoNrEnabler: QcomNvUtils" + + private const val DEFAULT_SPC_CODE = "000000" + private const val READING_RDE_RESP_BUF_SIZE = 6144 + private const val WRITING_RESP_BUF_SIZE = 2048 + + data class OemHookDataHeader( + val reqId: Int, + val dataLength: Int, + val error: OemHookRespError, + ) { + val spcLockCode = ByteArray(6) + override fun toString(): String { + return "reqId = $reqId dataLength = $dataLength error = $error spcLockCode = ${ + byteArrToStringLog( + spcLockCode + ) + }" + } + + companion object { + const val SIZE = 18 + } + } + + fun readOemHookRespHeader(reqId: Int, bytes: ByteArray?): OemHookDataHeader? { + return bytes?.let { + readOemHookRespHeader( + ByteBuffer.wrap(it).order(QcomOemConstants.getByteOrderByRequestId(reqId)) + ) + } + } + + private fun readOemHookRespHeader(buf: ByteBuffer): OemHookDataHeader? { + return try { + val header = OemHookDataHeader( + buf.getInt(), + buf.getInt(), + OemHookRespError.fromInt(buf.getInt()), + ) + for (i in 0 until header.spcLockCode.size) { + header.spcLockCode[i] = buf.get() + } + Log.d(TAG, "readOemHookRespHeader: $header") + header + } catch (e: BufferUnderflowException) { + Log.w(TAG, "decode RespHeader exception, BufferUnderflowException") + null + } + } + + fun getReadingRdeNvReqData(rdeNv: QcomNvInfo.RdeNvValue): ByteArray { + return allocateRdeOemReqData( + QcomOemConstants.OEM_RIL_REQUEST_CDMA_GET_RDE_ITEM, rdeNv, DEFAULT_SPC_CODE + ) + } + + fun getWritingRdeNvReqData(rdeNv: QcomNvInfo.RdeNvValue): ByteArray { + return allocateRdeOemReqData( + QcomOemConstants.OEM_RIL_REQUEST_CDMA_SET_RDE_ITEM, rdeNv, DEFAULT_SPC_CODE + ) + } + + private fun allocateRdeOemReqData( + reqId: Int, rdeNv: QcomNvInfo.RdeNvValue, spcCode: String + ): ByteArray { + val buf = ByteBuffer.allocate(rdeNv.size + OemHookDataHeader.SIZE) + buf.order(QcomNvInfo.getRdeByteOrder()) + writeOemHookReqHeader( + buf, reqId, rdeNv.size, OemHookRespError.OEM_RIL_CDMA_SUCCESS, spcCode + ) + buf.putInt(rdeNv.elementId) + buf.putInt(rdeNv.recordNum) + buf.putInt(rdeNv.offset) + rdeNv.dataObj.let { + if (it != null) { + buf.putInt(it.size()) + it.serialize(buf) + } else { + buf.putInt(0) + buf.put(0.toByte()) + } + } + val data = buf.array() + Log.d( + TAG, + "RDE request for element: ${QcomNvInfo.getRdeNvName(rdeNv.elementId)} Allocated OemReqData: data = ${ + byteArrToStringLog( + data + ) + }" + ) + return data + } + + fun allocateReadingRdeNvRespBuffer(): ByteArray { + return ByteArray(READING_RDE_RESP_BUF_SIZE) + } + + fun allocateWritingRdeNvRespBuffer(): ByteArray { + return ByteArray(WRITING_RESP_BUF_SIZE) + } + + fun decodeReadingRdeNvResult(resultData: ByteArray?): QcomNvInfo.RdeNvValue? { + if (resultData == null) { + return null + } + val buf = ByteBuffer.wrap(resultData).order(QcomNvInfo.getRdeByteOrder()) + return try { + val header = readOemHookRespHeader(buf) + if (header != null && header.error === OemHookRespError.OEM_RIL_CDMA_SUCCESS) { + return deserializeRde(buf) + } + Log.w(TAG, "decodeReadingRdeNv get error for head") + null + } catch (e: BufferUnderflowException) { + Log.e(TAG, "decodeReadingRdeNvResult: buffer underflow") + null + } + } + + private fun deserializeRde(buf: ByteBuffer): QcomNvInfo.RdeNvValue { + val rdeNv = QcomNvInfo.RdeNvValue() + rdeNv.elementId = buf.getInt() + rdeNv.recordNum = buf.getInt() + rdeNv.offset = buf.getInt() + rdeNv.length = buf.getInt() + + Log.d(TAG, "decoding response for ${QcomNvInfo.getRdeNvName(rdeNv.elementId)}") + + when (rdeNv.elementId) { + QcomNvInfo.RDE_EFS_DSS_I -> { + if (rdeNv.length > 0) { + val nvData = QcomNvInfo.NvGenericDataType() + nvData.data = buf.array().copyOfRange(34, 34 + rdeNv.length) + rdeNv.dataObj = nvData + } + } + + else -> Log.d(TAG, "deserialize unknown elementId (${rdeNv.elementId})") + } + return rdeNv + } + + fun byteArrToStringLog(arr: ByteArray?): String { + if (arr == null || arr.isEmpty()) { + return "null" + } + val sb = StringBuilder() + for (i in arr) { + sb.append(String.format("%02X", i)) + } + return sb.toString() + } + + private fun writeOemHookReqHeader( + buf: ByteBuffer, reqId: Int, len: Int, err: OemHookRespError, spcLockCode: String + ) { + writeOemHookReqHeader(buf, reqId, len, err, spcLockCode.toByteArray()) + } + + private fun writeOemHookReqHeader( + buf: ByteBuffer, reqId: Int, len: Int, err: OemHookRespError, spcLockCode: ByteArray + ) { + buf.putInt(reqId) + buf.putInt(len) + buf.putInt(err.toInt()) + for (i in spcLockCode) { + buf.put(i) + } + Log.d( + TAG, + "writeOemHookReqHeader: reqId = $reqId dataLength = $len error = $err spcLockCode = ${ + byteArrToStringLog( + spcLockCode + ) + }" + ) + } + + enum class OemHookRespError(private val id: Int) { + OEM_RIL_CDMA_SUCCESS(0), + OEM_RIL_CDMA_RADIO_NOT_AVAILABLE(1), + OEM_RIL_CDMA_NAM_READ_WRITE_FAILURE(2), + OEM_RIL_CDMA_NAM_PASSWORD_INCORRECT(3), + OEM_RIL_CDMA_NAM_ACCESS_COUNTER_EXCEEDED(4), + OEM_RIL_CDMA_GENERIC_FAILURE(5); + + fun toInt(): Int { + return id + } + + companion object { + fun fromInt(id: Int): OemHookRespError { + for (en in values()) { + if (en.id == id) { + return en + } + } + return OEM_RIL_CDMA_GENERIC_FAILURE + } + } + } +} diff --git a/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomOemConstants.kt b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomOemConstants.kt new file mode 100644 index 0000000..345fa07 --- /dev/null +++ b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcomOemConstants.kt @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2024 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.motorola.nrenabler + +import android.util.Log +import java.nio.ByteOrder + +object QcomOemConstants { + const val TAG = "MotoNrEnabler: QcomOemConstants" + + private const val OEM_RIL_CDMA_MESSAGE_TYPE_CDMA = 33554432 + const val OEM_RIL_REQUEST_GET_NR_DISABLE_MODE = 327752 + const val OEM_RIL_REQUEST_SET_NR_DISABLE_MODE = 327753 + const val OEM_RIL_REQUEST_CDMA_SET_RDE_ITEM = 33554453 + const val OEM_RIL_REQUEST_CDMA_GET_RDE_ITEM = 33554454 + + fun getByteOrderByRequestId(reqId: Int): ByteOrder { + return if (reqId >= OEM_RIL_CDMA_MESSAGE_TYPE_CDMA) { + ByteOrder.LITTLE_ENDIAN + } else ByteOrder.BIG_ENDIAN + } + + fun getRequestName(reqId: Int): String { + return when (reqId) { + OEM_RIL_REQUEST_CDMA_SET_RDE_ITEM -> "OEM_RIL_REQUEST_CDMA_SET_RDE_ITEM" + OEM_RIL_REQUEST_CDMA_GET_RDE_ITEM -> "OEM_RIL_REQUEST_CDMA_GET_RDE_ITEM" + else -> { + Log.w(TAG, "unknown request ID: $reqId") + "" + } + } + } +} diff --git a/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcrilMsgTunnelConnector.kt b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcrilMsgTunnelConnector.kt new file mode 100644 index 0000000..8d14265 --- /dev/null +++ b/MotoNrEnabler/src/org/lineageos/motorola/nrenabler/QcrilMsgTunnelConnector.kt @@ -0,0 +1,161 @@ +/* + * SPDX-FileCopyrightText: 2024 The LineageOS Project + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.lineageos.motorola.nrenabler + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.os.Handler +import android.os.IBinder +import android.os.RemoteException +import android.util.Log +import com.android.internal.telephony.uicc.IccUtils +import com.qualcomm.qcrilmsgtunnel.IQcrilMsgTunnel + +class QcrilMsgTunnelConnector(private val context: Context) { + private val handler = Handler(context.mainLooper) + + private var qcrilMsgService: IQcrilMsgTunnel? = null + private val qcrilMsgTunnelConnection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected(name: ComponentName, service: IBinder) { + Log.d(TAG, "QcrilMsgTunnel Service connected") + qcrilMsgService = IQcrilMsgTunnel.Stub.asInterface(service) + if (qcrilMsgService == null) { + Log.e(TAG, "QcrilMsgTunnelService Connect Failed (onServiceConnected)") + return + } + service.linkToDeath(qcrilMsgServiceDeathRecipient, 0) + } + + override fun onServiceDisconnected(name: ComponentName) { + Log.e(TAG, "The connection to the service got disconnected unexpectedly!") + qcrilMsgService = null + } + } + private val qcrilMsgServiceDeathRecipient = IBinder.DeathRecipient { + Log.e(TAG, "QcrilMsgService Died") + context.unbindService(qcrilMsgTunnelConnection) + handler.postDelayed({ bindToQcrilMsgTunnelService() }, 4000) + } + + init { + bindToQcrilMsgTunnelService() + } + + private fun bindToQcrilMsgTunnelService() { + val intent = Intent() + intent.setClassName(QCRIL_MSG_TUNNEL_PACKAGE_NAME, QCRIL_MSG_TUNNEL_SERVICE_NAME) + Log.d(TAG, "Starting QcrilMsgTunnel Service") + context.bindService(intent, qcrilMsgTunnelConnection, Context.BIND_AUTO_CREATE) + } + + fun invokeOemRilRequestRawForPhone(phoneId: Int, oemReq: ByteArray?, oemResp: ByteArray?): Int { + return qcrilMsgService?.let { + Log.d( + TAG, "invokeOemRilRequestRawForSubscriber: phoneId = $phoneId oemReq = ${ + IccUtils.bytesToHexString( + oemReq + ) + }" + ) + val rspData = oemResp ?: ByteArray(1) + try { + val ret = it.sendOemRilRequestRaw(oemReq, rspData, phoneId) + Log.d( + TAG, "invokeOemRilRequestRawForSubscriber: phoneId = $phoneId oemResp = ${ + IccUtils.bytesToHexString(rspData) + }" + ) + ret + } catch (e: RemoteException) { + Log.e(TAG, "sendOemRilRequestRaw: Runtime Exception") + -1 + } + } ?: run { + Log.e(TAG, "QcrilMsgTunnel Service not connected") + -1 + } + } + + private fun getRdeNvValueByElementId( + phoneId: Int, rdeElementId: Int, recordNum: Int + ): QcomNvInfo.RdeNvValue? { + if (rdeElementId < 0) { + return null + } + val rdeNv = QcomNvInfo.RdeNvValue() + rdeNv.elementId = rdeElementId + rdeNv.recordNum = recordNum + val reqRdeData: ByteArray = QcomNvUtils.getReadingRdeNvReqData(rdeNv) + val respRdeData: ByteArray = QcomNvUtils.allocateReadingRdeNvRespBuffer() + return if (invokeOemRilRequestRawForPhone( + phoneId, reqRdeData, respRdeData + ) < 0 + ) { + null + } else QcomNvUtils.decodeReadingRdeNvResult(respRdeData) + } + + fun getRdeNvValueByElementId(phoneId: Int, rdeElementId: Int): QcomNvInfo.RdeNvValue? { + return getRdeNvValueByElementId(phoneId, rdeElementId, 0) + } + + fun setRdeNvValue(phoneId: Int, rdeElementId: Int, value: Byte): Boolean { + val data = QcomNvInfo.NvGenericDataType(value) + return setRdeNvValue(phoneId, rdeElementId, data) + } + + private fun setRdeNvValue( + phoneId: Int, rdeElementId: Int, nvData: QcomNvInfo.NvDataType + ): Boolean { + return setRdeNvValue(phoneId, rdeElementId, 0, nvData) + } + + private fun setRdeNvValue( + phoneId: Int, rdeElementId: Int, rdeRecordNum: Int, nvData: QcomNvInfo.NvDataType + ): Boolean { + val nv = QcomNvInfo.RdeNvValue() + nv.elementId = rdeElementId + nv.recordNum = rdeRecordNum + nv.dataObj = nvData + return setRdeNvValue(phoneId, nv) + } + + private fun setRdeNvValue(phoneId: Int, nv: QcomNvInfo.RdeNvValue): Boolean { + val reqData: ByteArray = QcomNvUtils.getWritingRdeNvReqData(nv) + val respData: ByteArray = QcomNvUtils.allocateWritingRdeNvRespBuffer() + return getWritingRdeNvRespResult(phoneId, reqData, respData) + } + + private fun getWritingRdeNvRespResult( + phoneId: Int, reqData: ByteArray, respData: ByteArray + ): Boolean { + return getWritingNvRespResult( + phoneId, QcomOemConstants.OEM_RIL_REQUEST_CDMA_SET_RDE_ITEM, reqData, respData + ) + } + + private fun getWritingNvRespResult( + phoneId: Int, reqId: Int, reqData: ByteArray, respData: ByteArray + ): Boolean { + if (invokeOemRilRequestRawForPhone(phoneId, reqData, respData) < 0) { + return false + } + val respHeader = QcomNvUtils.readOemHookRespHeader(reqId, respData) ?: return false + Log.d( + TAG, "get Writing NV result for ${QcomOemConstants.getRequestName(respHeader.reqId)}" + ) + return respHeader.error == QcomNvUtils.OemHookRespError.OEM_RIL_CDMA_SUCCESS + } + + companion object { + private const val TAG = "MotoNrEnabler: QcrilMsgTunnelConnector" + private const val QCRIL_MSG_TUNNEL_PACKAGE_NAME = "com.qualcomm.qcrilmsgtunnel" + private const val QCRIL_MSG_TUNNEL_SERVICE_NAME = + "com.qualcomm.qcrilmsgtunnel.QcrilMsgTunnelService" + } +}