Introduce nrmodeswitcher

This configures the NR mode to "SA Preferred" upon each boot (a setting
that persists even after a factory reset), which is desirable in most
cases. The mode can be customized using ro.vendor.radio.auto_nr_mode:
  0: NSA Preferred
  1: NSA Only
  2: SA Only
  3: SA Preferred

Change-Id: I86ad739167a51af04c041887a93462bf5bb5e32f
This commit is contained in:
dianlujitao
2025-07-19 20:07:41 +08:00
committed by Bruno Martins
parent 29a150eca3
commit 5be9c6e55b
7 changed files with 424 additions and 0 deletions

19
nrmodeswitcher/Android.bp Normal file
View File

@@ -0,0 +1,19 @@
//
// SPDX-FileCopyrightText: 2025 The LineageOS Project
// SPDX-License-Identifier: Apache-2.0
//
cc_binary {
name: "nrmodeswitcher",
init_rc: ["nrmodeswitcher.rc"],
vendor: true,
srcs: [
"main.cpp",
"OplusRadioResponse.cpp",
],
shared_libs: [
"libbase",
"libbinder_ndk",
"vendor.oplus.hardware.radio-V2-ndk",
],
}

View File

@@ -0,0 +1,206 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#include "OplusRadioResponse.h"
namespace aidl::vendor::oplus::hardware::radio {
OplusRadioResponse::OplusRadioResponse(int32_t& in_result) : in_result_(in_result) {}
ndk::ScopedAStatus OplusRadioResponse::setCallbackExtResponse() {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setNrModeResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) {
in_result_ = in_result;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getNrModeResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setModemErrorFatalResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setVoNrEnabledResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getVoNrEnabledResponse(const OplusRadioResponseInfo& in_info,
bool in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setEccListResponse(const OplusRadioResponseInfo& in_info,
const std::string& in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::SetHsrModeForListeningResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::SetLogIdForListeningResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setSlowStartResponse(const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setCtVolteModeResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::simlockReqResponse(const OplusRadioResponseInfo& in_info,
const std::vector<uint8_t>& in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::updateRegionlockBlobResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::updateRegionlockStatusResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getRegionlockStatusResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setNwCongestionCfgResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setModemEsimStatusResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockOperatorIdResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockFeestateResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setSimlockFeestateResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockUnlockStateResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setSimlockFactoryResetTimeResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockFactoryResetTimeResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setSimlockActivateTimeResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockActivateTimeResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockFeatureResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_feature) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockLockMarkResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_lockmark) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockDeviceLockStateResponse(
const OplusRadioResponseInfo& in_info, int32_t in_lockstate) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockDeviceLockinfoResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_lockinfo) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockVersionInfoResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_version) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockMaxRetryResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockCurrentRetryResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setPsDetachAttachActionResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::unlockRegionlockResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::updateRegionlockKeyResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getRegionlockSettingDataResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setRegionlockSettingDataResponse(
const OplusRadioResponseInfo& in_info, int32_t in_result) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::setSimlockOfflineLockResponse(
const OplusRadioResponseInfo& in_info) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OplusRadioResponse::getSimlockOfflineLockResponse(
const OplusRadioResponseInfo& in_info, const std::vector<int32_t>& in_retryCount) {
return ndk::ScopedAStatus::ok();
}
}; // namespace aidl::vendor::oplus::hardware::radio

View File

@@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <aidl/vendor/oplus/hardware/radio/BnOplusRadioResponse.h>
namespace aidl::vendor::oplus::hardware::radio {
class OplusRadioResponse : public BnOplusRadioResponse {
public:
explicit OplusRadioResponse(int32_t& in_result);
ndk::ScopedAStatus setCallbackExtResponse() override;
ndk::ScopedAStatus setNrModeResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus getNrModeResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus setModemErrorFatalResponse(const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus setVoNrEnabledResponse(const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus getVoNrEnabledResponse(const OplusRadioResponseInfo& in_info,
bool in_result) override;
ndk::ScopedAStatus setEccListResponse(const OplusRadioResponseInfo& in_info,
const std::string& in_result) override;
ndk::ScopedAStatus SetHsrModeForListeningResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus SetLogIdForListeningResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus setSlowStartResponse(const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus setCtVolteModeResponse(const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus simlockReqResponse(const OplusRadioResponseInfo& in_info,
const std::vector<uint8_t>& in_result) override;
ndk::ScopedAStatus updateRegionlockBlobResponse(const OplusRadioResponseInfo& in_info,
const std::vector<uint8_t>& in_status) override;
ndk::ScopedAStatus updateRegionlockStatusResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) override;
ndk::ScopedAStatus getRegionlockStatusResponse(const OplusRadioResponseInfo& in_info,
const std::vector<uint8_t>& in_status) override;
ndk::ScopedAStatus setNwCongestionCfgResponse(const OplusRadioResponseInfo& in_info,
const std::vector<uint8_t>& in_status) override;
ndk::ScopedAStatus setModemEsimStatusResponse(const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus getSimlockOperatorIdResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus getSimlockFeestateResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus setSimlockFeestateResponse(const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus getSimlockUnlockStateResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus setSimlockFactoryResetTimeResponse(
const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus getSimlockFactoryResetTimeResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) override;
ndk::ScopedAStatus setSimlockActivateTimeResponse(
const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus getSimlockActivateTimeResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_status) override;
ndk::ScopedAStatus getSimlockFeatureResponse(const OplusRadioResponseInfo& in_info,
const std::vector<uint8_t>& in_feature) override;
ndk::ScopedAStatus getSimlockLockMarkResponse(const OplusRadioResponseInfo& in_info,
const std::vector<uint8_t>& in_lockmark) override;
ndk::ScopedAStatus getSimlockDeviceLockStateResponse(const OplusRadioResponseInfo& in_info,
int32_t in_lockstate) override;
ndk::ScopedAStatus getSimlockDeviceLockinfoResponse(
const OplusRadioResponseInfo& in_info,
const std::vector<uint8_t>& in_lockinfo) override;
ndk::ScopedAStatus getSimlockVersionInfoResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_version) override;
ndk::ScopedAStatus getSimlockMaxRetryResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus getSimlockCurrentRetryResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus setPsDetachAttachActionResponse(
const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus unlockRegionlockResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus updateRegionlockKeyResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus getRegionlockSettingDataResponse(
const OplusRadioResponseInfo& in_info, const std::vector<uint8_t>& in_result) override;
ndk::ScopedAStatus setRegionlockSettingDataResponse(const OplusRadioResponseInfo& in_info,
int32_t in_result) override;
ndk::ScopedAStatus setSimlockOfflineLockResponse(
const OplusRadioResponseInfo& in_info) override;
ndk::ScopedAStatus getSimlockOfflineLockResponse(
const OplusRadioResponseInfo& in_info,
const std::vector<int32_t>& in_retryCount) override;
private:
int32_t& in_result_;
};
}; // namespace aidl::vendor::oplus::hardware::radio

89
nrmodeswitcher/main.cpp Normal file
View File

@@ -0,0 +1,89 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <thread>
#include <aidl/vendor/oplus/hardware/radio/IOplusRadio.h>
#include "OplusRadioResponse.h"
using aidl::vendor::oplus::hardware::radio::IOplusRadio;
using aidl::vendor::oplus::hardware::radio::OplusRadioResponse;
using android::base::GetIntProperty;
using std::chrono_literals::operator""s;
namespace {
enum OplusNrMode {
NSA_PRE,
NSA_ONLY,
SA_ONLY,
SA_PRE,
NR_MODE_MAX,
};
constexpr auto kDefaultAutoNrMode = SA_PRE;
constexpr auto kPropertyDefaultAutoNrMode = "ro.vendor.radio.auto_nr_mode";
constexpr auto kIRadioAidlServiceName = "vendor.oplus.hardware.radio.IRadioStable/OplusRadio";
constexpr auto kMaxSimSlot = 2;
constexpr auto kOplusRilSerial = 1001;
OplusNrMode GetDefaultAutoNrMode() {
auto mode = GetIntProperty(kPropertyDefaultAutoNrMode, static_cast<int>(kDefaultAutoNrMode));
if (mode < NSA_PRE || mode >= NR_MODE_MAX) {
LOG(WARNING) << "Invalid NR mode: " << mode << ", using default: " << kDefaultAutoNrMode;
return kDefaultAutoNrMode;
}
return static_cast<OplusNrMode>(mode);
}
}; // anonymous namespace
int main() {
int rc = EXIT_SUCCESS;
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
auto mode = GetDefaultAutoNrMode();
for (auto i = 0; i < kMaxSimSlot; ++i) {
auto instance = kIRadioAidlServiceName + std::to_string(i);
if (!AServiceManager_isDeclared(instance.c_str())) {
// Instance not declared in manifest, skip.
continue;
}
auto radio = IOplusRadio::fromBinder(
ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str())));
if (!radio) {
LOG(DEBUG) << "Failed to get service " << instance;
continue;
}
int32_t in_result = 0;
auto resp_cb = ndk::SharedRefBase::make<OplusRadioResponse>(in_result);
radio->setCallback(resp_cb, nullptr);
bool succeed = false;
for (auto retry = 100; retry > 0; --retry) {
auto status = radio->setNrMode(kOplusRilSerial, mode);
if (!status.isOk() || in_result != 0) {
LOG(ERROR) << "setNrMode failed for SIM" << i
<< ", (remaining retries: " << retry - 1 << ")";
std::this_thread::sleep_for(1s);
continue;
}
succeed = true;
break;
}
if (!succeed) rc = EXIT_FAILURE;
}
return rc;
}

View File

@@ -0,0 +1,5 @@
service vendor.nrmodeswitcher /vendor/bin/nrmodeswitcher
class late_start
user radio
group radio
oneshot