--- /dev/null
+//
+// 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,
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ SPDX-FileCopyrightText: 2024 The LineageOS Project
+ SPDX-License-Identifier: Apache-2.0
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.lineageos.motorola.nrenabler"
+ android:sharedUserId="android.uid.phone">
+
+ <uses-permission android:name="com.qualcomm.permission.USE_QCRIL_MSG_TUNNEL" />
+
+ <application
+ android:label="MotoNrEnabler"
+ android:persistent="true"
+ android:directBootAware="true">
+ <service
+ android:name=".NrEnablerService" />
+
+ <receiver
+ android:name=".BootCompletedReceiver"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </receiver>
+ </application>
+
+</manifest>
--- /dev/null
+package com.qualcomm.qcrilmsgtunnel;
+
+interface IQcrilMsgTunnel {
+ int sendOemRilRequestRaw(in byte[] request, out byte[] response, in int sub);
+}
--- /dev/null
+/*
+ * 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"
+ }
+}
--- /dev/null
+/*
+ * 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"
+ }
+}
--- /dev/null
+/*
+ * 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
+ }
+ }
+}
--- /dev/null
+/*
+ * 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"
+ }
+}
--- /dev/null
+/*
+ * 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")
+ ""
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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")
+ ""
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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"
+ }
+}