Merge "ExternalCamera: Fix a deadlock crash using std::thread" into main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 8c53006..c18983e 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,7 +8,4 @@
gofmt = true
[Hook Scripts]
-aosp_hook_confirmationui = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} confirmationui
-aosp_hook_gatekeeper = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} gatekeeper
-aosp_hook_keymaster = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} keymaster
generate_vehicle_property_enums = ${REPO_ROOT}/hardware/interfaces/automotive/vehicle/tools/generate_annotation_enums.py --android_build_top ${REPO_ROOT} --preupload_files ${PREUPLOAD_FILES} --check_only
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 123a5ec..aa624ff 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -214,24 +214,33 @@
StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
mVendorDebug.forceTransientBurst,
mVendorDebug.forceSynchronousDrain};
- std::unique_ptr<StreamContext::DataMQ> dataMQ = nullptr;
- std::shared_ptr<IStreamCallback> streamAsyncCallback = nullptr;
std::shared_ptr<ISoundDose> soundDose;
if (!getSoundDose(&soundDose).isOk()) {
LOG(ERROR) << __func__ << ": could not create sound dose instance";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
- if (!hasMmapFlag(flags)) {
- dataMQ = std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames);
- streamAsyncCallback = asyncCallback;
+ StreamContext temp;
+ if (hasMmapFlag(flags)) {
+ MmapBufferDescriptor mmapDesc;
+ RETURN_STATUS_IF_ERROR(
+ createMmapBuffer(*portConfigIt, in_bufferSizeFrames, frameSize, &mmapDesc));
+ temp = StreamContext(
+ std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
+ std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
+ portConfigIt->format.value(), portConfigIt->channelMask.value(),
+ portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
+ portConfigIt->ext.get<AudioPortExt::mix>().handle, std::move(mmapDesc),
+ outEventCallback, mSoundDose.getInstance(), params);
+ } else {
+ temp = StreamContext(
+ std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
+ std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
+ portConfigIt->format.value(), portConfigIt->channelMask.value(),
+ portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
+ portConfigIt->ext.get<AudioPortExt::mix>().handle,
+ std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
+ asyncCallback, outEventCallback, mSoundDose.getInstance(), params);
}
- StreamContext temp(
- std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
- std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
- portConfigIt->format.value(), portConfigIt->channelMask.value(),
- portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
- portConfigIt->ext.get<AudioPortExt::mix>().handle, std::move(dataMQ),
- streamAsyncCallback, outEventCallback, mSoundDose.getInstance(), params);
if (temp.isValid()) {
*out_context = std::move(temp);
} else {
@@ -394,9 +403,10 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
-ndk::ScopedAStatus Module::createMmapBuffer(
- const ::aidl::android::hardware::audio::core::StreamContext& context __unused,
- ::aidl::android::hardware::audio::core::StreamDescriptor* desc __unused) {
+ndk::ScopedAStatus Module::createMmapBuffer(const AudioPortConfig& portConfig __unused,
+ int32_t bufferSizeFrames __unused,
+ int32_t frameSizeBytes __unused,
+ MmapBufferDescriptor* desc __unused) {
LOG(ERROR) << __func__ << ": " << mType << ": is not implemented";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
@@ -977,9 +987,6 @@
RETURN_STATUS_IF_ERROR(createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
nullptr, nullptr, &context));
context.fillDescriptor(&_aidl_return->desc);
- if (hasMmapFlag(context.getFlags())) {
- RETURN_STATUS_IF_ERROR(createMmapBuffer(context, &_aidl_return->desc));
- }
std::shared_ptr<StreamIn> stream;
RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata,
getMicrophoneInfos(), &stream));
@@ -1027,9 +1034,6 @@
isNonBlocking ? in_args.callback : nullptr,
in_args.eventCallback, &context));
context.fillDescriptor(&_aidl_return->desc);
- if (hasMmapFlag(context.getFlags())) {
- RETURN_STATUS_IF_ERROR(createMmapBuffer(context, &_aidl_return->desc));
- }
std::shared_ptr<StreamOut> stream;
RETURN_STATUS_IF_ERROR(createOutputStream(std::move(context), in_args.sourceMetadata,
in_args.offloadInfo, &stream));
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 2800bed..873fc48 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -65,18 +65,26 @@
if (mReplyMQ) {
desc->reply = mReplyMQ->dupeDesc();
}
+ desc->frameSizeBytes = getFrameSize();
+ desc->bufferSizeFrames = getBufferSizeInFrames();
if (mDataMQ) {
- desc->frameSizeBytes = getFrameSize();
- desc->bufferSizeFrames = getBufferSizeInFrames();
desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
+ } else {
+ MmapBufferDescriptor mmapDesc; // Move-only due to `fd`.
+ mmapDesc.sharedMemory.fd = mMmapBufferDesc.sharedMemory.fd.dup();
+ mmapDesc.sharedMemory.size = mMmapBufferDesc.sharedMemory.size;
+ mmapDesc.burstSizeFrames = mMmapBufferDesc.burstSizeFrames;
+ mmapDesc.flags = mMmapBufferDesc.flags;
+ desc->audio.set<StreamDescriptor::AudioBuffer::Tag::mmap>(std::move(mmapDesc));
}
}
size_t StreamContext::getBufferSizeInFrames() const {
if (mDataMQ) {
return mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / getFrameSize();
+ } else {
+ return mMmapBufferDesc.sharedMemory.size / getFrameSize();
}
- return 0;
}
size_t StreamContext::getFrameSize() const {
@@ -96,9 +104,13 @@
LOG(ERROR) << "frame size is invalid";
return false;
}
- if (!hasMmapFlag(mFlags) && mDataMQ && !mDataMQ->isValid()) {
+ if (!isMmap() && mDataMQ && !mDataMQ->isValid()) {
LOG(ERROR) << "data FMQ is invalid";
return false;
+ } else if (isMmap() &&
+ (mMmapBufferDesc.sharedMemory.fd.get() == -1 ||
+ mMmapBufferDesc.sharedMemory.size == 0 || mMmapBufferDesc.burstSizeFrames == 0)) {
+ LOG(ERROR) << "mmap info is invalid" << mMmapBufferDesc.toString();
}
return true;
}
@@ -115,6 +127,7 @@
mCommandMQ.reset();
mReplyMQ.reset();
mDataMQ.reset();
+ mMmapBufferDesc.sharedMemory.fd.set(-1);
}
pid_t StreamWorkerCommonLogic::getTid() const {
@@ -128,7 +141,7 @@
std::string StreamWorkerCommonLogic::init() {
if (mContext->getCommandMQ() == nullptr) return "Command MQ is null";
if (mContext->getReplyMQ() == nullptr) return "Reply MQ is null";
- if (!hasMmapFlag(mContext->getFlags())) {
+ if (!mContext->isMmap()) {
StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
if (dataMQ == nullptr) return "Data MQ is null";
if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
@@ -167,7 +180,7 @@
} else {
reply->observable = reply->hardware = kUnknownPosition;
}
- if (hasMmapFlag(mContext->getFlags())) {
+ if (mContext->isMmap()) {
if (auto status = mDriver->getMmapPositionAndLatency(&reply->hardware, &reply->latencyMs);
status != ::android::OK) {
reply->hardware = kUnknownPosition;
@@ -252,9 +265,8 @@
mState == StreamDescriptor::State::ACTIVE ||
mState == StreamDescriptor::State::PAUSED ||
mState == StreamDescriptor::State::DRAINING) {
- if (bool success = hasMmapFlag(mContext->getFlags())
- ? readMmap(&reply)
- : read(fmqByteCount, &reply);
+ if (bool success =
+ mContext->isMmap() ? readMmap(&reply) : read(fmqByteCount, &reply);
!success) {
mState = StreamDescriptor::State::ERROR;
}
@@ -548,9 +560,8 @@
if (mState != StreamDescriptor::State::ERROR &&
mState != StreamDescriptor::State::TRANSFERRING &&
mState != StreamDescriptor::State::TRANSFER_PAUSED) {
- if (bool success = hasMmapFlag(mContext->getFlags())
- ? writeMmap(&reply)
- : write(fmqByteCount, &reply);
+ if (bool success = mContext->isMmap() ? writeMmap(&reply)
+ : write(fmqByteCount, &reply);
!success) {
mState = StreamDescriptor::State::ERROR;
}
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 0661015..379264d 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -212,8 +212,8 @@
const ::aidl::android::media::audio::common::AudioFormatDescription &format,
int32_t latencyMs, int32_t sampleRateHz, int32_t *bufferSizeFrames);
virtual ndk::ScopedAStatus createMmapBuffer(
- const ::aidl::android::hardware::audio::core::StreamContext& context,
- ::aidl::android::hardware::audio::core::StreamDescriptor* desc);
+ const ::aidl::android::media::audio::common::AudioPortConfig& portConfig,
+ int32_t bufferSizeFrames, int32_t frameSizeBytes, MmapBufferDescriptor* desc);
// Utility and helper functions accessible to subclasses.
static int32_t calculateBufferSizeFramesForPcm(int32_t latencyMs, int32_t sampleRateHz) {
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 376c684..bb790e9 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -104,6 +104,27 @@
mOutEventCallback(outEventCallback),
mStreamDataProcessor(streamDataProcessor),
mDebugParameters(debugParameters) {}
+ StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
+ const ::aidl::android::media::audio::common::AudioFormatDescription& format,
+ const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
+ int sampleRate, const ::aidl::android::media::audio::common::AudioIoFlags& flags,
+ int32_t nominalLatencyMs, int32_t mixPortHandle, MmapBufferDescriptor&& mmapDesc,
+ std::shared_ptr<IStreamOutEventCallback> outEventCallback,
+ std::weak_ptr<sounddose::StreamDataProcessorInterface> streamDataProcessor,
+ DebugParameters debugParameters)
+ : mCommandMQ(std::move(commandMQ)),
+ mInternalCommandCookie(std::rand() | 1 /* make sure it's not 0 */),
+ mReplyMQ(std::move(replyMQ)),
+ mFormat(format),
+ mChannelLayout(channelLayout),
+ mSampleRate(sampleRate),
+ mFlags(flags),
+ mNominalLatencyMs(nominalLatencyMs),
+ mMixPortHandle(mixPortHandle),
+ mMmapBufferDesc(std::move(mmapDesc)),
+ mOutEventCallback(outEventCallback),
+ mStreamDataProcessor(streamDataProcessor),
+ mDebugParameters(debugParameters) {}
void fillDescriptor(StreamDescriptor* desc);
std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
@@ -136,6 +157,7 @@
bool isInput() const {
return mFlags.getTag() == ::aidl::android::media::audio::common::AudioIoFlags::input;
}
+ bool isMmap() const { return ::aidl::android::hardware::audio::common::hasMmapFlag(mFlags); }
bool isValid() const;
// 'reset' is called on a Binder thread when closing the stream. Does not use
// locking because it only cleans MQ pointers which were also set on the Binder thread.
@@ -155,7 +177,9 @@
::aidl::android::media::audio::common::AudioIoFlags mFlags;
int32_t mNominalLatencyMs;
int32_t mMixPortHandle;
+ // Only one of `mDataMQ` or `mMapBufferDesc` can be active, depending on `isMmap`
std::unique_ptr<DataMQ> mDataMQ;
+ MmapBufferDescriptor mMmapBufferDesc;
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
std::weak_ptr<sounddose::StreamDataProcessorInterface> mStreamDataProcessor;
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml b/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml
index 4170b4c..c92e852 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml
@@ -33,6 +33,6 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="{MODULE}" />
- <option name="native-test-timeout" value="30m" />
+ <option name="native-test-timeout" value="10m" />
</test>
</configuration>
diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
index 2ce7b51..98f7d79 100644
--- a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
+++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp
@@ -465,6 +465,7 @@
float fullScaleSineDb) {
ASSERT_NO_FATAL_FAILURE(SetUpDynamicsProcessingEffect());
SKIP_TEST_IF_DATA_UNSUPPORTED(mDescriptor.common.flags);
+ mInput.resize(kFrameCount * mChannelCount);
ASSERT_NO_FATAL_FAILURE(
generateSineWave(testFrequencies, mInput, 1.0, kSamplingFrequency, mChannelLayout));
mInputDb = calculateDb(mInput);
@@ -722,13 +723,10 @@
public DynamicsProcessingTestHelper {
public:
DynamicsProcessingInputGainDataTest()
- : DynamicsProcessingTestHelper((GetParam()), AudioChannelLayout::LAYOUT_MONO) {
- mInput.resize(kFrameCount * mChannelCount);
- }
+ : DynamicsProcessingTestHelper((GetParam()), AudioChannelLayout::LAYOUT_MONO) {}
void SetUp() override {
- ASSERT_NO_FATAL_FAILURE(
- setUpDataTest({static_cast<int>(kInputFrequency)}, kSineFullScaleDb));
+ ASSERT_NO_FATAL_FAILURE(setUpDataTest({kInputFrequency}, kSineFullScaleDb));
}
void TearDown() override { TearDownDynamicsProcessingEffect(); }
@@ -851,15 +849,12 @@
: public ::testing::TestWithParam<LimiterConfigDataTestParams>,
public DynamicsProcessingTestHelper {
public:
- DynamicsProcessingLimiterConfigDataTest()
- : DynamicsProcessingTestHelper(GetParam(), AudioChannelLayout::LAYOUT_MONO) {
- mBufferSize = kFrameCount * mChannelCount;
- mInput.resize(mBufferSize);
- }
+ DynamicsProcessingLimiterConfigDataTest(LimiterConfigDataTestParams param = GetParam(),
+ int32_t layout = AudioChannelLayout::LAYOUT_MONO)
+ : DynamicsProcessingTestHelper(param, layout) {}
void SetUp() override {
- ASSERT_NO_FATAL_FAILURE(
- setUpDataTest({static_cast<int>(kInputFrequency)}, kSineFullScaleDb));
+ ASSERT_NO_FATAL_FAILURE(setUpDataTest({kInputFrequency}, kSineFullScaleDb));
}
void TearDown() override { TearDownDynamicsProcessingEffect(); }
@@ -876,12 +871,35 @@
ratio = inputOverThreshold / outputOverThreshold;
}
- void setLimiterParamsAndProcess(std::vector<float>& input, std::vector<float>& output) {
+ void setLimiterParamsAndProcess(std::vector<float>& input, std::vector<float>& output,
+ bool isEngineLimiterEnabled = true) {
+ mEngineConfigPreset.limiterInUse = isEngineLimiterEnabled;
addEngineConfig(mEngineConfigPreset);
addLimiterConfig(mLimiterConfigList);
EXPECT_NO_FATAL_FAILURE(setParamsAndProcess(input, output));
}
+ void testEnableDisableConfiguration(bool isLimiterEnabled, bool isEngineLimiterEnabled) {
+ cleanUpLimiterConfig();
+ std::vector<float> output(mInput.size());
+ for (int i = 0; i < mChannelCount; i++) {
+ // Set non-default values
+ fillLimiterConfig(mLimiterConfigList, i, isLimiterEnabled, kDefaultLinkerGroup,
+ 5 /*attack time*/, 5 /*release time*/, 10 /*ratio*/,
+ -20 /*threshold*/, 5 /*postgain*/);
+ }
+ ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output, isEngineLimiterEnabled));
+ float outputdB = calculateDb(output, kStartIndex);
+ if (isAllParamsValid()) {
+ if (isLimiterEnabled && isEngineLimiterEnabled) {
+ EXPECT_GT(std::abs(mInputDb - outputdB), kMinDifferenceDb)
+ << "Input level: " << mInputDb << " Output level: " << outputdB;
+ } else {
+ EXPECT_NEAR(mInputDb, outputdB, kLimiterTestToleranceDb);
+ }
+ }
+ }
+
void cleanUpLimiterConfig() {
CleanUp();
mLimiterConfigList.clear();
@@ -892,8 +910,9 @@
static constexpr float kDefaultRatio = 4;
static constexpr float kDefaultThreshold = -10;
static constexpr float kDefaultPostGain = 0;
- static constexpr float kInputFrequency = 1000;
static constexpr float kLimiterTestToleranceDb = 0.05;
+ static constexpr float kMinDifferenceDb = 5;
+ const std::vector<bool> kEnableValues = {true, false, true};
std::vector<DynamicsProcessing::LimiterConfig> mLimiterConfigList;
int mBufferSize;
};
@@ -975,25 +994,16 @@
}
TEST_P(DynamicsProcessingLimiterConfigDataTest, LimiterEnableDisable) {
- std::vector<bool> limiterEnableValues = {false, true};
- std::vector<float> output(mInput.size());
- for (bool isEnabled : limiterEnableValues) {
- cleanUpLimiterConfig();
- for (int i = 0; i < mChannelCount; i++) {
- // Set non-default values
- fillLimiterConfig(mLimiterConfigList, i, isEnabled, kDefaultLinkerGroup,
- 5 /*attack time*/, 5 /*release time*/, 10 /*ratio*/,
- -10 /*threshold*/, 5 /*postgain*/);
- }
- ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
- if (!isAllParamsValid()) {
- continue;
- }
- if (isEnabled) {
- EXPECT_NE(mInputDb, calculateDb(output, kStartIndex));
- } else {
- EXPECT_NEAR(mInputDb, calculateDb(output, kStartIndex), kLimiterTestToleranceDb);
- }
+ for (bool isLimiterEnabled : kEnableValues) {
+ ASSERT_NO_FATAL_FAILURE(
+ testEnableDisableConfiguration(isLimiterEnabled, true /*Engine Enabled*/));
+ }
+}
+
+TEST_P(DynamicsProcessingLimiterConfigDataTest, LimiterEnableDisableViaEngine) {
+ for (bool isEngineLimiterEnabled : kEnableValues) {
+ ASSERT_NO_FATAL_FAILURE(
+ testEnableDisableConfiguration(true /*Limiter Enabled*/, isEngineLimiterEnabled));
}
}
@@ -1010,6 +1020,103 @@
});
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingLimiterConfigDataTest);
+class DynamicsProcessingLimiterLinkerDataTest : public DynamicsProcessingLimiterConfigDataTest {
+ public:
+ DynamicsProcessingLimiterLinkerDataTest()
+ : DynamicsProcessingLimiterConfigDataTest(GetParam(), AudioChannelLayout::LAYOUT_STEREO) {}
+
+ void calculateExpectedOutputDb(std::vector<float>& expectedOutputDb) {
+ std::vector<float> inputDbValues = calculateStereoDb(mInput, kStartIndex);
+ ASSERT_EQ(inputDbValues.size(), kRatioThresholdPairValues.size());
+ EXPECT_NEAR(inputDbValues[0], inputDbValues[1], kToleranceDb);
+ for (size_t i = 0; i < kRatioThresholdPairValues.size(); i++) {
+ const auto& [ratio, threshold] = kRatioThresholdPairValues[i];
+ expectedOutputDb.push_back((inputDbValues[i] - threshold) / ratio + threshold);
+ }
+ }
+
+ std::vector<float> calculateStereoDb(const std::vector<float>& input,
+ size_t startSamplePos = 0) {
+ std::vector<float> leftChannel;
+ std::vector<float> rightChannel;
+ for (size_t i = 0; i < input.size(); i += 2) {
+ leftChannel.push_back(input[i]);
+ if (i + 1 < input.size()) {
+ rightChannel.push_back(input[i + 1]);
+ }
+ }
+ return {calculateDb(leftChannel, startSamplePos),
+ calculateDb(rightChannel, startSamplePos)};
+ }
+
+ void setLinkGroupAndProcess(std::vector<float>& output, bool hasSameLinkGroup) {
+ for (int i = 0; i < mChannelCount; i++) {
+ const auto& [ratio, threshold] = kRatioThresholdPairValues[i];
+ ASSERT_NE(ratio, 0);
+ int linkGroup = hasSameLinkGroup ? kDefaultLinkerGroup : i;
+ fillLimiterConfig(mLimiterConfigList, i, true, linkGroup, kDefaultAttackTime,
+ kDefaultReleaseTime, ratio, threshold, kDefaultPostGain);
+ }
+
+ ASSERT_NO_FATAL_FAILURE(setLimiterParamsAndProcess(mInput, output));
+
+ if (!isAllParamsValid()) {
+ GTEST_SKIP() << "Invalid parameters. Skipping the test\n";
+ }
+ }
+
+ const std::vector<std::pair<float, float>> kRatioThresholdPairValues = {{2, -10}, {5, -20}};
+};
+
+TEST_P(DynamicsProcessingLimiterLinkerDataTest, SameLinkGroupDifferentConfigs) {
+ std::vector<float> output(mInput.size());
+
+ ASSERT_NO_FATAL_FAILURE(setLinkGroupAndProcess(output, true));
+
+ std::vector<float> outputDbValues = calculateStereoDb(output, kStartIndex);
+
+ std::vector<float> expectedOutputDbValues;
+ ASSERT_NO_FATAL_FAILURE(calculateExpectedOutputDb(expectedOutputDbValues));
+
+ // Verify that the actual output dB is same as the calculated maximum attenuation.
+ float expectedOutputDb = std::min(expectedOutputDbValues[0], expectedOutputDbValues[1]);
+ EXPECT_NEAR(outputDbValues[0], expectedOutputDb, kToleranceDb);
+ EXPECT_NEAR(outputDbValues[1], expectedOutputDb, kToleranceDb);
+}
+
+TEST_P(DynamicsProcessingLimiterLinkerDataTest, DifferentLinkGroupDifferentConfigs) {
+ std::vector<float> output(mInput.size());
+
+ ASSERT_NO_FATAL_FAILURE(setLinkGroupAndProcess(output, false));
+
+ std::vector<float> outputDbValues = calculateStereoDb(output, kStartIndex);
+
+ std::vector<float> expectedOutputDbValues;
+ ASSERT_NO_FATAL_FAILURE(calculateExpectedOutputDb(expectedOutputDbValues));
+
+ // Verify that both channels have different compression levels
+ EXPECT_GT(abs(expectedOutputDbValues[0] - expectedOutputDbValues[1]), kMinDifferenceDb)
+ << "Left channel level: " << expectedOutputDbValues[0]
+ << " Right channel level: " << expectedOutputDbValues[1];
+
+ // Verify that the actual output and the calculated dB values are same
+ EXPECT_NEAR(outputDbValues[0], expectedOutputDbValues[0], kToleranceDb);
+ EXPECT_NEAR(outputDbValues[1], expectedOutputDbValues[1], kToleranceDb);
+}
+
+INSTANTIATE_TEST_SUITE_P(DynamicsProcessingTest, DynamicsProcessingLimiterLinkerDataTest,
+ testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+ IFactory::descriptor, getEffectTypeUuidDynamicsProcessing())),
+ [](const auto& info) {
+ auto descriptor = info.param;
+ std::string name = getPrefix(descriptor.second);
+ std::replace_if(
+ name.begin(), name.end(),
+ [](const char c) { return !std::isalnum(c); }, '_');
+ return name;
+ });
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingLimiterLinkerDataTest);
+
/**
* Test DynamicsProcessing ChannelConfig
*/
@@ -1215,7 +1322,6 @@
public:
DynamicsProcessingEqBandConfigDataTest()
: DynamicsProcessingTestHelper(GetParam(), AudioChannelLayout::LAYOUT_MONO) {
- mInput.resize(kFrameCount * mChannelCount);
mBinOffsets.resize(mMultitoneTestFrequencies.size());
}
@@ -1444,7 +1550,6 @@
public:
DynamicsProcessingMbcBandConfigDataTest()
: DynamicsProcessingTestHelper(GetParam(), AudioChannelLayout::LAYOUT_MONO) {
- mInput.resize(kFrameCount * mChannelCount);
mBinOffsets.resize(mMultitoneTestFrequencies.size());
}
diff --git a/automotive/TEST_MAPPING b/automotive/TEST_MAPPING
index f041ca6..4a4c3e2 100644
--- a/automotive/TEST_MAPPING
+++ b/automotive/TEST_MAPPING
@@ -1,9 +1,6 @@
{
"auto-presubmit": [
{
- "name": "AndroidCarApiTest"
- },
- {
"name": "CarHiddenApiTest"
},
{
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
index f149c45..f8f102a 100644
--- a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -161,6 +161,8 @@
}
bool rename(std::string_view from, std::string_view to) {
+ if (!down(from)) return false;
+
nl::MessageFactory<ifinfomsg> req(RTM_SETLINK);
req.add(IFLA_IFNAME, to);
diff --git a/automotive/vehicle/aidl/impl/current/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/current/fake_impl/hardware/src/FakeVehicleHardware.cpp
index 4eb84dd..be909c5 100644
--- a/automotive/vehicle/aidl/impl/current/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/current/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -2091,7 +2091,7 @@
}
triggerSupportedValueChange(propId, areaId);
- return StringPrintf("Min/Max supported value for propId: %s, areaId: %s set",
+ return StringPrintf("Min/Max supported value for propId: %s, areaId: %s set\n",
maybeInfo->propIdStr.c_str(), maybeInfo->propIdStr.c_str());
}
@@ -2135,7 +2135,7 @@
*maybeSupportedValues;
}
triggerSupportedValueChange(maybeInfo->propId, maybeInfo->areaId);
- return StringPrintf("Supported values list for propId: %s, areaId: %s set",
+ return StringPrintf("Supported values list for propId: %s, areaId: %s set\n",
maybeInfo->propIdStr.c_str(), maybeInfo->propIdStr.c_str());
}
diff --git a/bluetooth/audio/flags/btaudiohal.aconfig b/bluetooth/audio/flags/btaudiohal.aconfig
index 13e2116..35e84de 100644
--- a/bluetooth/audio/flags/btaudiohal.aconfig
+++ b/bluetooth/audio/flags/btaudiohal.aconfig
@@ -13,4 +13,11 @@
namespace: "pixel_bluetooth"
description: "Flag for reporting lea broadcast audio config to HAL"
bug: "321168976"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "leaudio_sw_offload"
+ namespace: "pixel_bluetooth"
+ description: "Flag for using sw offload path to send premium audio"
+ bug: "398885696"
+}
diff --git a/broadcastradio/aidl/android/hardware/broadcastradio/AlertUrgency.aidl b/broadcastradio/aidl/android/hardware/broadcastradio/AlertUrgency.aidl
index c7bfdbc..daebb4e 100644
--- a/broadcastradio/aidl/android/hardware/broadcastradio/AlertUrgency.aidl
+++ b/broadcastradio/aidl/android/hardware/broadcastradio/AlertUrgency.aidl
@@ -17,7 +17,7 @@
package android.hardware.broadcastradio;
/**
- * The severity of the subject event of the emergency alert message.
+ * The urgency of the subject event of the emergency alert message.
*
* <p>(see ITU-T X.1303 bis for more info).
*/
diff --git a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
index 92f69bd..e2c7208 100644
--- a/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
+++ b/camera/provider/aidl/vts/VtsAidlHalCameraProvider_TargetTest.cpp
@@ -1628,10 +1628,10 @@
Stream previewStream;
std::shared_ptr<DeviceCb> cb;
- configurePreviewStreams(
+ ASSERT_NO_FATAL_FAILURE(configurePreviewStreams(
name, mProvider, &previewThreshold, physicalIds, &mSession, &previewStream,
&halStreams /*out*/, &supportsPartialResults /*out*/, &partialResultCount /*out*/,
- &halBufManagedStreamIds /*out*/, &cb /*out*/, 0 /*streamConfigCounter*/, true);
+ &halBufManagedStreamIds /*out*/, &cb /*out*/, 0 /*streamConfigCounter*/, true));
if (mSession == nullptr) {
// stream combination not supported by HAL, skip test for device
continue;
@@ -2244,10 +2244,10 @@
Stream previewStream;
std::vector<HalStream> halStreams;
std::shared_ptr<DeviceCb> cb;
- configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/,
- &previewStream /*out*/, &halStreams /*out*/,
- &supportsPartialResults /*out*/, &partialResultCount /*out*/,
- &useHalBufManager /*out*/, &cb /*out*/);
+ ASSERT_NO_FATAL_FAILURE(configurePreviewStream(
+ name, mProvider, &previewThreshold, &mSession /*out*/, &previewStream /*out*/,
+ &halStreams /*out*/, &supportsPartialResults /*out*/, &partialResultCount /*out*/,
+ &useHalBufManager /*out*/, &cb /*out*/));
::aidl::android::hardware::common::fmq::MQDescriptor<
int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite>
@@ -2373,10 +2373,10 @@
bool supportsPartialResults = false;
bool useHalBufManager = false;
int32_t partialResultCount = 0;
- configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/,
- &previewStream /*out*/, &halStreams /*out*/,
- &supportsPartialResults /*out*/, &partialResultCount /*out*/,
- &useHalBufManager /*out*/, &cb /*out*/);
+ ASSERT_NO_FATAL_FAILURE(configurePreviewStream(
+ name, mProvider, &previewThreshold, &mSession /*out*/, &previewStream /*out*/,
+ &halStreams /*out*/, &supportsPartialResults /*out*/, &partialResultCount /*out*/,
+ &useHalBufManager /*out*/, &cb /*out*/));
ASSERT_NE(mSession, nullptr);
ASSERT_FALSE(halStreams.empty());
@@ -2637,10 +2637,10 @@
bool supportsPartialResults = false;
bool useHalBufManager = false;
int32_t partialResultCount = 0;
- configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/,
- &previewStream /*out*/, &halStreams /*out*/,
- &supportsPartialResults /*out*/, &partialResultCount /*out*/,
- &useHalBufManager /*out*/, &cb /*out*/);
+ ASSERT_NO_FATAL_FAILURE(configurePreviewStream(
+ name, mProvider, &previewThreshold, &mSession /*out*/, &previewStream /*out*/,
+ &halStreams /*out*/, &supportsPartialResults /*out*/, &partialResultCount /*out*/,
+ &useHalBufManager /*out*/, &cb /*out*/));
RequestTemplate reqTemplate = RequestTemplate::PREVIEW;
ndk::ScopedAStatus ret = mSession->constructDefaultRequestSettings(reqTemplate, &settings);
@@ -2692,10 +2692,10 @@
bool useHalBufManager = false;
int32_t partialResultCount = 0;
- configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/,
- &previewStream /*out*/, &halStreams /*out*/,
- &supportsPartialResults /*out*/, &partialResultCount /*out*/,
- &useHalBufManager /*out*/, &cb /*out*/);
+ ASSERT_NO_FATAL_FAILURE(configurePreviewStream(
+ name, mProvider, &previewThreshold, &mSession /*out*/, &previewStream /*out*/,
+ &halStreams /*out*/, &supportsPartialResults /*out*/, &partialResultCount /*out*/,
+ &useHalBufManager /*out*/, &cb /*out*/));
ASSERT_NE(mSession, nullptr);
ASSERT_NE(cb, nullptr);
@@ -2817,10 +2817,10 @@
bool useHalBufManager = false;
int32_t partialResultCount = 0;
- configurePreviewStream(name, mProvider, &previewThreshold, &mSession /*out*/,
- &previewStream /*out*/, &halStreams /*out*/,
- &supportsPartialResults /*out*/, &partialResultCount /*out*/,
- &useHalBufManager /*out*/, &cb /*out*/);
+ ASSERT_NO_FATAL_FAILURE(configurePreviewStream(
+ name, mProvider, &previewThreshold, &mSession /*out*/, &previewStream /*out*/,
+ &halStreams /*out*/, &supportsPartialResults /*out*/, &partialResultCount /*out*/,
+ &useHalBufManager /*out*/, &cb /*out*/));
ndk::ScopedAStatus returnStatus = mSession->flush();
ASSERT_TRUE(returnStatus.isOk());
diff --git a/camera/provider/aidl/vts/camera_aidl_test.cpp b/camera/provider/aidl/vts/camera_aidl_test.cpp
index 75ad532..2bdd5e8 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.cpp
+++ b/camera/provider/aidl/vts/camera_aidl_test.cpp
@@ -2296,14 +2296,10 @@
bool supportsPartialResults = false;
bool useHalBufManager = false;
int32_t partialResultCount = 0;
- configureSingleStream(name, mProvider, &streamThreshold, bufferUsage, reqTemplate,
- &session /*out*/, &testStream /*out*/, &halStreams /*out*/,
- &supportsPartialResults /*out*/, &partialResultCount /*out*/,
- &useHalBufManager /*out*/, &cb /*out*/);
-
- ASSERT_NE(session, nullptr);
- ASSERT_NE(cb, nullptr);
- ASSERT_FALSE(halStreams.empty());
+ ASSERT_NO_FATAL_FAILURE(configureSingleStream(
+ name, mProvider, &streamThreshold, bufferUsage, reqTemplate, &session /*out*/,
+ &testStream /*out*/, &halStreams /*out*/, &supportsPartialResults /*out*/,
+ &partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/));
std::shared_ptr<ResultMetadataQueue> resultQueue;
::aidl::android::hardware::common::fmq::MQDescriptor<
@@ -2718,37 +2714,37 @@
config.streams = streams;
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE, &config,
jpegBufferSize);
- if (*session != nullptr) {
- CameraMetadata sessionParams;
- ret = (*session)->constructDefaultRequestSettings(reqTemplate, &sessionParams);
- ASSERT_TRUE(ret.isOk());
- config.sessionParams = sessionParams;
- config.streamConfigCounter = (int32_t)streamConfigCounter;
- bool supported = false;
- ret = device->isStreamCombinationSupported(config, &supported);
- ASSERT_TRUE(ret.isOk());
- ASSERT_EQ(supported, true);
+ CameraMetadata sessionParams;
+ ret = (*session)->constructDefaultRequestSettings(reqTemplate, &sessionParams);
+ ASSERT_TRUE(ret.isOk());
+ config.sessionParams = sessionParams;
+ config.streamConfigCounter = (int32_t)streamConfigCounter;
- std::vector<HalStream> halConfigs;
- std::set<int32_t> halBufManagedStreamIds;
- ret = configureStreams(*session, config, bufferManagerType, &halBufManagedStreamIds,
- &halConfigs);
- ALOGI("configureStreams returns status: %d:%d", ret.getExceptionCode(),
- ret.getServiceSpecificError());
- ASSERT_TRUE(ret.isOk());
- ASSERT_EQ(1u, halConfigs.size());
- halStreams->clear();
- halStreams->push_back(halConfigs[0]);
- *useHalBufManager = halBufManagedStreamIds.size() != 0;
- if (*useHalBufManager) {
- std::vector<Stream> ss(1);
- std::vector<HalStream> hs(1);
- ss[0] = config.streams[0];
- hs[0] = halConfigs[0];
- (*cb)->setCurrentStreamConfig(ss, hs);
- }
+ bool supported = false;
+ ret = device->isStreamCombinationSupported(config, &supported);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(supported, true);
+
+ std::vector<HalStream> halConfigs;
+ std::set<int32_t> halBufManagedStreamIds;
+ ret = configureStreams(*session, config, bufferManagerType, &halBufManagedStreamIds,
+ &halConfigs);
+ ALOGI("configureStreams returns status: %d:%d", ret.getExceptionCode(),
+ ret.getServiceSpecificError());
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(1u, halConfigs.size());
+ halStreams->clear();
+ halStreams->push_back(halConfigs[0]);
+ *useHalBufManager = halBufManagedStreamIds.size() != 0;
+ if (*useHalBufManager) {
+ std::vector<Stream> ss(1);
+ std::vector<HalStream> hs(1);
+ ss[0] = config.streams[0];
+ hs[0] = halConfigs[0];
+ (*cb)->setCurrentStreamConfig(ss, hs);
}
+
*previewStream = config.streams[0];
ASSERT_TRUE(ret.isOk());
}
@@ -2808,10 +2804,11 @@
bool supportsPartialResults = false;
bool useHalBufManager = false;
int32_t partialResultCount = 0;
- configureSingleStream(name, mProvider, &streamThreshold, GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
- RequestTemplate::PREVIEW, &session /*out*/, &testStream /*out*/,
- &halStreams /*out*/, &supportsPartialResults /*out*/,
- &partialResultCount /*out*/, &useHalBufManager /*out*/, &cb /*out*/);
+ ASSERT_NO_FATAL_FAILURE(configureSingleStream(
+ name, mProvider, &streamThreshold, GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
+ RequestTemplate::PREVIEW, &session /*out*/, &testStream /*out*/,
+ &halStreams /*out*/, &supportsPartialResults /*out*/, &partialResultCount /*out*/,
+ &useHalBufManager /*out*/, &cb /*out*/));
::aidl::android::hardware::common::fmq::MQDescriptor<
int8_t, aidl::android::hardware::common::fmq::SynchronizedReadWrite>
@@ -3570,10 +3567,10 @@
Stream* previewStream, std::vector<HalStream>* halStreams, bool* supportsPartialResults,
int32_t* partialResultCount, bool* useHalBufManager, std::shared_ptr<DeviceCb>* cb,
uint32_t streamConfigCounter) {
- configureSingleStream(name, provider, previewThreshold, GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
- RequestTemplate::PREVIEW, session, previewStream, halStreams,
- supportsPartialResults, partialResultCount, useHalBufManager, cb,
- streamConfigCounter);
+ ASSERT_NO_FATAL_FAILURE(configureSingleStream(
+ name, provider, previewThreshold, GRALLOC1_CONSUMER_USAGE_HWCOMPOSER,
+ RequestTemplate::PREVIEW, session, previewStream, halStreams, supportsPartialResults,
+ partialResultCount, useHalBufManager, cb, streamConfigCounter));
}
Status CameraAidlTest::isOfflineSessionSupported(const camera_metadata_t* staticMeta) {
diff --git a/compatibility_matrices/compatibility_matrix.202504.xml b/compatibility_matrices/compatibility_matrix.202504.xml
index 85702e2..7600594 100644
--- a/compatibility_matrices/compatibility_matrix.202504.xml
+++ b/compatibility_matrices/compatibility_matrix.202504.xml
@@ -531,6 +531,14 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" exclusive-to="virtual-machine">
+ <name>android.hardware.security.see.authmgr</name>
+ <version>1</version>
+ <interface>
+ <name>IAuthMgrAuthorization</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="aidl" updatable-via-apex="true">
<name>android.hardware.security.secureclock</name>
<version>1</version>
diff --git a/compatibility_matrices/compatibility_matrix.202604.xml b/compatibility_matrices/compatibility_matrix.202604.xml
index 46b04c9..ee3ba44 100644
--- a/compatibility_matrices/compatibility_matrix.202604.xml
+++ b/compatibility_matrices/compatibility_matrix.202604.xml
@@ -531,6 +531,14 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" exclusive-to="virtual-machine">
+ <name>android.hardware.security.see.authmgr</name>
+ <version>1</version>
+ <interface>
+ <name>IAuthMgrAuthorization</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="aidl" updatable-via-apex="true">
<name>android.hardware.security.secureclock</name>
<version>1</version>
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
index 740bc59..c603ff8 100644
--- a/gnss/common/utils/default/Utils.cpp
+++ b/gnss/common/utils/default/Utils.cpp
@@ -147,10 +147,13 @@
return gnssData;
}
-GnssData Utils::getMockMeasurement(const bool enableCorrVecOutputs, const bool enableFullTracking) {
+namespace {
+GnssMeasurement getMockGnssMeasurement(int svid, GnssConstellationType constellationType,
+ float cN0DbHz, float basebandCN0DbHz,
+ double carrierFrequencyHz, bool enableCorrVecOutputs) {
aidl::android::hardware::gnss::GnssSignalType signalType = {
- .constellation = GnssConstellationType::GLONASS,
- .carrierFrequencyHz = 1.59975e+09,
+ .constellation = constellationType,
+ .carrierFrequencyHz = carrierFrequencyHz,
.codeType = aidl::android::hardware::gnss::GnssSignalType::CODE_TYPE_C,
};
GnssMeasurement measurement = {
@@ -161,23 +164,23 @@
GnssMeasurement::HAS_SATELLITE_ISB |
GnssMeasurement::HAS_SATELLITE_ISB_UNCERTAINTY |
GnssMeasurement::HAS_SATELLITE_PVT,
- .svid = 13,
+ .svid = svid,
.signalType = signalType,
+ .state = GnssMeasurement::STATE_CODE_LOCK | GnssMeasurement::STATE_BIT_SYNC |
+ GnssMeasurement::STATE_SUBFRAME_SYNC | GnssMeasurement::STATE_TOW_DECODED |
+ GnssMeasurement::STATE_GLO_STRING_SYNC |
+ GnssMeasurement::STATE_GLO_TOD_DECODED,
.receivedSvTimeInNs = 8195997131077,
.receivedSvTimeUncertaintyInNs = 15,
- .antennaCN0DbHz = 30.0,
- .basebandCN0DbHz = 26.5,
- .agcLevelDb = 2.3,
+ .antennaCN0DbHz = cN0DbHz,
+ .basebandCN0DbHz = basebandCN0DbHz,
.pseudorangeRateMps = -484.13739013671875,
.pseudorangeRateUncertaintyMps = 0.1037999987602233,
.accumulatedDeltaRangeState = GnssMeasurement::ADR_STATE_VALID,
.accumulatedDeltaRangeM = 1.52,
.accumulatedDeltaRangeUncertaintyM = 2.43,
.multipathIndicator = aidl::android::hardware::gnss::GnssMultipathIndicator::UNKNOWN,
- .state = GnssMeasurement::STATE_CODE_LOCK | GnssMeasurement::STATE_BIT_SYNC |
- GnssMeasurement::STATE_SUBFRAME_SYNC | GnssMeasurement::STATE_TOW_DECODED |
- GnssMeasurement::STATE_GLO_STRING_SYNC |
- GnssMeasurement::STATE_GLO_TOD_DECODED,
+ .agcLevelDb = 2.3,
.fullInterSignalBiasNs = 21.5,
.fullInterSignalBiasUncertaintyNs = 792.0,
.satelliteInterSignalBiasNs = 233.9,
@@ -199,35 +202,15 @@
.satClkDriftMps = 0},
.ionoDelayMeters = 3.069949602639317e-08,
.tropoDelayMeters = 3.882265204404031,
- .ephemerisSource =
- SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM,
.timeOfClockSeconds = 12345,
.issueOfDataClock = 143,
.timeOfEphemerisSeconds = 9876,
.issueOfDataEphemeris = 48,
+ .ephemerisSource =
+ SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM,
},
.correlationVectors = {}};
- GnssClock clock = {.gnssClockFlags = GnssClock::HAS_FULL_BIAS | GnssClock::HAS_BIAS |
- GnssClock::HAS_BIAS_UNCERTAINTY | GnssClock::HAS_DRIFT |
- GnssClock::HAS_DRIFT_UNCERTAINTY,
- .timeNs = 2713545000000,
- .fullBiasNs = -1226701900521857520,
- .biasNs = 0.59689998626708984,
- .biasUncertaintyNs = 47514.989972114563,
- .driftNsps = -51.757811607455452,
- .driftUncertaintyNsps = 310.64968328491528,
- .hwClockDiscontinuityCount = 1,
- .referenceSignalTypeForIsb = signalType};
-
- ElapsedRealtime timestamp = {
- .flags = ElapsedRealtime::HAS_TIMESTAMP_NS | ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS,
- .timestampNs = ::android::elapsedRealtimeNano(),
- // This is an hardcoded value indicating a 1ms of uncertainty between the two clocks.
- // In an actual implementation provide an estimate of the synchronization uncertainty
- // or don't set the field.
- .timeUncertaintyNs = 1020400};
-
if (enableCorrVecOutputs) {
aidl::android::hardware::gnss::CorrelationVector correlationVector1 = {
.frequencyOffsetMps = 10,
@@ -242,6 +225,68 @@
measurement.correlationVectors = {correlationVector1, correlationVector2};
measurement.flags |= GnssMeasurement::HAS_CORRELATION_VECTOR;
}
+ return measurement;
+}
+} // namespace
+
+GnssData Utils::getMockMeasurement(const bool enableCorrVecOutputs, const bool enableFullTracking) {
+ std::vector<GnssMeasurement> measurements = {
+ // GPS
+ getMockGnssMeasurement(3, GnssConstellationType::GPS, 32.5, 27.5, kGpsL1FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(5, GnssConstellationType::GPS, 27.0, 22.0, kGpsL1FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(17, GnssConstellationType::GPS, 30.5, 25.5, kGpsL5FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(26, GnssConstellationType::GPS, 24.1, 19.1, kGpsL5FreqHz,
+ enableCorrVecOutputs),
+ // GAL
+ getMockGnssMeasurement(2, GnssConstellationType::GALILEO, 33.5, 27.5, kGalE1FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(4, GnssConstellationType::GALILEO, 28.0, 22.0, kGalE1FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(10, GnssConstellationType::GALILEO, 35.5, 25.5, kGalE1FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(29, GnssConstellationType::GALILEO, 34.1, 19.1, kGalE1FreqHz,
+ enableCorrVecOutputs),
+ // GLO
+ getMockGnssMeasurement(5, GnssConstellationType::GLONASS, 20.5, 15.5, kGloG1FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(17, GnssConstellationType::GLONASS, 21.5, 16.5, kGloG1FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(18, GnssConstellationType::GLONASS, 28.3, 25.3, kGloG1FreqHz,
+ enableCorrVecOutputs),
+ getMockGnssMeasurement(10, GnssConstellationType::GLONASS, 25.0, 20.0, kGloG1FreqHz,
+ enableCorrVecOutputs),
+ // IRNSS
+ getMockGnssMeasurement(3, GnssConstellationType::IRNSS, 22.0, 19.7, kIrnssL5FreqHz,
+ enableCorrVecOutputs),
+ };
+
+ GnssClock clock = {
+ .gnssClockFlags = GnssClock::HAS_FULL_BIAS | GnssClock::HAS_BIAS |
+ GnssClock::HAS_BIAS_UNCERTAINTY | GnssClock::HAS_DRIFT |
+ GnssClock::HAS_DRIFT_UNCERTAINTY,
+ .timeNs = 2713545000000,
+ .fullBiasNs = -1226701900521857520,
+ .biasNs = 0.59689998626708984,
+ .biasUncertaintyNs = 47514.989972114563,
+ .driftNsps = -51.757811607455452,
+ .driftUncertaintyNsps = 310.64968328491528,
+ .hwClockDiscontinuityCount = 1,
+ .referenceSignalTypeForIsb = {
+ .constellation = GnssConstellationType::GLONASS,
+ .carrierFrequencyHz = 1.59975e+09,
+ .codeType = aidl::android::hardware::gnss::GnssSignalType::CODE_TYPE_C,
+ }};
+
+ ElapsedRealtime timestamp = {
+ .flags = ElapsedRealtime::HAS_TIMESTAMP_NS | ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS,
+ .timestampNs = ::android::elapsedRealtimeNano(),
+ // This is an hardcoded value indicating a 1ms of uncertainty between the two clocks.
+ // In an actual implementation provide an estimate of the synchronization uncertainty
+ // or don't set the field.
+ .timeUncertaintyNs = 1020400};
GnssAgc gnssAgc1 = {
.agcLevelDb = 3.5,
@@ -255,7 +300,7 @@
.carrierFrequencyHz = (int64_t)kGpsL1FreqHz,
};
- GnssData gnssData = {.measurements = {measurement},
+ GnssData gnssData = {.measurements = measurements,
.clock = clock,
.elapsedRealtime = timestamp,
.gnssAgcs = std::vector({gnssAgc1, gnssAgc2}),
diff --git a/graphics/composer/aidl/libhwc_aidl_test/ComposerClientWrapper.cpp b/graphics/composer/aidl/libhwc_aidl_test/ComposerClientWrapper.cpp
index 2252ce3..bfc4d2d 100644
--- a/graphics/composer/aidl/libhwc_aidl_test/ComposerClientWrapper.cpp
+++ b/graphics/composer/aidl/libhwc_aidl_test/ComposerClientWrapper.cpp
@@ -458,6 +458,11 @@
return mComposerCallback->takeListOfRefreshRateChangedDebugData();
}
+std::vector<std::pair<int64_t, common::DisplayHotplugEvent>>
+ComposerClientWrapper::getAndClearLatestHotplugs() {
+ return mComposerCallback->getAndClearLatestHotplugs();
+}
+
int64_t ComposerClientWrapper::getInvalidDisplayId() {
// returns an invalid display id (one that has not been registered to a
// display. Currently assuming that a device will never have close to
diff --git a/graphics/composer/aidl/libhwc_aidl_test/GraphicsComposerCallback.cpp b/graphics/composer/aidl/libhwc_aidl_test/GraphicsComposerCallback.cpp
index ba16348..0daff56 100644
--- a/graphics/composer/aidl/libhwc_aidl_test/GraphicsComposerCallback.cpp
+++ b/graphics/composer/aidl/libhwc_aidl_test/GraphicsComposerCallback.cpp
@@ -100,6 +100,16 @@
return mInvalidRefreshRateDebugEnabledCallbackCount;
}
+std::vector<std::pair<int64_t, common::DisplayHotplugEvent>>
+GraphicsComposerCallback::getAndClearLatestHotplugs() {
+ std::vector<std::pair<int64_t, common::DisplayHotplugEvent>> ret;
+ {
+ std::scoped_lock lock(mMutex);
+ ret.swap(mLatestHotplugs);
+ }
+ return ret;
+}
+
::ndk::ScopedAStatus GraphicsComposerCallback::onHotplug(int64_t in_display, bool in_connected) {
std::scoped_lock lock(mMutex);
@@ -196,6 +206,11 @@
::ndk::ScopedAStatus GraphicsComposerCallback::onHotplugEvent(int64_t in_display,
common::DisplayHotplugEvent event) {
+ {
+ std::scoped_lock lock(mMutex);
+ mLatestHotplugs.emplace_back(in_display, event);
+ }
+
switch (event) {
case common::DisplayHotplugEvent::CONNECTED:
return onHotplug(in_display, true);
diff --git a/graphics/composer/aidl/libhwc_aidl_test/include/ComposerClientWrapper.h b/graphics/composer/aidl/libhwc_aidl_test/include/ComposerClientWrapper.h
index 5ba52bc..3d805c2 100644
--- a/graphics/composer/aidl/libhwc_aidl_test/include/ComposerClientWrapper.h
+++ b/graphics/composer/aidl/libhwc_aidl_test/include/ComposerClientWrapper.h
@@ -203,6 +203,8 @@
std::pair<ScopedAStatus, std::vector<Luts>> getLuts(int64_t display,
const std::vector<Buffer>& buffers);
+ std::vector<std::pair<int64_t, common::DisplayHotplugEvent>> getAndClearLatestHotplugs();
+
static constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20fps
static constexpr int32_t kNoFrameIntervalNs = 0;
diff --git a/graphics/composer/aidl/libhwc_aidl_test/include/GraphicsComposerCallback.h b/graphics/composer/aidl/libhwc_aidl_test/include/GraphicsComposerCallback.h
index ff379b7..c769677 100644
--- a/graphics/composer/aidl/libhwc_aidl_test/include/GraphicsComposerCallback.h
+++ b/graphics/composer/aidl/libhwc_aidl_test/include/GraphicsComposerCallback.h
@@ -50,6 +50,8 @@
int32_t getInvalidRefreshRateDebugEnabledCallbackCount() const;
+ std::vector<std::pair<int64_t, common::DisplayHotplugEvent>> getAndClearLatestHotplugs();
+
private:
virtual ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override;
virtual ::ndk::ScopedAStatus onRefresh(int64_t in_display) override;
@@ -71,6 +73,8 @@
mutable std::mutex mMutex;
// the set of all currently connected displays
std::vector<int64_t> mDisplays GUARDED_BY(mMutex);
+ std::vector<std::pair<int64_t /*display id*/, common::DisplayHotplugEvent>> mLatestHotplugs
+ GUARDED_BY(mMutex);
// true only when vsync is enabled
bool mVsyncAllowed GUARDED_BY(mMutex) = true;
// true only when RefreshRateChangedCallbackDebugEnabled is set to true.
diff --git a/radio/aidl/minradio/libminradio/Android.bp b/radio/aidl/minradio/libminradio/Android.bp
new file mode 100644
index 0000000..169df5e
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/Android.bp
@@ -0,0 +1,84 @@
+// Copyright (C) 2024 The Android Open Source 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_defaults {
+ name: "android.hardware.radio-minradio@defaults",
+ relative_install_path: "hw",
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-g",
+
+ // binder_to_string.h uses deprecated codecvt_utf8_utf16.
+ // We can't fix it in foreesable future.
+ "-D_LIBCPP_DISABLE_DEPRECATION_WARNINGS",
+ ],
+ shared_libs: [
+ "android.hardware.radio.config-V4-ndk",
+ "android.hardware.radio.data-V4-ndk",
+ "android.hardware.radio.modem-V4-ndk",
+ "android.hardware.radio.network-V4-ndk",
+ "android.hardware.radio.sim-V4-ndk",
+ "libbase",
+ "libbinder_ndk",
+ "libutils",
+ ],
+ sanitize: {
+ address: true,
+ all_undefined: true,
+ fuzzer: true,
+ integer_overflow: true,
+ },
+ strip: {
+ none: true,
+ },
+}
+
+cc_library {
+ name: "android.hardware.radio-library.minradio",
+ defaults: ["android.hardware.radio-minradio@defaults"],
+ export_include_dirs: ["include"],
+ srcs: [
+ "RadioSlotBase.cpp",
+ "ResponseTracker.cpp",
+ "SlotContext.cpp",
+ "config/RadioConfig.cpp",
+ "data/RadioData.cpp",
+ "modem/RadioModem.cpp",
+ "network/RadioNetwork.cpp",
+ "network/RadioNetworkResponseTracker.cpp",
+ "network/structs.cpp",
+ "response.cpp",
+ "sim/apps/AraM.cpp",
+ "sim/apps/FilesystemApp.cpp",
+ "sim/apps/tlv.cpp",
+ "sim/App.cpp",
+ "sim/AppManager.cpp",
+ "sim/Filesystem.cpp",
+ "sim/IccUtils.cpp",
+ "sim/RadioSim.cpp",
+ ],
+}
diff --git a/radio/aidl/minradio/libminradio/RadioSlotBase.cpp b/radio/aidl/minradio/libminradio/RadioSlotBase.cpp
new file mode 100644
index 0000000..0f4bd68
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/RadioSlotBase.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/RadioSlotBase.h>
+
+namespace android::hardware::radio::minimal {
+
+RadioSlotBase::RadioSlotBase(std::shared_ptr<SlotContext> context) : mContext(context) {}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/ResponseTracker.cpp b/radio/aidl/minradio/libminradio/ResponseTracker.cpp
new file mode 100644
index 0000000..20b8522
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/ResponseTracker.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/ResponseTracker.h>
+
+#include <libminradio/debug.h>
+
+#include <random>
+
+namespace android::hardware::radio::minimal {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::RadioError;
+using ::aidl::android::hardware::radio::RadioResponseInfo;
+using ::ndk::ScopedAStatus;
+
+RadioError ResponseTrackerResultBase::toError(const ScopedAStatus& status) {
+ CHECK(!status.isOk()) << "statusToError called with no error";
+ return RadioError::GENERIC_FAILURE;
+}
+
+ResponseTrackerResultBase::ResponseTrackerResultBase(const char* descriptor)
+ : ResponseTrackerResultBase(descriptor, RadioError::RADIO_NOT_AVAILABLE) {}
+
+ResponseTrackerResultBase::ResponseTrackerResultBase(const char* descriptor, RadioError error)
+ : mDescriptor(descriptor), mError(error) {}
+
+ResponseTrackerResultBase::ResponseTrackerResultBase(const char* descriptor, ScopedAStatus st)
+ : ResponseTrackerResultBase(descriptor, toError(st)) {}
+
+bool ResponseTrackerResultBase::isOk() const {
+ return mError == RadioError::NONE;
+}
+
+bool ResponseTrackerResultBase::expectOk() const {
+ if (isOk()) return true;
+ LOG(ERROR) << "Request for " << mDescriptor << " failed: " << mError;
+ return false;
+}
+
+RadioError ResponseTrackerResultBase::getError() const {
+ return mError;
+}
+
+const char* ResponseTrackerResultBase::getDescriptor() const {
+ return mDescriptor;
+}
+
+ResponseTrackerBase::ScopedSerial::ScopedSerial(int32_t serial, ResponseTrackerBase* tracker)
+ : mSerial(serial), mTracker(tracker) {}
+
+ResponseTrackerBase::ScopedSerial::~ScopedSerial() {
+ if (mIsReleased) return;
+ mTracker->cancelTracking(*this);
+}
+
+ResponseTrackerBase::ScopedSerial::operator int32_t() const {
+ CHECK(!mIsReleased) << "ScopedSerial " << mSerial << " is not valid anymore";
+ return mSerial;
+}
+
+void ResponseTrackerBase::ScopedSerial::release() {
+ mIsReleased = true;
+}
+
+int32_t ResponseTrackerBase::initialSerial() {
+ /* Android framework tends to start request serial numbers from 0, so let's pick something from
+ * the second quarter of int32_t negative range. This way the chance of having a conflict is
+ * closer to zero. */
+ static const int32_t rangeSize = std::abs(std::numeric_limits<int32_t>::min() / 4);
+ static const int32_t rangeStart = std::numeric_limits<int32_t>::min() + rangeSize;
+
+ static std::random_device generator;
+ static std::uniform_int_distribution<int32_t> distribution(rangeStart, rangeStart + rangeSize);
+
+ return distribution(generator);
+}
+
+ResponseTrackerBase::ScopedSerial ResponseTrackerBase::newSerial() {
+ std::unique_lock lck(mSerialsGuard);
+
+ auto serial = mSerial++;
+ if (serial == 0) [[unlikely]] {
+ serial = mSerial++;
+ }
+ if constexpr (debug::kSuperCrazyVerbose) {
+ LOG(VERBOSE) << "Tracking " << serial << " internally";
+ }
+
+ auto inserted = mTrackedSerials.emplace(serial, nullptr).second;
+ CHECK(inserted) << "Detected tracked serials conflict at " << serial;
+
+ return {serial, this};
+}
+
+bool ResponseTrackerBase::isTracked(int32_t serial) const {
+ std::unique_lock lck(mSerialsGuard);
+ return mTrackedSerials.contains(serial);
+}
+
+void ResponseTrackerBase::cancelTracking(ResponseTrackerBase::ScopedSerial& serial) {
+ std::unique_lock lck(mSerialsGuard);
+ auto erased = mTrackedSerials.erase(serial);
+ CHECK(erased == 1) << "Couldn't cancel tracking " << serial;
+ LOG(VERBOSE) << "Cancelled tracking " << serial << " internally";
+ serial.release();
+}
+
+ScopedAStatus ResponseTrackerBase::handle(const RadioResponseInfo& info,
+ std::unique_ptr<ResponseTrackerResultBase> result) {
+ std::unique_lock lck(mSerialsGuard);
+ if constexpr (debug::kSuperCrazyVerbose) {
+ LOG(VERBOSE) << "Handling " << info.serial << " internally (not sending to the framework)";
+ }
+
+ auto it = mTrackedSerials.find(info.serial);
+ CHECK(it != mTrackedSerials.end()) << "Request not tracked: " << info;
+ CHECK(it->second == nullptr) << "Request already handled: " << info;
+ it->second = std::move(result);
+
+ return ScopedAStatus::ok();
+}
+
+std::unique_ptr<ResponseTrackerResultBase> ResponseTrackerBase::getResultBase(
+ ResponseTrackerBase::ScopedSerial& serial) {
+ std::unique_lock lck(mSerialsGuard);
+ auto node = mTrackedSerials.extract(serial);
+ CHECK(node.key()) << "Request " << serial << " is not tracked";
+ if (!node.mapped()) {
+ LOG(WARNING) << "Didn't get result for " << serial
+ << ". It may either mean setResponseFunctions has reset the callbacks or"
+ " the callback wasn't called synchronously from the scope of "
+ "request method implementation.";
+ serial.release();
+ return nullptr;
+ }
+ if constexpr (debug::kSuperCrazyVerbose) {
+ LOG(VERBOSE) << "Finished tracking " << serial << " internally";
+ }
+ serial.release();
+ return std::move(node.mapped());
+}
+
+// This symbol silences "Mismatched versions of delegator and implementation" errors from Delegator
+// implementation. In this specific case, Delegators are used to encapsulate incoming callbacks, not
+// outgoing interfaces - so clamping delegator interface version to lower than implementation's
+// version wouldn't make any difference - the local binary wouldn't know what to do with a newer
+// interface anyways. This happens when Radio HAL (which includes callback interfaces) defined on
+// system partition is newer than one used to build local binary (usually on vendor partition).
+extern "C" void assert2_no_op(const char*, int, const char*, const char*) {}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/SlotContext.cpp b/radio/aidl/minradio/libminradio/SlotContext.cpp
new file mode 100644
index 0000000..cffc178
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/SlotContext.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/SlotContext.h>
+
+namespace android::hardware::radio::minimal {
+
+SlotContext::SlotContext(unsigned slotIndex) : mSlotIndex(slotIndex) {}
+
+unsigned SlotContext::getSlotIndex() const {
+ return mSlotIndex;
+}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/config/RadioConfig.cpp b/radio/aidl/minradio/libminradio/config/RadioConfig.cpp
new file mode 100644
index 0000000..bf89368
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/config/RadioConfig.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/config/RadioConfig.h>
+
+#include <libminradio/debug.h>
+#include <libminradio/response.h>
+
+#define RADIO_MODULE "Config"
+
+namespace android::hardware::radio::minimal {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::RadioError;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::config;
+constexpr auto ok = &ScopedAStatus::ok;
+
+RadioConfig::RadioConfig() {}
+
+ScopedAStatus RadioConfig::getHalDeviceCapabilities(int32_t serial) {
+ LOG_CALL;
+ /* modemReducedFeatureSet1 disables:
+ * - android.hardware.radio.network.LinkCapacityEstimate.secondaryDownlinkCapacityKbps
+ * - android.hardware.radio.network.LinkCapacityEstimate.secondaryUplinkCapacityKbps
+ * - android.hardware.radio.network.IRadioNetwork.setNrDualConnectivityState
+ * - android.hardware.radio.network.IRadioNetwork.isNrDualConnectivityEnabled
+ * - android.hardware.radio.data.IRadioData.setDataThrottling
+ * - android.hardware.radio.data.IRadioData.getSlicingConfig
+ * - android.hardware.radio.network.IRadioNetworkIndication.currentPhysicalChannelConfigs
+ */
+ respond()->getHalDeviceCapabilitiesResponse(noError(serial), /*modemReducedFeatureSet1*/ true);
+ return ok();
+}
+
+ScopedAStatus RadioConfig::getNumOfLiveModems(int32_t serial) {
+ LOG_CALL;
+ respond()->getNumOfLiveModemsResponse(noError(serial), 1);
+ return ok();
+}
+
+ScopedAStatus RadioConfig::getPhoneCapability(int32_t serial) {
+ LOG_CALL;
+ aidl::PhoneCapability cap{
+ .maxActiveData = 1,
+ .maxActiveInternetData = 1,
+ .isInternetLingeringSupported = false,
+ .logicalModemIds = {0},
+ };
+ respond()->getPhoneCapabilityResponse(noError(serial), cap);
+ return ok();
+}
+
+ScopedAStatus RadioConfig::setNumOfLiveModems(int32_t serial, int8_t numOfLiveModems) {
+ LOG_CALL << numOfLiveModems;
+ if (numOfLiveModems == 1) {
+ respond()->setNumOfLiveModemsResponse(noError(serial));
+ } else {
+ respond()->setNumOfLiveModemsResponse(errorResponse(serial, RadioError::INVALID_ARGUMENTS));
+ }
+ return ok();
+}
+
+ScopedAStatus RadioConfig::setPreferredDataModem(int32_t serial, int8_t modemId) {
+ LOG_CALL_IGNORED << modemId;
+ respond()->setPreferredDataModemResponse(
+ (modemId == 0) ? noError(serial)
+ : errorResponse(serial, RadioError::INVALID_ARGUMENTS));
+ return ok();
+}
+
+ScopedAStatus RadioConfig::setResponseFunctions(
+ const std::shared_ptr<aidl::IRadioConfigResponse>& response,
+ const std::shared_ptr<aidl::IRadioConfigIndication>& indication) {
+ LOG_CALL_NOSERIAL << response << ' ' << indication;
+ CHECK(response);
+ CHECK(indication);
+ respond = response;
+ indicate = indication;
+ return ok();
+}
+
+ScopedAStatus RadioConfig::setSimSlotsMapping( //
+ int32_t serial, const std::vector<aidl::SlotPortMapping>& slotMap) {
+ LOG_CALL_IGNORED << slotMap;
+ respond()->setSimSlotsMappingResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioConfig::getSimultaneousCallingSupport(int32_t serial) {
+ LOG_CALL;
+ respond()->getSimultaneousCallingSupportResponse(noError(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioConfig::getSimTypeInfo(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus RadioConfig::setSimType(int32_t serial, const std::vector<aidl::SimType>& simTypes) {
+ LOG_NOT_SUPPORTED << simTypes;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/data/RadioData.cpp b/radio/aidl/minradio/libminradio/data/RadioData.cpp
new file mode 100644
index 0000000..a096c82
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/data/RadioData.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2023 The Android Open Source 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.
+ */
+
+#include <libminradio/data/RadioData.h>
+
+#include <libminradio/debug.h>
+#include <libminradio/response.h>
+
+#include <ranges>
+
+#define RADIO_MODULE "Data"
+
+namespace android::hardware::radio::minimal {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::RadioIndicationType;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::data;
+constexpr auto ok = &ScopedAStatus::ok;
+
+int32_t RadioData::setupDataCallCid() {
+ return ++mLastDataCallCid;
+}
+
+void RadioData::setupDataCallBase(aidl::SetupDataCallResult dataCall) {
+ {
+ const std::lock_guard<std::mutex> lock(mDataCallListGuard);
+ mDataCallList[dataCall.cid] = dataCall;
+ }
+ indicate()->dataCallListChanged(RadioIndicationType::UNSOLICITED, getDataCallListBase());
+}
+
+void RadioData::deactivateDataCallBase(int32_t cid) {
+ {
+ const std::lock_guard<std::mutex> lock(mDataCallListGuard);
+ auto it = mDataCallList.find(cid);
+ if (it == mDataCallList.end()) return;
+
+ mDataCallList.erase(it);
+ }
+ indicate()->dataCallListChanged(RadioIndicationType::UNSOLICITED, getDataCallListBase());
+}
+
+std::vector<aidl::SetupDataCallResult> RadioData::getDataCallListBase() const {
+ const std::lock_guard<std::mutex> lock(mDataCallListGuard);
+ auto dataCalls = std::views::values(mDataCallList);
+ return {dataCalls.begin(), dataCalls.end()};
+}
+
+ScopedAStatus RadioData::allocatePduSessionId(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus RadioData::cancelHandover(int32_t serial, int32_t callId) {
+ LOG_NOT_SUPPORTED << callId;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus RadioData::deactivateDataCall(int32_t serial, int32_t cid,
+ aidl::DataRequestReason reason) {
+ LOG_CALL_IGNORED << cid << " " << reason;
+ deactivateDataCallBase(cid);
+ respond()->deactivateDataCallResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioData::getDataCallList(int32_t serial) {
+ LOG_CALL;
+ respond()->getDataCallListResponse(noError(serial), getDataCallListBase());
+ return ok();
+}
+
+ScopedAStatus RadioData::getSlicingConfig(int32_t serial) {
+ // Disabled with modemReducedFeatureSet1.
+ LOG_NOT_SUPPORTED;
+ respond()->getSlicingConfigResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioData::releasePduSessionId(int32_t serial, int32_t id) {
+ LOG_NOT_SUPPORTED << id;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus RadioData::responseAcknowledgement() {
+ LOG_CALL_NOSERIAL;
+ return ok();
+}
+
+ScopedAStatus RadioData::setDataAllowed(int32_t serial, bool allow) {
+ LOG_NOT_SUPPORTED << allow;
+ respond()->setDataAllowedResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioData::setDataProfile(int32_t serial,
+ const std::vector<aidl::DataProfileInfo>& profiles) {
+ LOG_CALL_IGNORED << profiles;
+ respond()->setDataProfileResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioData::setDataThrottling(int32_t serial, aidl::DataThrottlingAction dta,
+ int64_t completionDurationMs) {
+ // Disabled with modemReducedFeatureSet1.
+ LOG_NOT_SUPPORTED << dta << ' ' << completionDurationMs;
+ respond()->setDataThrottlingResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioData::setInitialAttachApn(int32_t serial,
+ const std::optional<aidl::DataProfileInfo>& info) {
+ LOG_CALL_IGNORED << info;
+ respond()->setInitialAttachApnResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioData::setResponseFunctions(
+ const std::shared_ptr<aidl::IRadioDataResponse>& response,
+ const std::shared_ptr<aidl::IRadioDataIndication>& indication) {
+ LOG_CALL_NOSERIAL << response << ' ' << indication;
+ CHECK(response);
+ CHECK(indication);
+ respond = response;
+ indicate = indication;
+ return ok();
+}
+
+ScopedAStatus RadioData::startHandover(int32_t serial, int32_t callId) {
+ LOG_NOT_SUPPORTED << callId;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus RadioData::startKeepalive(int32_t serial, const aidl::KeepaliveRequest& keepalive) {
+ LOG_NOT_SUPPORTED << keepalive;
+ respond()->startKeepaliveResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioData::stopKeepalive(int32_t serial, int32_t sessionHandle) {
+ LOG_NOT_SUPPORTED << sessionHandle;
+ respond()->stopKeepaliveResponse(notSupported(serial));
+ return ok();
+}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/GuaranteedCallback.h b/radio/aidl/minradio/libminradio/include/libminradio/GuaranteedCallback.h
new file mode 100644
index 0000000..6c5d5eb
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/GuaranteedCallback.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source 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.
+ */
+#pragma once
+
+#include <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <utils/Mutex.h>
+
+namespace android::hardware::radio::minimal {
+
+template <typename Interface, typename DefaultImplementation, bool isIndication = false>
+class GuaranteedCallback {
+ mutable std::mutex mCallbackGuard;
+ std::shared_ptr<Interface> mCallback GUARDED_BY(mCallbackGuard);
+
+ public:
+ GuaranteedCallback<Interface, DefaultImplementation, isIndication>& operator=(
+ const std::shared_ptr<Interface>& callback) {
+ CHECK(callback);
+ const std::lock_guard<std::mutex> lock(mCallbackGuard);
+ mCallback = callback;
+ return *this;
+ }
+
+ std::shared_ptr<Interface> operator()() {
+ const std::lock_guard<std::mutex> lock(mCallbackGuard);
+ if (mCallback) return mCallback;
+
+ LOG(isIndication ? WARNING : ERROR) << "Callback is not set for " << Interface::descriptor;
+ mCallback = ndk::SharedRefBase::make<DefaultImplementation>();
+ return mCallback;
+ }
+
+ operator bool() const { return mCallback != nullptr; }
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/RadioSlotBase.h b/radio/aidl/minradio/libminradio/include/libminradio/RadioSlotBase.h
new file mode 100644
index 0000000..d46357e
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/RadioSlotBase.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/SlotContext.h>
+
+#include <memory>
+
+namespace android::hardware::radio::minimal {
+
+class RadioSlotBase {
+ protected:
+ std::shared_ptr<SlotContext> mContext;
+
+ public:
+ RadioSlotBase(std::shared_ptr<SlotContext> context);
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/ResponseTracker.h b/radio/aidl/minradio/libminradio/include/libminradio/ResponseTracker.h
new file mode 100644
index 0000000..978d64c
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/ResponseTracker.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/radio/RadioError.h>
+#include <aidl/android/hardware/radio/RadioResponseInfo.h>
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_auto_utils.h>
+#include <libminradio/binder_printing.h>
+
+#include <map>
+#include <memory>
+
+namespace android::hardware::radio::minimal {
+
+class ResponseTrackerResultBase {
+ private:
+ const char* mDescriptor;
+ ::aidl::android::hardware::radio::RadioError mError;
+
+ static ::aidl::android::hardware::radio::RadioError toError(const ::ndk::ScopedAStatus& status);
+
+ protected:
+ ResponseTrackerResultBase(const char* descriptor);
+ ResponseTrackerResultBase(const char* descriptor,
+ ::aidl::android::hardware::radio::RadioError error);
+ ResponseTrackerResultBase(const char* descriptor, ::ndk::ScopedAStatus st);
+
+ public:
+ virtual ~ResponseTrackerResultBase() = default;
+
+ bool isOk() const;
+ bool expectOk() const;
+ ::aidl::android::hardware::radio::RadioError getError() const;
+ const char* getDescriptor() const;
+};
+
+template <typename ResultData>
+class ResponseTrackerResult : public ResponseTrackerResultBase {
+ private:
+ ResultData mResultData;
+
+ public:
+ ResponseTrackerResult() : ResponseTrackerResultBase(ResultData::descriptor) {}
+ ResponseTrackerResult(::aidl::android::hardware::radio::RadioError error)
+ : ResponseTrackerResultBase(ResultData::descriptor, error) {}
+ ResponseTrackerResult(::ndk::ScopedAStatus st)
+ : ResponseTrackerResultBase(ResultData::descriptor, std::move(st)) {}
+ ResponseTrackerResult(ResultData data)
+ : ResponseTrackerResultBase(ResultData::descriptor,
+ ::aidl::android::hardware::radio::RadioError::NONE),
+ mResultData(data) {}
+
+ const ResultData& get() const {
+ CHECK(expectOk()) << "Request failed";
+ return mResultData;
+ }
+ const ResultData& operator*() const { return get(); }
+ const ResultData* operator->() const { return &get(); }
+};
+
+template <typename ResultData>
+std::ostream& operator<<(std::ostream& os, const ResponseTrackerResult<ResultData>& val) {
+ using namespace ::android::hardware::radio::minimal::binder_printing;
+ if (val.isOk()) {
+ return os << *val;
+ } else {
+ return os << "ResponseTrackerResult<" << val.getDescriptor() //
+ << ">{error=" << val.getError() << "}";
+ }
+}
+
+class ResponseTrackerBase {
+ protected:
+ class ScopedSerial;
+
+ private:
+ mutable std::mutex mSerialsGuard;
+ int32_t mSerial GUARDED_BY(mSerialsGuard) = initialSerial();
+ std::map<int32_t, std::unique_ptr<ResponseTrackerResultBase>> mTrackedSerials
+ GUARDED_BY(mSerialsGuard);
+
+ static int32_t initialSerial();
+ ::ndk::ScopedAStatus handle(const ::aidl::android::hardware::radio::RadioResponseInfo& info,
+ std::unique_ptr<ResponseTrackerResultBase> result);
+ std::unique_ptr<ResponseTrackerResultBase> getResultBase(ScopedSerial& serial);
+
+ protected:
+ class ScopedSerial {
+ private:
+ int32_t mSerial;
+ bool mIsReleased = false;
+
+ /* Raw pointer to allow ResponseTrackerBase self-reference. DISALLOW_COPY_AND_ASSIGN and
+ * protected status of newSerial ensures ScopedSerial won't outlive mTracker. */
+ ResponseTrackerBase* mTracker;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSerial);
+
+ public:
+ ScopedSerial(int32_t serial, ResponseTrackerBase* tracker);
+ ~ScopedSerial();
+ operator int32_t() const;
+ void release();
+ };
+
+ ScopedSerial newSerial();
+ bool isTracked(int32_t serial) const;
+ void cancelTracking(ScopedSerial& serial);
+
+ template <typename ResultData>
+ ::ndk::ScopedAStatus handle(const ::aidl::android::hardware::radio::RadioResponseInfo& info,
+ const ResultData& data) {
+ std::unique_ptr<ResponseTrackerResultBase> result =
+ std::make_unique<ResponseTrackerResult<ResultData>>(data);
+ return handle(info, std::move(result));
+ }
+
+ template <typename ResultData>
+ ResponseTrackerResult<ResultData> getResult(ScopedSerial& serial) {
+ auto baseResult = getResultBase(serial);
+ if (!baseResult) return {};
+ CHECK(baseResult->getDescriptor() == ResultData::descriptor)
+ << "Failed to get ResponseTracker result. Expected " << ResultData::descriptor
+ << ", but got " << baseResult->getDescriptor();
+ return static_cast<ResponseTrackerResult<ResultData>&>(*baseResult);
+ }
+};
+
+template <typename RequestInterface, typename ResponseInterface>
+class ResponseTracker : public ResponseInterface::DefaultDelegator, protected ResponseTrackerBase {
+ private:
+ std::weak_ptr<RequestInterface> mRequest;
+
+ protected:
+ std::shared_ptr<RequestInterface> request() {
+ auto req = mRequest.lock();
+ CHECK(req) << "request() should only be called from RequestInterface context! "
+ << "Failing this check means RequestInterface has been free'd.";
+ return req;
+ }
+
+ public:
+ ResponseTracker(std::shared_ptr<RequestInterface> req,
+ const std::shared_ptr<ResponseInterface>& resp)
+ : ResponseInterface::DefaultDelegator(resp), mRequest(req) {}
+};
+
+template <typename ResponseTrackerT>
+class ResponseTrackerHolder {
+ private:
+ mutable std::mutex mResponseTrackerGuard;
+ std::shared_ptr<ResponseTrackerT> mTracker GUARDED_BY(mResponseTrackerGuard);
+
+ public:
+ operator bool() const {
+ std::unique_lock lck(mResponseTrackerGuard);
+ return mTracker != nullptr;
+ }
+
+ ResponseTrackerHolder& operator=(std::shared_ptr<ResponseTrackerT> tracker) {
+ std::unique_lock lck(mResponseTrackerGuard);
+ mTracker = std::move(tracker);
+ return *this;
+ }
+
+ std::shared_ptr<ResponseTrackerT> operator()() const {
+ std::unique_lock lck(mResponseTrackerGuard);
+ return mTracker;
+ }
+
+ std::shared_ptr<ResponseTrackerT> get() const {
+ std::unique_lock lck(mResponseTrackerGuard);
+ return mTracker;
+ }
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/SlotContext.h b/radio/aidl/minradio/libminradio/include/libminradio/SlotContext.h
new file mode 100644
index 0000000..bc6f61e
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/SlotContext.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+namespace android::hardware::radio::minimal {
+
+class SlotContext {
+ public:
+ SlotContext(unsigned slotIndex);
+
+ unsigned getSlotIndex() const;
+
+ private:
+ unsigned mSlotIndex;
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/binder_printing.h b/radio/aidl/minradio/libminradio/include/libminradio/binder_printing.h
new file mode 100644
index 0000000..c583b3b
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/binder_printing.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <ostream>
+
+namespace android::hardware::radio::minimal::binder_printing {
+
+namespace details {
+
+template <typename _T>
+class LooksLikeBinderStruct {
+ template <typename _U>
+ static auto _test(int) -> decltype(std::declval<_U>().writeToParcel(nullptr), std::true_type());
+ template <typename _U>
+ static std::false_type _test(...);
+
+ public:
+ enum { value = decltype(_test<_T>(0))::value };
+};
+
+template <typename _T>
+class HasToStringMethod {
+ template <typename _U>
+ static auto _test(int) -> decltype(std::declval<_U>().toString(), std::true_type());
+ template <typename _U>
+ static std::false_type _test(...);
+
+ public:
+ enum { value = decltype(_test<_T>(0))::value };
+};
+
+template <typename _T>
+class HasToStringFunction {
+ template <typename _U>
+ static auto _test(int) -> decltype(toString(std::declval<_U>()), std::true_type());
+ template <typename _U>
+ static std::false_type _test(...);
+
+ public:
+ enum { value = decltype(_test<_T>(0))::value };
+};
+
+} // namespace details
+
+template <typename T, typename = std::enable_if_t<details::LooksLikeBinderStruct<T>::value &&
+ details::HasToStringMethod<T>::value>>
+std::ostream& operator<<(std::ostream& os, const T& val) {
+ return os << val.toString();
+}
+
+template <typename T, typename = std::enable_if_t<details::LooksLikeBinderStruct<T>::value &&
+ details::HasToStringMethod<T>::value>>
+std::ostream& operator<<(std::ostream& os, const std::optional<T>& val) {
+ if (!val.has_value()) return os << "nullopt";
+ return os << *val;
+}
+
+template <typename T,
+ typename = std::enable_if_t<std::is_enum<T>::value &&
+ details::HasToStringFunction<T>::value>,
+ typename = void>
+std::ostream& operator<<(std::ostream& os, T val) {
+ return os << toString(val);
+}
+
+template <typename T, typename = std::enable_if_t<
+ (details::LooksLikeBinderStruct<T>::value &&
+ details::HasToStringMethod<T>::value) ||
+ (std::is_enum<T>::value && details::HasToStringFunction<T>::value) ||
+ std::is_same_v<T, int32_t> || std::is_same_v<T, std::string>>>
+std::ostream& operator<<(std::ostream& os, const std::vector<T>& val) {
+ os << '[';
+ bool first = true;
+ for (auto&& el : val) {
+ if (first) {
+ first = false;
+ } else {
+ os << ", ";
+ }
+ os << el;
+ }
+ return os << ']';
+}
+
+} // namespace android::hardware::radio::minimal::binder_printing
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/config/RadioConfig.h b/radio/aidl/minradio/libminradio/include/libminradio/config/RadioConfig.h
new file mode 100644
index 0000000..16f0ca2
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/config/RadioConfig.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/GuaranteedCallback.h>
+
+#include <aidl/android/hardware/radio/config/BnRadioConfig.h>
+
+namespace android::hardware::radio::minimal {
+
+class RadioConfig : public aidl::android::hardware::radio::config::BnRadioConfig {
+ public:
+ RadioConfig();
+
+ protected:
+ ::ndk::ScopedAStatus getHalDeviceCapabilities(int32_t serial) override;
+ ::ndk::ScopedAStatus getNumOfLiveModems(int32_t serial) override;
+ ::ndk::ScopedAStatus getPhoneCapability(int32_t serial) override;
+ ::ndk::ScopedAStatus setNumOfLiveModems(int32_t serial, int8_t numOfLiveModems) override;
+ ::ndk::ScopedAStatus setPreferredDataModem(int32_t serial, int8_t modemId) override;
+ ::ndk::ScopedAStatus setResponseFunctions(
+ const std::shared_ptr<aidl::android::hardware::radio::config::IRadioConfigResponse>&
+ response,
+ const std::shared_ptr<aidl::android::hardware::radio::config::IRadioConfigIndication>&
+ indication) override;
+ ::ndk::ScopedAStatus setSimSlotsMapping(
+ int32_t serial,
+ const std::vector<aidl::android::hardware::radio::config::SlotPortMapping>& slotMap)
+ override;
+ ::ndk::ScopedAStatus getSimultaneousCallingSupport(int32_t serial) override;
+ ::ndk::ScopedAStatus getSimTypeInfo(int32_t serial) override;
+ ::ndk::ScopedAStatus setSimType(
+ int32_t serial,
+ const std::vector<::aidl::android::hardware::radio::config::SimType>& simTypes)
+ override;
+
+ GuaranteedCallback<::aidl::android::hardware::radio::config::IRadioConfigIndication,
+ ::aidl::android::hardware::radio::config::IRadioConfigIndicationDefault,
+ true>
+ indicate;
+ GuaranteedCallback<::aidl::android::hardware::radio::config::IRadioConfigResponse,
+ ::aidl::android::hardware::radio::config::IRadioConfigResponseDefault>
+ respond;
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/data/RadioData.h b/radio/aidl/minradio/libminradio/include/libminradio/data/RadioData.h
new file mode 100644
index 0000000..2da71a2
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/data/RadioData.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/GuaranteedCallback.h>
+#include <libminradio/RadioSlotBase.h>
+
+#include <aidl/android/hardware/radio/data/BnRadioData.h>
+
+#include <map>
+
+namespace android::hardware::radio::minimal {
+
+class RadioData : public RadioSlotBase, public aidl::android::hardware::radio::data::BnRadioData {
+ public:
+ using RadioSlotBase::RadioSlotBase;
+
+ protected:
+ int32_t setupDataCallCid();
+ void setupDataCallBase(aidl::android::hardware::radio::data::SetupDataCallResult dataCall);
+ void deactivateDataCallBase(int32_t cid);
+ std::vector<aidl::android::hardware::radio::data::SetupDataCallResult> getDataCallListBase()
+ const;
+
+ ::ndk::ScopedAStatus allocatePduSessionId(int32_t serial) override;
+ ::ndk::ScopedAStatus cancelHandover(int32_t serial, int32_t callId) override;
+ ::ndk::ScopedAStatus deactivateDataCall(
+ int32_t serial, int32_t cid,
+ ::aidl::android::hardware::radio::data::DataRequestReason reason) override;
+ ::ndk::ScopedAStatus getDataCallList(int32_t serial) override;
+ ::ndk::ScopedAStatus getSlicingConfig(int32_t serial) override;
+ ::ndk::ScopedAStatus releasePduSessionId(int32_t serial, int32_t id) override;
+ ::ndk::ScopedAStatus responseAcknowledgement() override;
+ ::ndk::ScopedAStatus setDataAllowed(int32_t serial, bool allow) override;
+ ::ndk::ScopedAStatus setDataProfile(
+ int32_t serial,
+ const std::vector<::aidl::android::hardware::radio::data::DataProfileInfo>& profiles)
+ override;
+ ::ndk::ScopedAStatus setDataThrottling(
+ int32_t serial,
+ ::aidl::android::hardware::radio::data::DataThrottlingAction dataThrottlingAction,
+ int64_t completionDurationMillis) override;
+ ::ndk::ScopedAStatus setInitialAttachApn(
+ int32_t serial,
+ const std::optional<::aidl::android::hardware::radio::data::DataProfileInfo>& dpInfo)
+ override;
+ ::ndk::ScopedAStatus setResponseFunctions(
+ const std::shared_ptr<::aidl::android::hardware::radio::data::IRadioDataResponse>&
+ radioDataResponse,
+ const std::shared_ptr<::aidl::android::hardware::radio::data::IRadioDataIndication>&
+ radioDataIndication) override;
+ ::ndk::ScopedAStatus startHandover(int32_t serial, int32_t callId) override;
+ ::ndk::ScopedAStatus startKeepalive(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::data::KeepaliveRequest& keepalive) override;
+ ::ndk::ScopedAStatus stopKeepalive(int32_t serial, int32_t sessionHandle) override;
+
+ GuaranteedCallback<::aidl::android::hardware::radio::data::IRadioDataIndication,
+ ::aidl::android::hardware::radio::data::IRadioDataIndicationDefault, true>
+ indicate;
+ GuaranteedCallback<::aidl::android::hardware::radio::data::IRadioDataResponse,
+ ::aidl::android::hardware::radio::data::IRadioDataResponseDefault>
+ respond;
+
+ private:
+ int32_t mLastDataCallCid = 0;
+ mutable std::mutex mDataCallListGuard;
+ std::map<int32_t, ::aidl::android::hardware::radio::data::SetupDataCallResult> mDataCallList
+ GUARDED_BY(mDataCallListGuard);
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/debug.h b/radio/aidl/minradio/libminradio/include/libminradio/debug.h
new file mode 100644
index 0000000..9646aca
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/debug.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source 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.
+ */
+#pragma once
+
+#include "binder_printing.h"
+
+#include <android-base/logging.h>
+
+namespace android::hardware::radio::minimal::debug {
+
+static constexpr bool kSuperVerbose = true;
+static constexpr bool kSuperCrazyVerbose = false;
+
+// clang-format off
+#define LOG_CALL_ALWAYS \
+ LOG(VERBOSE) << '[' << serial << ("] " RADIO_MODULE ".") << __func__ << ' '
+
+#define LOG_CALL \
+ if constexpr (::android::hardware::radio::minimal::debug::kSuperVerbose) \
+ LOG_CALL_ALWAYS
+
+#define LOG_CALL_RESPONSE \
+ if constexpr (::android::hardware::radio::minimal::debug::kSuperCrazyVerbose) \
+ LOG(VERBOSE) << '[' << info.serial << ("] " RADIO_MODULE ".") << __func__ << ' '
+
+#define LOG_CALL_NOSERIAL \
+ if constexpr (::android::hardware::radio::minimal::debug::kSuperVerbose) \
+ LOG(VERBOSE) << (RADIO_MODULE ".") << __func__ << ' '
+// clang-format on
+
+/**
+ * Logs calls implemented to pretend doing the right thing, but doing nothing instead.
+ */
+#define LOG_CALL_IGNORED LOG_CALL_ALWAYS << "(ignored) "
+
+/**
+ * Logs calls always responding with REQUEST_NOT_SUPPORTED error.
+ */
+#define LOG_NOT_SUPPORTED LOG_CALL_ALWAYS << "(not supported) "
+
+/**
+ * Logs calls to deprecated methods. They should be never called by the framework nor xTS.
+ */
+#define LOG_AND_RETURN_DEPRECATED() \
+ LOG(ERROR) << '[' << serial << ("] " RADIO_MODULE ".") << __func__ << " (deprecated!) "; \
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)
+
+} // namespace android::hardware::radio::minimal::debug
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/modem/RadioModem.h b/radio/aidl/minradio/libminradio/include/libminradio/modem/RadioModem.h
new file mode 100644
index 0000000..fda44c8
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/modem/RadioModem.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/GuaranteedCallback.h>
+#include <libminradio/RadioSlotBase.h>
+
+#include <aidl/android/hardware/radio/modem/BnRadioModem.h>
+
+namespace android::hardware::radio::minimal {
+
+class RadioModem : public RadioSlotBase,
+ public aidl::android::hardware::radio::modem::BnRadioModem {
+ public:
+ RadioModem(std::shared_ptr<SlotContext> context,
+ std::vector<aidl::android::hardware::radio::RadioTechnology> rats);
+
+ protected:
+ ::ndk::ScopedAStatus enableModem(int32_t serial, bool on) override;
+ ::ndk::ScopedAStatus getBasebandVersion(int32_t serial) override;
+ ::ndk::ScopedAStatus getDeviceIdentity(int32_t serial) override;
+ ::ndk::ScopedAStatus getHardwareConfig(int32_t serial) override;
+ ::ndk::ScopedAStatus getModemActivityInfo(int32_t serial) override;
+ ::ndk::ScopedAStatus getModemStackStatus(int32_t serial) override;
+ ::ndk::ScopedAStatus getRadioCapability(int32_t serial) override;
+ ::ndk::ScopedAStatus nvReadItem(
+ int32_t serial, ::aidl::android::hardware::radio::modem::NvItem itemId) override;
+ ::ndk::ScopedAStatus nvResetConfig(
+ int32_t serial, ::aidl::android::hardware::radio::modem::ResetNvType type) override;
+ ::ndk::ScopedAStatus nvWriteCdmaPrl(int32_t serial, const std::vector<uint8_t>& prl) override;
+ ::ndk::ScopedAStatus nvWriteItem(
+ int32_t serial, const ::aidl::android::hardware::radio::modem::NvWriteItem& i) override;
+ ::ndk::ScopedAStatus requestShutdown(int32_t serial) override;
+ ::ndk::ScopedAStatus responseAcknowledgement() override;
+ ::ndk::ScopedAStatus sendDeviceState(
+ int32_t serial, ::aidl::android::hardware::radio::modem::DeviceStateType stateType,
+ bool state) override;
+ ::ndk::ScopedAStatus setRadioCapability(
+ int32_t s, const ::aidl::android::hardware::radio::modem::RadioCapability& rc) override;
+ ::ndk::ScopedAStatus setRadioPower(int32_t serial, bool powerOn, bool forEmergencyCall,
+ bool preferredForEmergencyCall) override;
+ ::ndk::ScopedAStatus setResponseFunctions(
+ const std::shared_ptr<::aidl::android::hardware::radio::modem::IRadioModemResponse>&
+ radioModemResponse,
+ const std::shared_ptr<::aidl::android::hardware::radio::modem::IRadioModemIndication>&
+ radioModemIndication) override;
+
+ GuaranteedCallback<::aidl::android::hardware::radio::modem::IRadioModemIndication,
+ ::aidl::android::hardware::radio::modem::IRadioModemIndicationDefault, true>
+ indicate;
+ GuaranteedCallback<::aidl::android::hardware::radio::modem::IRadioModemResponse,
+ ::aidl::android::hardware::radio::modem::IRadioModemResponseDefault>
+ respond;
+
+ private:
+ int32_t mRatBitmap;
+
+ std::string getModemUuid() const;
+ std::string getSimUuid() const;
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/network/RadioNetwork.h b/radio/aidl/minradio/libminradio/include/libminradio/network/RadioNetwork.h
new file mode 100644
index 0000000..4d3505a
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/network/RadioNetwork.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/GuaranteedCallback.h>
+#include <libminradio/RadioSlotBase.h>
+#include <libminradio/network/RadioNetworkResponseTracker.h>
+
+#include <aidl/android/hardware/radio/network/BnRadioNetwork.h>
+
+namespace android::hardware::radio::minimal {
+
+class RadioNetwork : public RadioSlotBase,
+ public aidl::android::hardware::radio::network::BnRadioNetwork {
+ public:
+ using RadioSlotBase::RadioSlotBase;
+
+ protected:
+ std::vector<::aidl::android::hardware::radio::network::CellInfo> getCellInfoListBase();
+
+ ::ndk::ScopedAStatus getAllowedNetworkTypesBitmap(int32_t serial) override;
+ ::ndk::ScopedAStatus getAvailableBandModes(int32_t serial) override;
+ ::ndk::ScopedAStatus getAvailableNetworks(int32_t serial) override;
+ ::ndk::ScopedAStatus getBarringInfo(int32_t serial) override;
+ ::ndk::ScopedAStatus getCdmaRoamingPreference(int32_t serial) override;
+ ::ndk::ScopedAStatus getCellInfoList(int32_t serial) override;
+ ::ndk::ScopedAStatus getImsRegistrationState(int32_t serial) override;
+ ::ndk::ScopedAStatus getNetworkSelectionMode(int32_t serial) override;
+ ::ndk::ScopedAStatus getOperator(int32_t serial) override;
+ ::ndk::ScopedAStatus getSystemSelectionChannels(int32_t serial) override;
+ ::ndk::ScopedAStatus getVoiceRadioTechnology(int32_t serial) override;
+ ::ndk::ScopedAStatus getVoiceRegistrationState(int32_t serial) override;
+ ::ndk::ScopedAStatus isNrDualConnectivityEnabled(int32_t serial) override;
+ ::ndk::ScopedAStatus responseAcknowledgement() override;
+ ::ndk::ScopedAStatus setAllowedNetworkTypesBitmap(int32_t serial,
+ int32_t networkTypeBitmap) override;
+ ::ndk::ScopedAStatus setBandMode(
+ int32_t serial, ::aidl::android::hardware::radio::network::RadioBandMode mode) override;
+ ::ndk::ScopedAStatus setBarringPassword(int32_t serial, const std::string& facility,
+ const std::string& oldPassword,
+ const std::string& newPassword) override;
+ ::ndk::ScopedAStatus setCdmaRoamingPreference(
+ int32_t serial,
+ ::aidl::android::hardware::radio::network::CdmaRoamingType type) override;
+ ::ndk::ScopedAStatus setCellInfoListRate(int32_t serial, int32_t rate) override;
+ ::ndk::ScopedAStatus setIndicationFilter(int32_t serial, int32_t indicationFilter) override;
+ ::ndk::ScopedAStatus setLinkCapacityReportingCriteria(
+ int32_t serial, int32_t hysteresisMs, int32_t hysteresisDlKbps,
+ int32_t hysteresisUlKbps, const std::vector<int32_t>& thresholdsDownlinkKbps,
+ const std::vector<int32_t>& thresholdsUplinkKbps,
+ ::aidl::android::hardware::radio::AccessNetwork accessNetwork) override;
+ ::ndk::ScopedAStatus setLocationUpdates(int32_t serial, bool enable) override;
+ ::ndk::ScopedAStatus setNetworkSelectionModeAutomatic(int32_t serial) override;
+ ::ndk::ScopedAStatus setNetworkSelectionModeManual(
+ int32_t serial, const std::string& operatorNumeric,
+ ::aidl::android::hardware::radio::AccessNetwork ran) override;
+ ::ndk::ScopedAStatus setNrDualConnectivityState(
+ int32_t serial,
+ ::aidl::android::hardware::radio::network::NrDualConnectivityState nrSt) override;
+ ::ndk::ScopedAStatus setResponseFunctions(
+ const std::shared_ptr<::aidl::android::hardware::radio::network::IRadioNetworkResponse>&
+ radioNetworkResponse,
+ const std::shared_ptr<
+ ::aidl::android::hardware::radio::network::IRadioNetworkIndication>&
+ radioNetworkIndication) override;
+ ::ndk::ScopedAStatus setSignalStrengthReportingCriteria(
+ int32_t serial,
+ const std::vector<::aidl::android::hardware::radio::network::SignalThresholdInfo>&
+ signalThresholdInfos) override;
+ ::ndk::ScopedAStatus setSuppServiceNotifications(int32_t serial, bool enable) override;
+ ::ndk::ScopedAStatus setSystemSelectionChannels(
+ int32_t serial, bool specifyChannels,
+ const std::vector<::aidl::android::hardware::radio::network::RadioAccessSpecifier>&
+ specifiers) override;
+ ::ndk::ScopedAStatus startNetworkScan(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::network::NetworkScanRequest& request) override;
+ ::ndk::ScopedAStatus stopNetworkScan(int32_t serial) override;
+ ::ndk::ScopedAStatus supplyNetworkDepersonalization(int32_t serial,
+ const std::string& netPin) override;
+ ::ndk::ScopedAStatus setUsageSetting(
+ int32_t serial,
+ ::aidl::android::hardware::radio::network::UsageSetting usageSetting) override;
+ ::ndk::ScopedAStatus getUsageSetting(int32_t serial) override;
+
+ ::ndk::ScopedAStatus setEmergencyMode(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::network::EmergencyMode emergencyMode) override;
+ ::ndk::ScopedAStatus triggerEmergencyNetworkScan(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::network::EmergencyNetworkScanTrigger&
+ scanTrigger) override;
+ ::ndk::ScopedAStatus cancelEmergencyNetworkScan(int32_t serial, bool resetScan) override;
+ ::ndk::ScopedAStatus exitEmergencyMode(int32_t serial) override;
+ ::ndk::ScopedAStatus setNullCipherAndIntegrityEnabled(int32_t serial, bool enabled) override;
+ ::ndk::ScopedAStatus isNullCipherAndIntegrityEnabled(int32_t serial) override;
+ ::ndk::ScopedAStatus isN1ModeEnabled(int32_t serial) override;
+ ::ndk::ScopedAStatus setN1ModeEnabled(int32_t serial, bool enable) override;
+ ::ndk::ScopedAStatus isCellularIdentifierTransparencyEnabled(int32_t serial) override;
+ ::ndk::ScopedAStatus setCellularIdentifierTransparencyEnabled(int32_t serial,
+ bool enabled) override;
+ ::ndk::ScopedAStatus setSecurityAlgorithmsUpdatedEnabled(int32_t serial, bool enabled) override;
+ ::ndk::ScopedAStatus isSecurityAlgorithmsUpdatedEnabled(int32_t serial) override;
+ ::ndk::ScopedAStatus setSatellitePlmn(
+ int32_t in_serial, const std::vector<std::string>& carrierPlmnArray,
+ const std::vector<std::string>& allSatellitePlmnArray) override;
+ ::ndk::ScopedAStatus setSatelliteEnabledForCarrier(int32_t serial,
+ bool satelliteEnabled) override;
+ ::ndk::ScopedAStatus isSatelliteEnabledForCarrier(int32_t serial) override;
+
+ GuaranteedCallback<::aidl::android::hardware::radio::network::IRadioNetworkIndication,
+ ::aidl::android::hardware::radio::network::IRadioNetworkIndicationDefault,
+ true>
+ indicate;
+ GuaranteedCallback<::aidl::android::hardware::radio::network::IRadioNetworkResponse,
+ ::aidl::android::hardware::radio::network::IRadioNetworkResponseDefault>
+ respond;
+
+ private:
+ int32_t mAllowedNetworkTypesBitmap = std::numeric_limits<int32_t>::max();
+
+ ResponseTrackerHolder<RadioNetworkResponseTracker> mResponseTracker;
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/network/RadioNetworkResponseTracker.h b/radio/aidl/minradio/libminradio/include/libminradio/network/RadioNetworkResponseTracker.h
new file mode 100644
index 0000000..2978cd8
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/network/RadioNetworkResponseTracker.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/ResponseTracker.h>
+
+#include <aidl/android/hardware/radio/network/BnRadioNetworkResponse.h>
+#include <aidl/android/hardware/radio/network/IRadioNetwork.h>
+
+namespace android::hardware::radio::minimal {
+
+class RadioNetworkResponseTracker
+ : public ResponseTracker<::aidl::android::hardware::radio::network::IRadioNetwork,
+ ::aidl::android::hardware::radio::network::IRadioNetworkResponse> {
+ public:
+ RadioNetworkResponseTracker(
+ std::shared_ptr<::aidl::android::hardware::radio::network::IRadioNetwork> req,
+ const std::shared_ptr<::aidl::android::hardware::radio::network::IRadioNetworkResponse>&
+ resp);
+
+ ResponseTrackerResult<::aidl::android::hardware::radio::network::RegStateResult>
+ getDataRegistrationState();
+ ResponseTrackerResult<::aidl::android::hardware::radio::network::SignalStrength>
+ getSignalStrength();
+
+ protected:
+ ::ndk::ScopedAStatus getDataRegistrationStateResponse(
+ const ::aidl::android::hardware::radio::RadioResponseInfo& info,
+ const ::aidl::android::hardware::radio::network::RegStateResult& dataRegResp) override;
+ ::ndk::ScopedAStatus getSignalStrengthResponse(
+ const ::aidl::android::hardware::radio::RadioResponseInfo& info,
+ const ::aidl::android::hardware::radio::network::SignalStrength& signalStrength)
+ override;
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/network/structs.h b/radio/aidl/minradio/libminradio/include/libminradio/network/structs.h
new file mode 100644
index 0000000..4410924
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/network/structs.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/radio/network/CellInfo.h>
+#include <aidl/android/hardware/radio/network/RegStateResult.h>
+#include <aidl/android/hardware/radio/network/SignalStrength.h>
+#include <aidl/android/hardware/radio/network/SignalThresholdInfo.h>
+
+namespace android::hardware::radio::minimal::structs {
+
+::aidl::android::hardware::radio::network::SignalStrength makeSignalStrength();
+::aidl::android::hardware::radio::network::CellInfo makeCellInfo(
+ const ::aidl::android::hardware::radio::network::RegStateResult& regState,
+ const ::aidl::android::hardware::radio::network::SignalStrength& signalStrength);
+
+::aidl::android::hardware::radio::network::OperatorInfo getOperatorInfo(
+ const ::aidl::android::hardware::radio::network::CellIdentity& cellIdentity);
+
+int32_t rssiToSignalStrength(int32_t rssi);
+int32_t validateRsrp(int32_t rsrp);
+int32_t validateRsrq(int32_t rsrq);
+bool validateSignalThresholdInfos(
+ const std::vector<::aidl::android::hardware::radio::network::SignalThresholdInfo>& infos);
+
+} // namespace android::hardware::radio::minimal::structs
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/response.h b/radio/aidl/minradio/libminradio/include/libminradio/response.h
new file mode 100644
index 0000000..5692628
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/response.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/radio/RadioResponseInfo.h>
+
+namespace android::hardware::radio::minimal {
+
+aidl::android::hardware::radio::RadioResponseInfo noError(int32_t serial);
+aidl::android::hardware::radio::RadioResponseInfo notSupported(int32_t serial);
+aidl::android::hardware::radio::RadioResponseInfo errorResponse(
+ int32_t serial, aidl::android::hardware::radio::RadioError error);
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/sim/App.h b/radio/aidl/minradio/libminradio/include/libminradio/sim/App.h
new file mode 100644
index 0000000..9f0eebe
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/App.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/radio/sim/IccIo.h>
+#include <aidl/android/hardware/radio/sim/IccIoResult.h>
+#include <aidl/android/hardware/radio/sim/SimApdu.h>
+#include <android-base/macros.h>
+
+namespace android::hardware::radio::minimal::sim {
+
+class App {
+ public:
+ class Channel {
+ public:
+ Channel(uint8_t channelId);
+ virtual ~Channel() = default;
+
+ uint8_t getId() const;
+ std::vector<uint8_t> getSelectResponse() const;
+
+ virtual ::aidl::android::hardware::radio::sim::IccIoResult transmit(
+ const ::aidl::android::hardware::radio::sim::SimApdu& message) = 0;
+
+ private:
+ uint8_t mChannelId;
+
+ DISALLOW_COPY_AND_ASSIGN(Channel);
+ };
+
+ virtual ~App() = default;
+
+ std::string_view getAid() const;
+
+ virtual std::shared_ptr<Channel> newChannel(int32_t id) = 0;
+
+ virtual ::aidl::android::hardware::radio::sim::IccIoResult iccIo(
+ const ::aidl::android::hardware::radio::sim::IccIo& iccIo);
+
+ protected:
+ App(std::string_view aid);
+
+ private:
+ std::string mAid;
+
+ DISALLOW_COPY_AND_ASSIGN(App);
+};
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/sim/AppManager.h b/radio/aidl/minradio/libminradio/include/libminradio/sim/AppManager.h
new file mode 100644
index 0000000..c142fb9
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/AppManager.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/radio/RadioError.h>
+#include <aidl/android/hardware/radio/sim/IccIo.h>
+#include <android-base/macros.h>
+#include <libminradio/sim/App.h>
+
+#include <map>
+
+namespace android::hardware::radio::minimal::sim {
+
+class AppManager {
+ public:
+ AppManager();
+
+ void addApp(std::shared_ptr<App> app);
+
+ std::pair<::aidl::android::hardware::radio::RadioError, std::shared_ptr<App::Channel>>
+ openLogicalChannel(std::string_view aid, int32_t p2);
+ ::aidl::android::hardware::radio::RadioError closeLogicalChannel(int32_t channelId);
+
+ ::aidl::android::hardware::radio::sim::IccIoResult transmit(
+ const ::aidl::android::hardware::radio::sim::SimApdu& message);
+ ::aidl::android::hardware::radio::sim::IccIoResult iccIo(
+ const ::aidl::android::hardware::radio::sim::IccIo& iccIo);
+
+ private:
+ std::map<std::string, std::shared_ptr<App>, std::less<>> mApps;
+ mutable std::mutex mChannelsGuard;
+ std::map<int32_t, std::shared_ptr<App::Channel>> mChannels;
+
+ ::aidl::android::hardware::radio::sim::IccIoResult commandManageChannel(int32_t p1, int32_t p2);
+
+ DISALLOW_COPY_AND_ASSIGN(AppManager);
+};
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/sim/Filesystem.h b/radio/aidl/minradio/libminradio/include/libminradio/sim/Filesystem.h
new file mode 100644
index 0000000..4e7c5ee
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/Filesystem.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
+
+#include <map>
+#include <set>
+#include <span>
+
+namespace android::hardware::radio::minimal::sim {
+
+class Filesystem {
+ public:
+ /** 3GPP TS 27.007 8.18 */
+ struct Path {
+ int32_t fileId;
+ std::string pathId;
+ auto operator<=>(const Path&) const = default;
+ std::string toString() const;
+ };
+
+ typedef std::span<uint8_t const> FileView;
+
+ private:
+ mutable std::mutex mFilesGuard;
+ std::map<Path, std::vector<uint8_t>> mFiles GUARDED_BY(mFilesGuard);
+ std::set<int32_t> mUpdates GUARDED_BY(mFilesGuard);
+
+ DISALLOW_COPY_AND_ASSIGN(Filesystem);
+
+ public:
+ Filesystem();
+
+ void write(const Path& path, FileView contents);
+ void write(const Path& path, std::string_view contents);
+ void write(const Path& path, std::vector<uint8_t>&& contents);
+ std::optional<FileView> read(const Path& path) const;
+
+ void writeBch(const Path& path, std::string_view contents);
+ std::optional<std::string> readBch(const Path& path) const;
+
+ std::optional<Path> find(uint16_t fileId);
+
+ std::set<int32_t> fetchAndClearUpdates();
+};
+
+namespace paths {
+
+extern const Filesystem::Path mf;
+extern const Filesystem::Path fplmn;
+extern const Filesystem::Path iccid;
+extern const Filesystem::Path msisdn;
+extern const Filesystem::Path pl;
+extern const Filesystem::Path arr;
+extern const Filesystem::Path ad;
+
+} // namespace paths
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/sim/IccConstants.h b/radio/aidl/minradio/libminradio/include/libminradio/sim/IccConstants.h
new file mode 100644
index 0000000..d33ae28
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/IccConstants.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <string>
+
+namespace android::hardware::radio::minimal::sim::constants {
+
+// From frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/IccConstants.java
+// 3GPP TS 51.011 Annex D
+// ETSI TS 131 102 Annex A
+constexpr int EF_ADN = 0x6F3A;
+constexpr int EF_FDN = 0x6F3B;
+constexpr int EF_GID1 = 0x6F3E;
+constexpr int EF_GID2 = 0x6F3F;
+constexpr int EF_SDN = 0x6F49;
+constexpr int EF_EXT1 = 0x6F4A;
+constexpr int EF_EXT2 = 0x6F4B;
+constexpr int EF_EXT3 = 0x6F4C;
+constexpr int EF_EXT5 = 0x6F4E;
+constexpr int EF_EXT6 = 0x6FC8;
+constexpr int EF_MWIS = 0x6FCA;
+constexpr int EF_MBDN = 0x6FC7;
+constexpr int EF_PNN = 0x6FC5;
+constexpr int EF_OPL = 0x6FC6;
+constexpr int EF_SPN = 0x6F46;
+constexpr int EF_SMS = 0x6F3C;
+constexpr int EF_ICCID = 0x2FE2;
+constexpr int EF_AD = 0x6FAD;
+constexpr int EF_MBI = 0x6FC9;
+constexpr int EF_MSISDN = 0x6F40;
+constexpr int EF_SPDI = 0x6FCD;
+constexpr int EF_SST = 0x6F38;
+constexpr int EF_CFIS = 0x6FCB;
+constexpr int EF_IMG = 0x4F20;
+constexpr int EF_PSISMSC = 0x6FE5;
+constexpr int EF_SMSS = 0x6F43;
+constexpr int EF_PBR = 0x4F30;
+constexpr int EF_LI = 0x6F05;
+constexpr int EF_MAILBOX_CPHS = 0x6F17;
+constexpr int EF_VOICE_MAIL_INDICATOR_CPHS = 0x6F11;
+constexpr int EF_CFF_CPHS = 0x6F13;
+constexpr int EF_SPN_CPHS = 0x6F14;
+constexpr int EF_SPN_SHORT_CPHS = 0x6F18;
+constexpr int EF_INFO_CPHS = 0x6F16;
+constexpr int EF_CSP_CPHS = 0x6F15;
+constexpr int EF_CST = 0x6F32;
+constexpr int EF_RUIM_SPN = 0x6F41;
+constexpr int EF_PL = 0x2F05;
+constexpr int EF_ARR = 0x2F06;
+constexpr int EF_CSIM_LI = 0x6F3A;
+constexpr int EF_CSIM_SPN = 0x6F41;
+constexpr int EF_CSIM_MDN = 0x6F44;
+constexpr int EF_CSIM_IMSIM = 0x6F22;
+constexpr int EF_CSIM_CDMAHOME = 0x6F28;
+constexpr int EF_CSIM_EPRL = 0x6F5A;
+constexpr int EF_CSIM_PRL = 0x6F30;
+constexpr int EF_CSIM_MLPL = 0x4F20;
+constexpr int EF_CSIM_MSPL = 0x4F21;
+constexpr int EF_CSIM_MIPUPP = 0x6F4D;
+constexpr int EF_IMPU = 0x6F04;
+constexpr int EF_IMPI = 0x6F02;
+constexpr int EF_DOMAIN = 0x6F03;
+constexpr int EF_IST = 0x6F07;
+constexpr int EF_PCSCF = 0x6F09;
+constexpr int EF_PLMN_W_ACT = 0x6F60;
+constexpr int EF_OPLMN_W_ACT = 0x6F61;
+constexpr int EF_HPLMN_W_ACT = 0x6F62;
+constexpr int EF_EHPLMN = 0x6FD9;
+constexpr int EF_FPLMN = 0x6F7B;
+constexpr int EF_LRPLMNSI = 0x6FDC;
+constexpr int EF_HPPLMN = 0x6F31;
+// 3GPP TS 51.011 10.7
+constexpr int MF_SIM_VAL = 0x3F00;
+constexpr std::string MF_SIM = "3F00";
+constexpr std::string DF_TELECOM = "7F10";
+constexpr std::string DF_PHONEBOOK = "5F3A";
+constexpr std::string DF_GRAPHICS = "5F50";
+constexpr std::string DF_GSM = "7F20";
+constexpr std::string DF_CDMA = "7F25";
+constexpr std::string DF_MMSS = "5F3C";
+constexpr std::string DF_ADF = "7FFF";
+
+// From frameworks/base/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+constexpr int FPLMN_BYTE_SIZE = 3;
+
+// From frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+// 3GPP TS 11.11 9.2
+constexpr int COMMAND_READ_BINARY = 0xB0; // 176
+constexpr int COMMAND_UPDATE_BINARY = 0xD6; // 214
+constexpr int COMMAND_READ_RECORD = 0xB2; // 178
+constexpr int COMMAND_UPDATE_RECORD = 0xDC; // 220
+constexpr int COMMAND_SEEK = 0xA2; // 162 (also: SEARCH RECORD)
+constexpr int COMMAND_SELECT = 0xA4; // 164
+constexpr int COMMAND_GET_RESPONSE = 0xC0; // 192
+constexpr int COMMAND_STATUS = 0xF2; // 242
+constexpr int COMMAND_GET_DATA = 0xCA; // 202 (ISO 7816 7.4.2)
+constexpr int COMMAND_MANAGE_CHANNEL = 0x70; // 112
+constexpr int EF_TYPE_TRANSPARENT = 0;
+constexpr int EF_TYPE_LINEAR_FIXED = 1;
+constexpr int EF_TYPE_CYCLIC = 3;
+constexpr int TYPE_RFU = 0;
+constexpr int TYPE_MF = 1;
+constexpr int TYPE_DF = 2;
+constexpr int TYPE_EF = 4;
+constexpr int GET_RESPONSE_EF_SIZE_BYTES = 15;
+constexpr int RESPONSE_DATA_RFU_1 = 0;
+constexpr int RESPONSE_DATA_RFU_2 = 1;
+constexpr int RESPONSE_DATA_FILE_SIZE_1 = 2;
+constexpr int RESPONSE_DATA_FILE_SIZE_2 = 3;
+constexpr int RESPONSE_DATA_FILE_ID_1 = 4;
+constexpr int RESPONSE_DATA_FILE_ID_2 = 5;
+constexpr int RESPONSE_DATA_FILE_TYPE = 6;
+constexpr int RESPONSE_DATA_RFU_3 = 7;
+constexpr int RESPONSE_DATA_ACCESS_CONDITION_1 = 8;
+constexpr int RESPONSE_DATA_ACCESS_CONDITION_2 = 9;
+constexpr int RESPONSE_DATA_ACCESS_CONDITION_3 = 10;
+constexpr int RESPONSE_DATA_FILE_STATUS = 11;
+constexpr int RESPONSE_DATA_LENGTH = 12;
+constexpr int RESPONSE_DATA_STRUCTURE = 13;
+constexpr int RESPONSE_DATA_RECORD_LENGTH = 14;
+
+// From frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/IccIoResult.java
+// ISO 7816 5.1.3
+constexpr uint16_t IO_RESULT_SUCCESS = 0x9000;
+constexpr uint16_t IO_RESULT_NOT_SUPPORTED = 0x6A81;
+constexpr uint16_t IO_RESULT_FILE_NOT_FOUND = 0x6A82; // file or application
+constexpr uint16_t IO_RESULT_INCORRECT_DATA = 0x6A80;
+constexpr uint16_t IO_RESULT_INCORRECT_P1_P2 = 0x6A86;
+constexpr uint16_t IO_RESULT_INCORRECT_LENGTH = 0x6C00; // low byte is suggested length
+constexpr uint16_t IO_RESULT_CLASS_NOT_SUPPORTED = 0x6E00;
+constexpr uint16_t IO_RESULT_CHANNEL_NOT_SUPPORTED = 0x6881;
+constexpr uint16_t IO_RESULT_TECHNICAL_PROBLEM = 0x6F00;
+
+} // namespace android::hardware::radio::minimal::sim::constants
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/sim/IccUtils.h b/radio/aidl/minradio/libminradio/include/libminradio/sim/IccUtils.h
new file mode 100644
index 0000000..9f56f72
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/IccUtils.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/radio/sim/IccIoResult.h>
+
+#include <span>
+#include <string>
+
+namespace android::hardware::radio::minimal::sim {
+
+::aidl::android::hardware::radio::sim::IccIoResult toIccIoResult(std::span<uint8_t const> bytes);
+::aidl::android::hardware::radio::sim::IccIoResult toIccIoResult(std::vector<uint8_t>&& bytes);
+::aidl::android::hardware::radio::sim::IccIoResult toIccIoResult(std::string_view simResponse);
+::aidl::android::hardware::radio::sim::IccIoResult toIccIoResult(uint16_t errorCode);
+
+std::vector<uint8_t> hexStringToBytes(std::string_view str);
+std::vector<uint8_t> hexStringToBch(std::string_view str);
+std::string bytesToHexString(std::span<uint8_t const> bytes);
+std::string bytesToHexString(std::vector<uint8_t>&& bytes);
+std::string bchToHexString(std::span<uint8_t const> bytes);
+
+std::vector<uint8_t> uint8ToBytes(uint8_t val);
+std::vector<uint8_t> uint16ToBytes(uint16_t val);
+
+std::vector<uint8_t> encodeFplmns(std::span<std::string_view> fplmns);
+std::vector<uint8_t> encodeMsisdn(std::string_view phoneNumber);
+std::vector<uint8_t> encodeAd(uint8_t mncLength);
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/sim/RadioSim.h b/radio/aidl/minradio/libminradio/include/libminradio/sim/RadioSim.h
new file mode 100644
index 0000000..cd138a1
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/RadioSim.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/GuaranteedCallback.h>
+#include <libminradio/RadioSlotBase.h>
+#include <libminradio/sim/AppManager.h>
+#include <libminradio/sim/Filesystem.h>
+
+#include <aidl/android/hardware/radio/sim/BnRadioSim.h>
+
+#include <map>
+
+namespace android::hardware::radio::minimal {
+
+class RadioSim : public RadioSlotBase, public aidl::android::hardware::radio::sim::BnRadioSim {
+ public:
+ RadioSim(std::shared_ptr<SlotContext> context);
+
+ protected:
+ void setIccid(std::string iccid);
+ std::optional<std::string> getIccid() const;
+
+ /**
+ * Add CTS_UICC_2021 certificate to UICC.
+ *
+ * This *must not* be called on production build on user's device.
+ */
+ void addCtsCertificate();
+
+ ::ndk::ScopedAStatus areUiccApplicationsEnabled(int32_t serial) override;
+ ::ndk::ScopedAStatus changeIccPin2ForApp(int32_t serial, const std::string& oldPin2,
+ const std::string& newPin2,
+ const std::string& aid) override;
+ ::ndk::ScopedAStatus changeIccPinForApp(int32_t serial, const std::string& oldPin,
+ const std::string& newPin,
+ const std::string& aid) override;
+ ::ndk::ScopedAStatus enableUiccApplications(int32_t serial, bool enable) override;
+ ::ndk::ScopedAStatus getAllowedCarriers(int32_t serial) override;
+ ::ndk::ScopedAStatus getCdmaSubscription(int32_t serial) override;
+ ::ndk::ScopedAStatus getCdmaSubscriptionSource(int32_t serial) override;
+ ::ndk::ScopedAStatus getFacilityLockForApp(int32_t serial, const std::string& facility,
+ const std::string& password, int32_t serviceClass,
+ const std::string& appId) override;
+ ::ndk::ScopedAStatus getSimPhonebookCapacity(int32_t serial) override;
+ ::ndk::ScopedAStatus getSimPhonebookRecords(int32_t serial) override;
+ ::ndk::ScopedAStatus iccCloseLogicalChannel(int32_t serial, int32_t channelId) override;
+ ::ndk::ScopedAStatus iccCloseLogicalChannelWithSessionInfo(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::sim::SessionInfo& recordInfo) override;
+ ::ndk::ScopedAStatus iccIoForApp(
+ int32_t serial, const ::aidl::android::hardware::radio::sim::IccIo& iccIo) override;
+ ::ndk::ScopedAStatus iccOpenLogicalChannel(int32_t serial, const std::string& aid,
+ int32_t p2) override;
+ ::ndk::ScopedAStatus iccTransmitApduBasicChannel(
+ int32_t serial, const ::aidl::android::hardware::radio::sim::SimApdu& message) override;
+ ::ndk::ScopedAStatus iccTransmitApduLogicalChannel(
+ int32_t serial, const ::aidl::android::hardware::radio::sim::SimApdu& message) override;
+ ::ndk::ScopedAStatus reportStkServiceIsRunning(int32_t serial) override;
+ ::ndk::ScopedAStatus requestIccSimAuthentication(int32_t serial, int32_t authContext,
+ const std::string& authData,
+ const std::string& aid) override;
+ ::ndk::ScopedAStatus responseAcknowledgement() override;
+ ::ndk::ScopedAStatus sendEnvelope(int32_t serial, const std::string& command) override;
+ ::ndk::ScopedAStatus sendEnvelopeWithStatus(int32_t serial,
+ const std::string& contents) override;
+ ::ndk::ScopedAStatus sendTerminalResponseToSim(int32_t serial,
+ const std::string& commandResponse) override;
+ ::ndk::ScopedAStatus setAllowedCarriers(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::sim::CarrierRestrictions& carriers,
+ ::aidl::android::hardware::radio::sim::SimLockMultiSimPolicy multiSimPolicy) override;
+ ::ndk::ScopedAStatus setCarrierInfoForImsiEncryption(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::sim::ImsiEncryptionInfo& imsiEncryptionInfo)
+ override;
+ ::ndk::ScopedAStatus setCdmaSubscriptionSource(
+ int32_t serial,
+ ::aidl::android::hardware::radio::sim::CdmaSubscriptionSource cdmaSub) override;
+ ::ndk::ScopedAStatus setFacilityLockForApp( //
+ int32_t serial, const std::string& facility, bool lockState, const std::string& passwd,
+ int32_t serviceClass, const std::string& appId) override;
+ ::ndk::ScopedAStatus setResponseFunctions(
+ const std::shared_ptr<::aidl::android::hardware::radio::sim::IRadioSimResponse>&
+ radioSimResponse,
+ const std::shared_ptr<::aidl::android::hardware::radio::sim::IRadioSimIndication>&
+ radioSimIndication) override;
+ ::ndk::ScopedAStatus setSimCardPower(
+ int32_t serial, ::aidl::android::hardware::radio::sim::CardPowerState powerUp) override;
+ ::ndk::ScopedAStatus setUiccSubscription(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::sim::SelectUiccSub& uiccSub) override;
+ ::ndk::ScopedAStatus supplyIccPin2ForApp(int32_t serial, const std::string& pin2,
+ const std::string& aid) override;
+ ::ndk::ScopedAStatus supplyIccPinForApp(int32_t serial, const std::string& pin,
+ const std::string& aid) override;
+ ::ndk::ScopedAStatus supplyIccPuk2ForApp(int32_t serial, const std::string& puk2,
+ const std::string& pin2,
+ const std::string& aid) override;
+ ::ndk::ScopedAStatus supplyIccPukForApp(int32_t serial, const std::string& puk,
+ const std::string& pin,
+ const std::string& aid) override;
+ ::ndk::ScopedAStatus supplySimDepersonalization(
+ int32_t serial, ::aidl::android::hardware::radio::sim::PersoSubstate persoType,
+ const std::string& controlKey) override;
+ ::ndk::ScopedAStatus updateSimPhonebookRecords(
+ int32_t serial,
+ const ::aidl::android::hardware::radio::sim::PhonebookRecordInfo& recordInfo) override;
+
+ GuaranteedCallback<::aidl::android::hardware::radio::sim::IRadioSimIndication,
+ ::aidl::android::hardware::radio::sim::IRadioSimIndicationDefault, true>
+ indicate;
+ GuaranteedCallback<::aidl::android::hardware::radio::sim::IRadioSimResponse,
+ ::aidl::android::hardware::radio::sim::IRadioSimResponseDefault>
+ respond;
+
+ sim::AppManager mAppManager;
+ const std::shared_ptr<sim::Filesystem> mFilesystem = std::make_shared<sim::Filesystem>();
+
+ private:
+ bool mAreUiccApplicationsEnabled = true;
+};
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/sim/apps/AraM.h b/radio/aidl/minradio/libminradio/include/libminradio/sim/apps/AraM.h
new file mode 100644
index 0000000..e8c787f
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/apps/AraM.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#pragma once
+
+#include <libminradio/sim/App.h>
+
+#include <span>
+
+namespace android::hardware::radio::minimal::sim::apps {
+
+/**
+ * UICC carrier privileges app (ARA-M) implementation.
+ *
+ * https://source.android.com/docs/core/connect/uicc
+ */
+class AraM : public std::enable_shared_from_this<AraM>, public App {
+ public:
+ static constexpr char AID[] = "A00000015141434C00";
+
+ struct Rule {
+ std::vector<uint8_t> deviceAppID;
+ std::string pkg;
+ };
+
+ AraM();
+ std::shared_ptr<App::Channel> newChannel(int32_t id) override;
+
+ void addRule(Rule rule);
+ std::span<const Rule> getRules() const;
+
+ private:
+ std::vector<Rule> mRules;
+};
+
+} // namespace android::hardware::radio::minimal::sim::apps
diff --git a/radio/aidl/minradio/libminradio/include/libminradio/sim/apps/FilesystemApp.h b/radio/aidl/minradio/libminradio/include/libminradio/sim/apps/FilesystemApp.h
new file mode 100644
index 0000000..36178e4
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/apps/FilesystemApp.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/radio/sim/IccIo.h>
+#include <aidl/android/hardware/radio/sim/IccIoResult.h>
+#include <libminradio/sim/App.h>
+#include <libminradio/sim/Filesystem.h>
+
+namespace android::hardware::radio::minimal::sim::apps {
+
+class FilesystemApp : public App {
+ public:
+ static constexpr char AID[] = "";
+
+ FilesystemApp(const std::shared_ptr<Filesystem>& filesystem);
+ std::shared_ptr<App::Channel> newChannel(int32_t id) override;
+
+ ::aidl::android::hardware::radio::sim::IccIoResult iccIo(
+ const ::aidl::android::hardware::radio::sim::IccIo& iccIo) override;
+
+ private:
+ class FilesystemChannel;
+
+ std::shared_ptr<FilesystemChannel> mBasicChannel;
+ std::shared_ptr<Filesystem> mFilesystem;
+};
+
+} // namespace android::hardware::radio::minimal::sim::apps
diff --git a/radio/aidl/minradio/libminradio/modem/RadioModem.cpp b/radio/aidl/minradio/libminradio/modem/RadioModem.cpp
new file mode 100644
index 0000000..f29cd39
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/modem/RadioModem.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2023 The Android Open Source 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.
+ */
+
+#include <libminradio/modem/RadioModem.h>
+
+#include <libminradio/debug.h>
+#include <libminradio/response.h>
+
+#define RADIO_MODULE "Modem"
+
+namespace android::hardware::radio::minimal {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::RadioIndicationType;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::modem;
+namespace aidlRadio = ::aidl::android::hardware::radio;
+constexpr auto ok = &ScopedAStatus::ok;
+
+RadioModem::RadioModem(std::shared_ptr<SlotContext> context,
+ std::vector<aidlRadio::RadioTechnology> rats)
+ : RadioSlotBase(context) {
+ int32_t ratBitmap = 0;
+ for (auto rat : rats) {
+ CHECK(rat > aidlRadio::RadioTechnology::UNKNOWN) << "Invalid RadioTechnology: " << rat;
+ CHECK(rat <= aidlRadio::RadioTechnology::NR)
+ << ": " << rat << " not supported yet: "
+ << "please verify if RadioAccessFamily for this RadioTechnology is a bit-shifted 1";
+ ratBitmap |= 1 << static_cast<int32_t>(rat);
+ }
+ mRatBitmap = ratBitmap;
+}
+
+std::string RadioModem::getModemUuid() const {
+ // Assumes one modem per slot.
+ return std::format("com.android.minradio.modem{}", mContext->getSlotIndex());
+}
+
+std::string RadioModem::getSimUuid() const {
+ // Assumes one SIM per slot.
+ return std::format("com.android.minradio.sim{}", mContext->getSlotIndex());
+}
+
+ScopedAStatus RadioModem::enableModem(int32_t serial, bool on) {
+ LOG_NOT_SUPPORTED << on;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus RadioModem::getBasebandVersion(int32_t serial) {
+ LOG_CALL;
+ respond()->getBasebandVersionResponse( //
+ noError(serial), std::format("libminradio V{}", IRadioModem::version));
+ return ok();
+}
+
+ScopedAStatus RadioModem::getDeviceIdentity(int32_t serial) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioModem::getHardwareConfig(int32_t serial) {
+ LOG_CALL;
+
+ aidl::HardwareConfig modem1Config{
+ .type = aidl::HardwareConfig::TYPE_MODEM,
+ .uuid = getModemUuid(),
+ .state = aidl::HardwareConfig::STATE_ENABLED,
+ .modem = {{
+ .rilModel = 0, // 0=single (one-to-one relationship for hw and ril daemon)
+ .rat = static_cast<aidlRadio::RadioTechnology>(mRatBitmap),
+ .maxVoiceCalls = 0,
+ .maxDataCalls = 1,
+ .maxStandby = 1,
+ }},
+ };
+
+ aidl::HardwareConfig sim1Config{
+ .type = aidl::HardwareConfig::TYPE_SIM,
+ .uuid = getSimUuid(),
+ .state = aidl::HardwareConfig::STATE_ENABLED,
+ .sim = {{
+ .modemUuid = getModemUuid(),
+ }},
+ };
+
+ respond()->getHardwareConfigResponse(noError(serial), {modem1Config, sim1Config});
+ return ok();
+}
+
+ScopedAStatus RadioModem::getModemActivityInfo(int32_t serial) {
+ LOG_CALL_IGNORED;
+ const aidl::ActivityStatsTechSpecificInfo generalActivityStats{
+ .txmModetimeMs = {0, 0, 0, 0, 0},
+ };
+ const aidl::ActivityStatsInfo info{
+ // idleModeTimeMs doesn't make sense for external modem, but the framework
+ // doesn't allow for ModemActivityInfo.isEmpty
+ .idleModeTimeMs = 1,
+ .techSpecificInfo = {generalActivityStats},
+ };
+ respond()->getModemActivityInfoResponse(noError(serial), info);
+ return ok();
+}
+
+ScopedAStatus RadioModem::getModemStackStatus(int32_t serial) {
+ LOG_CALL;
+ respond()->getModemStackStatusResponse(noError(serial), true);
+ return ok();
+}
+
+ScopedAStatus RadioModem::getRadioCapability(int32_t serial) {
+ LOG_CALL;
+ aidl::RadioCapability cap{
+ .session = 0,
+ .phase = aidl::RadioCapability::PHASE_FINISH,
+ .raf = mRatBitmap, // rafs are nothing else than rat masks
+ .logicalModemUuid = getModemUuid(),
+ .status = aidl::RadioCapability::STATUS_SUCCESS,
+ };
+ respond()->getRadioCapabilityResponse(noError(serial), cap);
+ return ok();
+}
+
+ScopedAStatus RadioModem::nvReadItem(int32_t serial, aidl::NvItem) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioModem::nvResetConfig(int32_t serial, aidl::ResetNvType resetType) {
+ LOG_CALL << resetType; // RELOAD is the only non-deprecated argument
+ respond()->nvResetConfigResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioModem::nvWriteCdmaPrl(int32_t serial, const std::vector<uint8_t>&) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioModem::nvWriteItem(int32_t serial, const aidl::NvWriteItem&) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioModem::requestShutdown(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus RadioModem::responseAcknowledgement() {
+ LOG_CALL_NOSERIAL;
+ return ok();
+}
+
+ScopedAStatus RadioModem::sendDeviceState(int32_t serial, aidl::DeviceStateType type, bool state) {
+ LOG_CALL_IGNORED << type << ' ' << state;
+ respond()->sendDeviceStateResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioModem::setRadioCapability(int32_t serial, const aidl::RadioCapability& rc) {
+ LOG_NOT_SUPPORTED << rc;
+ respond()->setRadioCapabilityResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioModem::setRadioPower(int32_t serial, bool powerOn, bool forEmergencyCall,
+ bool preferredForEmergencyCall) {
+ LOG_CALL_IGNORED << powerOn << " " << forEmergencyCall << " " << preferredForEmergencyCall;
+ respond()->setRadioPowerResponse(noError(serial));
+ indicate()->radioStateChanged(RadioIndicationType::UNSOLICITED,
+ powerOn ? aidl::RadioState::ON : aidl::RadioState::OFF);
+ return ok();
+}
+
+ScopedAStatus RadioModem::setResponseFunctions(
+ const std::shared_ptr<aidl::IRadioModemResponse>& response,
+ const std::shared_ptr<aidl::IRadioModemIndication>& indication) {
+ LOG_CALL_NOSERIAL << response << ' ' << indication;
+ CHECK(response);
+ CHECK(indication);
+ respond = response;
+ indicate = indication;
+
+ indicate()->rilConnected(RadioIndicationType::UNSOLICITED);
+ indicate()->radioStateChanged(RadioIndicationType::UNSOLICITED, aidl::RadioState::ON);
+
+ return ok();
+}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/network/RadioNetwork.cpp b/radio/aidl/minradio/libminradio/network/RadioNetwork.cpp
new file mode 100644
index 0000000..e2d2a56
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/network/RadioNetwork.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2023 The Android Open Source 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.
+ */
+
+#include <libminradio/network/RadioNetwork.h>
+
+#include <libminradio/debug.h>
+#include <libminradio/network/structs.h>
+#include <libminradio/response.h>
+
+#include <chrono>
+#include <thread>
+
+#define RADIO_MODULE "Network"
+
+namespace android::hardware::radio::minimal {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::AccessNetwork;
+using ::aidl::android::hardware::radio::RadioError;
+using ::aidl::android::hardware::radio::RadioIndicationType;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::network;
+namespace aidlRadio = ::aidl::android::hardware::radio;
+constexpr auto ok = &ScopedAStatus::ok;
+
+std::vector<aidl::CellInfo> RadioNetwork::getCellInfoListBase() {
+ if (!mResponseTracker) return {};
+
+ // There's a slight race between get*RegistrationState and getSignalStrength, but
+ // getCellInfoListBase is best-effort anyway, so it's the best we can do here.
+ auto dataRegistrationState = mResponseTracker()->getDataRegistrationState();
+ auto signalStrength = mResponseTracker()->getSignalStrength();
+ if (!dataRegistrationState.expectOk() || !signalStrength.expectOk()) return {};
+
+ return {structs::makeCellInfo(*dataRegistrationState, *signalStrength)};
+}
+
+ScopedAStatus RadioNetwork::getAllowedNetworkTypesBitmap(int32_t serial) {
+ LOG_CALL;
+ respond()->getAllowedNetworkTypesBitmapResponse(noError(serial), mAllowedNetworkTypesBitmap);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getAvailableBandModes(int32_t serial) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioNetwork::getAvailableNetworks(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->getAvailableNetworksResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getBarringInfo(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->getBarringInfoResponse(notSupported(serial), {}, {});
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getCdmaRoamingPreference(int32_t serial) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioNetwork::getCellInfoList(int32_t serial) {
+ LOG_CALL;
+ respond()->getCellInfoListResponse(noError(serial), getCellInfoListBase());
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getImsRegistrationState(int32_t serial) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioNetwork::getNetworkSelectionMode(int32_t serial) {
+ LOG_CALL;
+ respond()->getNetworkSelectionModeResponse(noError(serial), /*manual*/ false);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getOperator(int32_t serial) {
+ LOG_CALL;
+
+ auto dataRegistrationState = mResponseTracker()->getDataRegistrationState();
+ if (!dataRegistrationState.expectOk()) {
+ respond()->getOperatorResponse(errorResponse(serial, RadioError::INTERNAL_ERR), {}, {}, {});
+ return ok();
+ }
+
+ auto opInfo = structs::getOperatorInfo(dataRegistrationState->cellIdentity);
+ respond()->getOperatorResponse(noError(serial), opInfo.alphaLong, opInfo.alphaShort,
+ opInfo.operatorNumeric);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getSystemSelectionChannels(int32_t serial) {
+ LOG_CALL_IGNORED;
+ respond()->getSystemSelectionChannelsResponse(noError(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getVoiceRadioTechnology(int32_t serial) {
+ LOG_CALL;
+ respond()->getVoiceRadioTechnologyResponse(noError(serial),
+ aidlRadio::RadioTechnology::UNKNOWN);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getVoiceRegistrationState(int32_t serial) {
+ LOG_CALL;
+ respond()->getVoiceRegistrationStateResponse(noError(serial),
+ {aidl::RegState::NOT_REG_MT_NOT_SEARCHING_OP});
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::isNrDualConnectivityEnabled(int32_t serial) {
+ // Disabled with modemReducedFeatureSet1.
+ LOG_NOT_SUPPORTED;
+ respond()->isNrDualConnectivityEnabledResponse(notSupported(serial), false);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::responseAcknowledgement() {
+ LOG_CALL_NOSERIAL;
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setAllowedNetworkTypesBitmap(int32_t serial, int32_t ntype) {
+ LOG_CALL_IGNORED << ntype;
+ mAllowedNetworkTypesBitmap = ntype;
+ respond()->setAllowedNetworkTypesBitmapResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setBandMode(int32_t serial, aidl::RadioBandMode) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioNetwork::setBarringPassword(int32_t serial, const std::string& facility,
+ const std::string& oldPw, const std::string& newPw) {
+ LOG_NOT_SUPPORTED << facility << ' ' << oldPw << ' ' << newPw;
+ respond()->setBarringPasswordResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setCdmaRoamingPreference(int32_t serial, aidl::CdmaRoamingType) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioNetwork::setCellInfoListRate(int32_t serial, int32_t rate) {
+ LOG_NOT_SUPPORTED << rate;
+ respond()->setCellInfoListRateResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setIndicationFilter(int32_t serial, int32_t indFilter) {
+ LOG_CALL_IGNORED << indFilter;
+ respond()->setIndicationFilterResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setLinkCapacityReportingCriteria( //
+ int32_t serial, int32_t hysteresisMs, int32_t hysteresisDlKbps, int32_t hysteresisUlKbps,
+ const std::vector<int32_t>& thrDownlinkKbps, const std::vector<int32_t>& thrUplinkKbps,
+ AccessNetwork accessNetwork) {
+ LOG_NOT_SUPPORTED << hysteresisMs << ' ' << hysteresisDlKbps << ' ' << hysteresisUlKbps << ' '
+ << thrDownlinkKbps << ' ' << thrUplinkKbps << ' ' << accessNetwork;
+ respond()->setLinkCapacityReportingCriteriaResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setLocationUpdates(int32_t serial, bool) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioNetwork::setNetworkSelectionModeAutomatic(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->setNetworkSelectionModeAutomaticResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setNetworkSelectionModeManual( //
+ int32_t serial, const std::string& opNumeric, AccessNetwork ran) {
+ LOG_NOT_SUPPORTED << opNumeric << ' ' << ran;
+ respond()->setNetworkSelectionModeManualResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setNrDualConnectivityState(int32_t serial,
+ aidl::NrDualConnectivityState st) {
+ // Disabled with modemReducedFeatureSet1.
+ LOG_NOT_SUPPORTED << st;
+ respond()->setNrDualConnectivityStateResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setResponseFunctions(
+ const std::shared_ptr<aidl::IRadioNetworkResponse>& response,
+ const std::shared_ptr<aidl::IRadioNetworkIndication>& indication) {
+ LOG_CALL_NOSERIAL << response << ' ' << indication;
+ CHECK(response);
+ CHECK(indication);
+ mResponseTracker = ndk::SharedRefBase::make<RadioNetworkResponseTracker>(
+ ref<aidl::IRadioNetwork>(), response);
+ respond = mResponseTracker.get();
+ indicate = indication;
+
+ indicate()->cellInfoList(RadioIndicationType::UNSOLICITED, getCellInfoListBase());
+ auto signalStrengthResponse = mResponseTracker()->getSignalStrength();
+ if (signalStrengthResponse.expectOk()) {
+ aidl::SignalStrength signalStrength = *signalStrengthResponse;
+ indicate()->currentSignalStrength(RadioIndicationType::UNSOLICITED, signalStrength);
+
+ // TODO(b/379302126): fix race condition in ServiceStateTracker which doesn't listen for
+ // EVENT_UNSOL_CELL_INFO_LIST for the first ~1.3s after setResponseFunctions
+ // TODO(b/379302126): fix race condition in SignalStrengthController, starting to listen for
+ // EVENT_SIGNAL_STRENGTH_UPDATE after ~3.7s
+ // This workaround thread would be a race condition itself (with use-after-free), but we can
+ // drop it once the two bugs mentioned above are fixed.
+ std::thread([this, signalStrength] {
+ for (int i = 0; i < 10; i++) {
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(1s);
+ indicate()->cellInfoList(RadioIndicationType::UNSOLICITED, getCellInfoListBase());
+ indicate()->currentSignalStrength(RadioIndicationType::UNSOLICITED, signalStrength);
+ }
+ }).detach();
+ }
+
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setSignalStrengthReportingCriteria(
+ int32_t serial, const std::vector<aidl::SignalThresholdInfo>& infos) {
+ LOG_CALL_IGNORED << infos;
+ respond()->setSignalStrengthReportingCriteriaResponse(
+ structs::validateSignalThresholdInfos(infos)
+ ? noError(serial)
+ : errorResponse(serial, RadioError::INVALID_ARGUMENTS));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setSuppServiceNotifications(int32_t serial, bool) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioNetwork::setSystemSelectionChannels( //
+ int32_t serial, bool specifyCh, const std::vector<aidl::RadioAccessSpecifier>& specifiers) {
+ LOG_CALL_IGNORED << specifyCh << ' ' << specifiers;
+ if (specifiers.empty()) {
+ respond()->setSystemSelectionChannelsResponse(noError(serial));
+ } else {
+ respond()->setSystemSelectionChannelsResponse(notSupported(serial));
+ }
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::startNetworkScan(int32_t serial, const aidl::NetworkScanRequest& req) {
+ LOG_NOT_SUPPORTED << req;
+ respond()->startNetworkScanResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::stopNetworkScan(int32_t serial) {
+ LOG_CALL_IGNORED;
+ respond()->stopNetworkScanResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::supplyNetworkDepersonalization(int32_t serial,
+ const std::string& nPin) {
+ LOG_NOT_SUPPORTED << nPin;
+ respond()->supplyNetworkDepersonalizationResponse(notSupported(serial), -1);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setUsageSetting(int32_t serial, aidl::UsageSetting usageSetting) {
+ LOG_CALL_IGNORED << usageSetting;
+ if (usageSetting == aidl::UsageSetting::DATA_CENTRIC) {
+ respond()->setUsageSettingResponse(noError(serial));
+ } else {
+ respond()->setUsageSettingResponse(errorResponse(serial, RadioError::INVALID_ARGUMENTS));
+ }
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getUsageSetting(int32_t serial) {
+ LOG_CALL;
+ respond()->getUsageSettingResponse(noError(serial), aidl::UsageSetting::DATA_CENTRIC);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setEmergencyMode(int32_t serial, aidl::EmergencyMode emergencyMode) {
+ LOG_NOT_SUPPORTED << emergencyMode;
+ respond()->setEmergencyModeResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::triggerEmergencyNetworkScan(
+ int32_t serial, const aidl::EmergencyNetworkScanTrigger& trigger) {
+ LOG_NOT_SUPPORTED << trigger;
+ respond()->triggerEmergencyNetworkScanResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::cancelEmergencyNetworkScan(int32_t serial, bool resetScan) {
+ LOG_NOT_SUPPORTED << resetScan;
+ respond()->cancelEmergencyNetworkScanResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::exitEmergencyMode(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->exitEmergencyModeResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setNullCipherAndIntegrityEnabled(int32_t serial, bool enabled) {
+ LOG_CALL_IGNORED << enabled;
+ respond()->setNullCipherAndIntegrityEnabledResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::isNullCipherAndIntegrityEnabled(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->isNullCipherAndIntegrityEnabledResponse(notSupported(serial), false);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::isN1ModeEnabled(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->isN1ModeEnabledResponse(notSupported(serial), false);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setN1ModeEnabled(int32_t serial, bool enable) {
+ LOG_NOT_SUPPORTED << enable;
+ respond()->setN1ModeEnabledResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::isCellularIdentifierTransparencyEnabled(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->isCellularIdentifierTransparencyEnabledResponse(notSupported(serial), false);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setCellularIdentifierTransparencyEnabled(int32_t serial, bool enabled) {
+ LOG_CALL_IGNORED << enabled;
+ respond()->setCellularIdentifierTransparencyEnabledResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::isSecurityAlgorithmsUpdatedEnabled(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->isSecurityAlgorithmsUpdatedEnabledResponse(notSupported(serial), false);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setSecurityAlgorithmsUpdatedEnabled(int32_t serial, bool enable) {
+ LOG_NOT_SUPPORTED << enable;
+ respond()->setSecurityAlgorithmsUpdatedEnabledResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setSatellitePlmn(
+ int32_t serial, const std::vector<std::string>& carrierPlmnArray,
+ const std::vector<std::string>& allSatellitePlmnArray) {
+ LOG_NOT_SUPPORTED << carrierPlmnArray << ' ' << allSatellitePlmnArray;
+ respond()->setSatellitePlmnResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::setSatelliteEnabledForCarrier(int32_t serial, bool satelliteEnabled) {
+ LOG_NOT_SUPPORTED << satelliteEnabled;
+ respond()->setSatelliteEnabledForCarrierResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::isSatelliteEnabledForCarrier(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->isSatelliteEnabledForCarrierResponse(notSupported(serial), false);
+ return ok();
+}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/network/RadioNetworkResponseTracker.cpp b/radio/aidl/minradio/libminradio/network/RadioNetworkResponseTracker.cpp
new file mode 100644
index 0000000..d3a4ad8
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/network/RadioNetworkResponseTracker.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+// see assert2_no_op in ResponseTracker.cpp
+#define __assert2 assert2_no_op
+#define __noreturn__ const
+#include <aidl/android/hardware/radio/network/BnRadioNetworkResponse.h>
+#undef __assert2
+#undef __noreturn__
+#include <cassert>
+
+#include <libminradio/network/RadioNetworkResponseTracker.h>
+
+#include <libminradio/debug.h>
+
+#define RADIO_MODULE "NetworkResponse"
+
+namespace android::hardware::radio::minimal {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::RadioResponseInfo;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::network;
+
+RadioNetworkResponseTracker::RadioNetworkResponseTracker(
+ std::shared_ptr<aidl::IRadioNetwork> req,
+ const std::shared_ptr<aidl::IRadioNetworkResponse>& resp)
+ : ResponseTracker(req, resp) {}
+
+ResponseTrackerResult<aidl::RegStateResult>
+RadioNetworkResponseTracker::getDataRegistrationState() {
+ auto serial = newSerial();
+ if (auto status = request()->getDataRegistrationState(serial); !status.isOk()) return status;
+ return getResult<aidl::RegStateResult>(serial);
+}
+
+ScopedAStatus RadioNetworkResponseTracker::getDataRegistrationStateResponse(
+ const RadioResponseInfo& info, const aidl::RegStateResult& respData) {
+ LOG_CALL_RESPONSE << respData;
+ if (isTracked(info.serial)) return handle(info, respData);
+ return IRadioNetworkResponseDelegator::getDataRegistrationStateResponse(info, respData);
+}
+
+ResponseTrackerResult<aidl::SignalStrength> RadioNetworkResponseTracker::getSignalStrength() {
+ auto serial = newSerial();
+ if (auto status = request()->getSignalStrength(serial); !status.isOk()) return status;
+ return getResult<aidl::SignalStrength>(serial);
+}
+
+ScopedAStatus RadioNetworkResponseTracker::getSignalStrengthResponse(
+ const RadioResponseInfo& info, const aidl::SignalStrength& respData) {
+ LOG_CALL_RESPONSE << respData;
+ if (isTracked(info.serial)) return handle(info, respData);
+ return IRadioNetworkResponseDelegator::getSignalStrengthResponse(info, respData);
+}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/network/structs.cpp b/radio/aidl/minradio/libminradio/network/structs.cpp
new file mode 100644
index 0000000..2366c18
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/network/structs.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#include <libminradio/network/structs.h>
+
+#include <android-base/logging.h>
+#include <libminradio/binder_printing.h>
+
+namespace android::hardware::radio::minimal::structs {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::RadioConst;
+namespace aidl = ::aidl::android::hardware::radio::network;
+
+aidl::SignalStrength makeSignalStrength() {
+ constexpr aidl::GsmSignalStrength gsm{
+ RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE,
+ };
+ constexpr aidl::LteSignalStrength lte{
+ RadioConst::VALUE_UNAVAILABLE, RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE, RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE, RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE,
+ };
+ constexpr aidl::TdscdmaSignalStrength tdscdma{
+ RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE,
+ };
+ constexpr aidl::WcdmaSignalStrength wcdma{
+ RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE,
+ };
+ constexpr aidl::NrSignalStrength nr{
+ RadioConst::VALUE_UNAVAILABLE, RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE, RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE, RadioConst::VALUE_UNAVAILABLE,
+ RadioConst::VALUE_UNAVAILABLE, {},
+ RadioConst::VALUE_UNAVAILABLE,
+ };
+
+ return {
+ .gsm = gsm,
+ .lte = lte,
+ .tdscdma = tdscdma,
+ .wcdma = wcdma,
+ .nr = nr,
+ };
+}
+
+aidl::CellInfo makeCellInfo(const aidl::RegStateResult& regState,
+ const aidl::SignalStrength& signalStrength) {
+ std::optional<aidl::CellInfoRatSpecificInfo> ratSpecificInfo;
+ auto& cellId = regState.cellIdentity;
+ switch (cellId.getTag()) {
+ case aidl::CellIdentity::Tag::noinit:
+ break;
+ case aidl::CellIdentity::Tag::gsm:
+ ratSpecificInfo = aidl::CellInfoGsm{
+ .cellIdentityGsm = cellId.get<aidl::CellIdentity::Tag::gsm>(),
+ .signalStrengthGsm = signalStrength.gsm,
+ };
+ break;
+ case aidl::CellIdentity::Tag::wcdma:
+ ratSpecificInfo = aidl::CellInfoWcdma{
+ .cellIdentityWcdma = cellId.get<aidl::CellIdentity::Tag::wcdma>(),
+ .signalStrengthWcdma = signalStrength.wcdma,
+ };
+ break;
+ case aidl::CellIdentity::Tag::tdscdma:
+ ratSpecificInfo = aidl::CellInfoTdscdma{
+ .cellIdentityTdscdma = cellId.get<aidl::CellIdentity::Tag::tdscdma>(),
+ .signalStrengthTdscdma = signalStrength.tdscdma,
+ };
+ break;
+ case aidl::CellIdentity::Tag::lte:
+ ratSpecificInfo = aidl::CellInfoLte{
+ .cellIdentityLte = cellId.get<aidl::CellIdentity::Tag::lte>(),
+ .signalStrengthLte = signalStrength.lte,
+ };
+ break;
+ case aidl::CellIdentity::Tag::nr:
+ ratSpecificInfo = aidl::CellInfoNr{
+ .cellIdentityNr = cellId.get<aidl::CellIdentity::Tag::nr>(),
+ .signalStrengthNr = signalStrength.nr,
+ };
+ break;
+ }
+ CHECK(ratSpecificInfo.has_value()) << "Cell identity not handled: " << cellId;
+
+ bool isRegistered = regState.regState == aidl::RegState::REG_HOME ||
+ regState.regState == aidl::RegState::REG_ROAMING;
+
+ return aidl::CellInfo{
+ .registered = isRegistered,
+ .connectionStatus = isRegistered ? aidl::CellConnectionStatus::PRIMARY_SERVING
+ : aidl::CellConnectionStatus::NONE,
+ .ratSpecificInfo = *ratSpecificInfo,
+ };
+}
+
+aidl::OperatorInfo getOperatorInfo(const aidl::CellIdentity& cellId) {
+ switch (cellId.getTag()) {
+ case aidl::CellIdentity::Tag::noinit:
+ return {};
+ case aidl::CellIdentity::Tag::gsm:
+ return cellId.get<aidl::CellIdentity::Tag::gsm>().operatorNames;
+ case aidl::CellIdentity::Tag::wcdma:
+ return cellId.get<aidl::CellIdentity::Tag::wcdma>().operatorNames;
+ case aidl::CellIdentity::Tag::tdscdma:
+ return cellId.get<aidl::CellIdentity::Tag::tdscdma>().operatorNames;
+ case aidl::CellIdentity::Tag::lte:
+ return cellId.get<aidl::CellIdentity::Tag::lte>().operatorNames;
+ case aidl::CellIdentity::Tag::nr:
+ return cellId.get<aidl::CellIdentity::Tag::nr>().operatorNames;
+ }
+ LOG(FATAL) << "Cell identity not handled: " << cellId;
+}
+
+int32_t rssiToSignalStrength(int32_t rssi) {
+ // 3GPP TS 27.007 8.5
+ if (rssi <= -113) return 0;
+ if (rssi >= -51) return 31;
+ if (rssi >= -1) return 99;
+ return (rssi + 113) / 2;
+}
+
+int32_t validateRsrp(int32_t rsrp) {
+ // 3GPP TS 27.007 8.69
+ if (rsrp < -140 || rsrp > -44) return RadioConst::VALUE_UNAVAILABLE;
+ return -rsrp;
+}
+
+int32_t validateRsrq(int32_t rsrq) {
+ // 3GPP TS 27.007 8.69
+ if (rsrq < -20 || rsrq > -3) return RadioConst::VALUE_UNAVAILABLE;
+ return -rsrq;
+}
+
+static bool validateSignalThresholdInfo(const aidl::SignalThresholdInfo& info) {
+ if (info.signalMeasurement <= 0) return false;
+ if (info.hysteresisMs < 0) return false;
+ if (info.hysteresisDb != 0 && info.thresholds.size() > 1) {
+ int minThreshold = info.thresholds[1] - info.thresholds[0];
+ for (size_t i = 2; i < info.thresholds.size(); i++) {
+ int delta = info.thresholds[i] - info.thresholds[i - 1];
+ if (minThreshold < delta) minThreshold = delta;
+ }
+ if (minThreshold < 0) return false;
+ if (info.hysteresisDb > minThreshold) return false;
+ }
+ return true;
+}
+
+bool validateSignalThresholdInfos(const std::vector<aidl::SignalThresholdInfo>& infos) {
+ for (auto& info : infos) {
+ if (!validateSignalThresholdInfo(info)) return false;
+ }
+ return true;
+}
+
+} // namespace android::hardware::radio::minimal::structs
diff --git a/radio/aidl/minradio/libminradio/response.cpp b/radio/aidl/minradio/libminradio/response.cpp
new file mode 100644
index 0000000..ab33a7f
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/response.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source 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.
+ */
+
+#include <libminradio/response.h>
+
+namespace android::hardware::radio::minimal {
+
+namespace aidl = ::aidl::android::hardware::radio;
+
+aidl::RadioResponseInfo noError(int32_t serial) {
+ return {
+ .type = aidl::RadioResponseType::SOLICITED,
+ .serial = serial,
+ .error = aidl::RadioError::NONE,
+ };
+}
+
+aidl::RadioResponseInfo notSupported(int32_t serial) {
+ return {
+ .type = aidl::RadioResponseType::SOLICITED,
+ .serial = serial,
+ .error = aidl::RadioError::REQUEST_NOT_SUPPORTED,
+ };
+}
+
+aidl::RadioResponseInfo errorResponse(int32_t serial, aidl::RadioError error) {
+ return {
+ .type = aidl::RadioResponseType::SOLICITED,
+ .serial = serial,
+ .error = error,
+ };
+}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/sim/App.cpp b/radio/aidl/minradio/libminradio/sim/App.cpp
new file mode 100644
index 0000000..8007769
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/App.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/sim/App.h>
+
+#include <android-base/logging.h>
+#include <libminradio/sim/IccConstants.h>
+#include <libminradio/sim/IccUtils.h>
+
+namespace android::hardware::radio::minimal::sim {
+
+using namespace ::android::hardware::radio::minimal::sim::constants;
+namespace aidl = ::aidl::android::hardware::radio::sim;
+
+App::App(std::string_view aid) : mAid(aid) {}
+
+std::string_view App::getAid() const {
+ return mAid;
+}
+
+App::Channel::Channel(uint8_t channelId) : mChannelId(channelId) {}
+
+uint8_t App::Channel::getId() const {
+ return mChannelId;
+}
+
+std::vector<uint8_t> App::Channel::getSelectResponse() const {
+ return {IO_RESULT_SUCCESS >> 8, IO_RESULT_SUCCESS & 0xFF};
+}
+
+aidl::IccIoResult App::iccIo(const aidl::IccIo&) {
+ return toIccIoResult(IO_RESULT_NOT_SUPPORTED);
+}
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/sim/AppManager.cpp b/radio/aidl/minradio/libminradio/sim/AppManager.cpp
new file mode 100644
index 0000000..fe7d7bc
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/AppManager.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/sim/AppManager.h>
+
+#include <aidl/android/hardware/radio/RadioConst.h>
+#include <android-base/logging.h>
+#include <libminradio/binder_printing.h>
+#include <libminradio/sim/IccConstants.h>
+#include <libminradio/sim/IccUtils.h>
+#include <libminradio/sim/apps/FilesystemApp.h>
+
+#include <set>
+
+namespace android::hardware::radio::minimal::sim {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using namespace ::android::hardware::radio::minimal::sim::constants;
+using ::aidl::android::hardware::radio::RadioConst;
+using ::aidl::android::hardware::radio::RadioError;
+namespace aidl = ::aidl::android::hardware::radio::sim;
+
+// ETSI TS 102 221 10.1.2 (table 10.5)
+static std::map<uint8_t, std::set<uint8_t>> mCommandClasses = {
+ {COMMAND_READ_BINARY, {0}}, //
+ {COMMAND_UPDATE_BINARY, {0}}, //
+ {COMMAND_READ_RECORD, {0}}, //
+ {COMMAND_UPDATE_RECORD, {0}}, //
+ {COMMAND_SEEK, {0}}, //
+ {COMMAND_SELECT, {0}}, //
+ {COMMAND_GET_RESPONSE, {0}}, //
+ {COMMAND_STATUS, {0x80, 0x81, 0x82, 0x83}}, //
+ {COMMAND_GET_DATA, {0x80}}, //
+ {COMMAND_MANAGE_CHANNEL, {0}}, //
+};
+
+static constexpr uint8_t MANAGE_CHANNEL_OPEN = 0x00;
+static constexpr uint8_t MANAGE_CHANNEL_CLOSE = 0x80;
+
+AppManager::AppManager() {}
+
+void AppManager::addApp(std::shared_ptr<App> app) {
+ mApps[std::string{app->getAid()}] = app;
+
+ // Channel 0 is always available per 3GPP TS 102 221 11.1.17
+ if (app->getAid() == apps::FilesystemApp::AID) {
+ std::unique_lock lck(mChannelsGuard);
+ mChannels[0] = app->newChannel(0);
+ }
+}
+
+std::pair<RadioError, std::shared_ptr<App::Channel>> AppManager::openLogicalChannel(
+ std::string_view aid, int32_t p2) {
+ auto appIt = mApps.find(aid);
+ if (appIt == mApps.end()) {
+ LOG(WARNING) << "App " << aid << " not found";
+ return {RadioError::NO_SUCH_ELEMENT, nullptr};
+ }
+
+ // ETSI TS 102 221 11.1.1.2 Table 11.2
+ // P2 == 0x00: Application activation / reset; First or only occurrence
+ // 0x0C: No data returned
+ if (p2 != 0x00 && p2 != 0x0C && p2 != RadioConst::P2_CONSTANT_NO_P2) {
+ LOG(ERROR) << "P2 != 0x00 or 0x0C not supported";
+ return {RadioError::INVALID_ARGUMENTS, nullptr};
+ }
+
+ std::unique_lock lck(mChannelsGuard);
+
+ // Find available channel. It must be in 1-3 range per 3GPP TS 102 221 11.1.17.1
+ std::optional<unsigned> channelId;
+ for (uint8_t i = 1; i <= 3; i++) {
+ if (mChannels.find(i) == mChannels.end()) {
+ channelId = i;
+ break;
+ }
+ }
+ if (!channelId.has_value()) {
+ LOG(ERROR) << "AppManager: All channels are busy";
+ return {RadioError::MISSING_RESOURCE, nullptr};
+ }
+
+ auto channel = appIt->second->newChannel(*channelId);
+ mChannels[*channelId] = channel;
+ LOG(DEBUG) << "AppManager: opened logical channel " << *channelId;
+ return {RadioError::NONE, std::move(channel)};
+}
+
+RadioError AppManager::closeLogicalChannel(int32_t channelId) {
+ if (channelId == 0) {
+ // 3GPP TS 102 221 11.1.17: channel 0 is guaranteed to be always available
+ return RadioError::INVALID_ARGUMENTS;
+ }
+
+ std::unique_lock lck(mChannelsGuard);
+ auto it = mChannels.find(channelId);
+ if (it == mChannels.end()) {
+ return RadioError::MISSING_RESOURCE;
+ }
+ mChannels.erase(it);
+ LOG(DEBUG) << "AppManager: closed logical channel " << channelId;
+ return RadioError::NONE;
+}
+
+aidl::IccIoResult AppManager::transmit(const aidl::SimApdu& message) {
+ // Fetch channel
+ std::shared_ptr<App::Channel> channel;
+ {
+ std::unique_lock lck(mChannelsGuard);
+ auto chIt = mChannels.find(message.sessionId);
+ if (chIt == mChannels.end()) {
+ return toIccIoResult(IO_RESULT_CHANNEL_NOT_SUPPORTED);
+ }
+ channel = chIt->second;
+ }
+
+ // Verify instruction matching command class
+ auto classIt = mCommandClasses.find(message.instruction);
+ if (classIt == mCommandClasses.end()) {
+ LOG(ERROR) << "Command not found for " << message;
+ return toIccIoResult(IO_RESULT_NOT_SUPPORTED);
+ }
+ if (!classIt->second.contains(message.cla)) {
+ LOG(ERROR) << "Unsupported command class: " << message;
+ return toIccIoResult(IO_RESULT_CLASS_NOT_SUPPORTED);
+ }
+
+ switch (message.instruction) {
+ case COMMAND_MANAGE_CHANNEL:
+ return commandManageChannel(message.p1, message.p2);
+ default:
+ // Pass the message to the channel
+ return channel->transmit(message);
+ }
+}
+
+aidl::IccIoResult AppManager::iccIo(const aidl::IccIo& iccIo) {
+ auto appIt = mApps.find(iccIo.aid);
+ if (appIt == mApps.end()) {
+ LOG(WARNING) << "App " << iccIo.aid << " not found";
+ return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
+ }
+
+ return appIt->second->iccIo(iccIo);
+}
+
+// ISO 7816 7.1.2
+aidl::IccIoResult AppManager::commandManageChannel(int32_t operation, int32_t channelId) {
+ if (operation == MANAGE_CHANNEL_OPEN) {
+ if (channelId != 0) {
+ LOG(ERROR) << "Not implemented: opening explicit channel IDs: " << channelId;
+ return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
+ }
+ auto [status, channel] = openLogicalChannel("", 0);
+ if (channel) {
+ return toIccIoResult(uint8ToBytes(channel->getId()));
+ } else {
+ return toIccIoResult(IO_RESULT_CHANNEL_NOT_SUPPORTED);
+ }
+ } else if (operation == MANAGE_CHANNEL_CLOSE) {
+ auto status = closeLogicalChannel(channelId);
+ if (status == RadioError::NONE) {
+ return toIccIoResult("");
+ }
+ return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
+ } else {
+ LOG(ERROR) << "Invalid MANAGE_CHANNEL operation: " << operation;
+ return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
+ }
+}
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/sim/Filesystem.cpp b/radio/aidl/minradio/libminradio/sim/Filesystem.cpp
new file mode 100644
index 0000000..4be63f4
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/Filesystem.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/sim/Filesystem.h>
+
+#include <libminradio/sim/IccConstants.h>
+#include <libminradio/sim/IccUtils.h>
+
+#include <format>
+
+namespace android::hardware::radio::minimal::sim {
+
+using namespace ::android::hardware::radio::minimal::sim::constants;
+using FileView = Filesystem::FileView;
+
+namespace paths {
+
+// 3GPP TS 51.011 10.7
+const Filesystem::Path mf{MF_SIM_VAL, ""};
+const Filesystem::Path fplmn{EF_FPLMN, MF_SIM + DF_ADF};
+const Filesystem::Path iccid{EF_ICCID, MF_SIM};
+const Filesystem::Path msisdn{EF_MSISDN, MF_SIM + DF_ADF};
+const Filesystem::Path pl{EF_PL, MF_SIM};
+const Filesystem::Path arr{EF_ARR, MF_SIM};
+const Filesystem::Path ad{EF_AD, MF_SIM + DF_ADF};
+
+} // namespace paths
+
+Filesystem::Filesystem() {
+ write(paths::mf, ""); // Directories are not implemented.
+ write(paths::arr, "");
+}
+
+void Filesystem::write(const Path& path, FileView contents) {
+ std::unique_lock lck(mFilesGuard);
+ mFiles[path].assign(contents.begin(), contents.end()); // C++23: assign_range
+ mUpdates.insert(path.fileId);
+}
+
+void Filesystem::write(const Path& path, std::string_view contents) {
+ std::unique_lock lck(mFilesGuard);
+ mFiles[path].assign(contents.begin(), contents.end()); // C++23: assign_range
+ mUpdates.insert(path.fileId);
+}
+
+void Filesystem::write(const Path& path, std::vector<uint8_t>&& contents) {
+ write(path, FileView(contents));
+}
+
+std::optional<FileView> Filesystem::read(const Path& path) const {
+ std::unique_lock lck(mFilesGuard);
+ auto it = mFiles.find(path);
+ if (it == mFiles.end()) return std::nullopt;
+
+ return FileView(it->second);
+}
+
+void Filesystem::writeBch(const Path& path, std::string_view contents) {
+ write(path, hexStringToBch(contents));
+}
+
+std::optional<std::string> Filesystem::readBch(const Path& path) const {
+ auto contents = read(path);
+ if (!contents.has_value()) return std::nullopt;
+ return bchToHexString(*contents);
+}
+
+std::optional<Filesystem::Path> Filesystem::find(uint16_t fileId) {
+ std::unique_lock lck(mFilesGuard);
+ for (auto& [path, content] : mFiles) {
+ if (path.fileId == fileId) return path;
+ }
+ return std::nullopt;
+}
+
+std::set<int32_t> Filesystem::fetchAndClearUpdates() {
+ std::unique_lock lck(mFilesGuard);
+ std::set<int32_t> result;
+ std::swap(result, mUpdates);
+ return result;
+}
+
+std::string Filesystem::Path::toString() const {
+ return std::format("{:s}/{:X}", pathId, fileId);
+}
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/sim/IccUtils.cpp b/radio/aidl/minradio/libminradio/sim/IccUtils.cpp
new file mode 100644
index 0000000..9458760
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/IccUtils.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+// C++ reimplementation of f/b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+
+#include <libminradio/sim/IccUtils.h>
+
+#include <android-base/logging.h>
+#include <libminradio/sim/IccConstants.h>
+
+namespace android::hardware::radio::minimal::sim {
+
+using namespace ::android::hardware::radio::minimal::sim::constants;
+namespace aidl = ::aidl::android::hardware::radio::sim;
+
+// frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/AdnRecord.java
+// 3GPP TS 31.102 4.2.26
+constexpr int ADN_FOOTER_SIZE_BYTES = 14;
+constexpr uint8_t ADN_UNUSED = 0xFF;
+constexpr int ADN_BCD_NUMBER_LENGTH = 0;
+constexpr int ADN_TON_AND_NPI = 1;
+constexpr int ADN_DIALING_NUMBER_START = 2;
+constexpr int ADN_DIALING_NUMBER_END = 11;
+
+// com.android.internal.telephony.uicc.IccUtils.charToByte
+// com.android.internal.telephony.uicc.IccUtils.hexCharToInt
+static uint8_t charToByte(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+ if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ }
+ if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ }
+ LOG(FATAL) << "IccUtils.charToByte: invalid hex character: " << static_cast<int>(c);
+ return 0;
+}
+
+static constexpr char kHexChars[] = "0123456789ABCDEF";
+
+static aidl::IccIoResult toIccIoResult(uint16_t errorCode, std::string_view simResponse) {
+ return {
+ .sw1 = errorCode >> 8,
+ .sw2 = errorCode & 0xFF,
+ .simResponse = std::string(simResponse),
+ };
+}
+
+aidl::IccIoResult toIccIoResult(std::span<uint8_t const> bytes) {
+ return toIccIoResult(IO_RESULT_SUCCESS, sim::bytesToHexString(bytes));
+}
+
+aidl::IccIoResult toIccIoResult(std::vector<uint8_t>&& bytes) {
+ return toIccIoResult(IO_RESULT_SUCCESS, sim::bytesToHexString(bytes));
+}
+
+aidl::IccIoResult toIccIoResult(std::string_view simResponse) {
+ return toIccIoResult(IO_RESULT_SUCCESS, simResponse);
+}
+
+aidl::IccIoResult toIccIoResult(uint16_t errorCode) {
+ return toIccIoResult(errorCode, "");
+}
+
+// com.android.internal.telephony.uicc.IccUtils.hexStringToBytes
+std::vector<uint8_t> hexStringToBytes(std::string_view str) {
+ CHECK(str.size() % 2 == 0) << "Hex string length not even";
+ std::vector<uint8_t> bytes(str.size() / 2);
+ for (size_t i = 0; i < bytes.size(); i++) {
+ bytes[i] = charToByte(str[i * 2]) << 4 | charToByte(str[i * 2 + 1]);
+ }
+ return bytes;
+}
+
+// com.android.internal.telephony.uicc.IccUtils.bchToString (inversion)
+// NOTE: BCH is a nibble-swizzled bytes reprezentation
+std::vector<uint8_t> hexStringToBch(std::string_view str) {
+ CHECK(str.size() % 2 == 0) << "Hex string length not even";
+ std::vector<uint8_t> bch(str.size() / 2);
+ for (size_t i = 0; i < bch.size(); i++) {
+ bch[i] = charToByte(str[i * 2]) | charToByte(str[i * 2 + 1]) << 4;
+ }
+ return bch;
+}
+
+// com.android.internal.telephony.uicc.IccUtils.bytesToHexString
+std::string bytesToHexString(std::span<uint8_t const> bytes) {
+ std::string ret(bytes.size() * 2, '0');
+ for (size_t i = 0; i < bytes.size(); i++) {
+ ret[i * 2 + 0] = kHexChars[0x0F & (bytes[i] >> 4)];
+ ret[i * 2 + 1] = kHexChars[0x0F & (bytes[i])];
+ }
+ return ret;
+}
+
+std::string bytesToHexString(std::vector<uint8_t>&& bytes) {
+ std::span<uint8_t> bytesSpan(bytes);
+ return bytesToHexString(bytesSpan);
+}
+
+// com.android.internal.telephony.uicc.IccUtils.bchToString
+std::string bchToHexString(std::span<uint8_t const> bytes) {
+ std::string ret(bytes.size() * 2, '0');
+ for (size_t i = 0; i < bytes.size(); i++) {
+ ret[i * 2 + 0] = kHexChars[0x0F & (bytes[i])];
+ ret[i * 2 + 1] = kHexChars[0x0F & (bytes[i] >> 4)];
+ }
+ return ret;
+}
+
+std::vector<uint8_t> uint8ToBytes(uint8_t val) {
+ return {val};
+}
+
+std::vector<uint8_t> uint16ToBytes(uint16_t val) {
+ return {
+ static_cast<uint8_t>(val >> 8),
+ static_cast<uint8_t>(val & 0xFF),
+ };
+}
+
+// com.android.internal.telephony.uicc.IccUtils.bcdToString (inversion)
+// integerString is a number with possible leading zeros
+static std::vector<uint8_t> stringToBcd(std::string_view intString) {
+ // Note: 3GPP TS 31.102 Table 4.4 describes BCD coding for characters * and # (not implemented)
+ bool isOdd = intString.size() % 2 == 1;
+ std::vector<uint8_t> ret(intString.size() / 2 + (isOdd ? 1 : 0), 0);
+ for (size_t i = 0; i < intString.size(); i++) {
+ const char digitC = intString[i];
+ CHECK(digitC >= '0' && digitC <= '9') << "Invalid numeric string: " << intString;
+ uint8_t digit = digitC - '0';
+
+ if (i % 2 == 1) digit <<= 4;
+ ret[i / 2] |= digit;
+ }
+ if (isOdd) {
+ *ret.rbegin() |= 0xF0;
+ }
+ return ret;
+}
+
+// com.android.internal.telephony.uicc.IccUtils.stringToBcdPlmn
+static void stringToBcdPlmn(std::string_view plmn, std::vector<uint8_t>& data, size_t offset) {
+ char digit6 = plmn.length() > 5 ? plmn[5] : 'F';
+ data[offset] = (charToByte(plmn[1]) << 4) | charToByte(plmn[0]);
+ data[offset + 1] = (charToByte(digit6) << 4) | charToByte(plmn[2]);
+ data[offset + 2] = (charToByte(plmn[4]) << 4) | charToByte(plmn[3]);
+}
+
+// com.android.internal.telephony.uicc.IccUtils.encodeFplmns
+std::vector<uint8_t> encodeFplmns(std::span<std::string_view> fplmns) {
+ // 3GPP TS 31.102 4.2.16
+ auto recordsCount = std::max<size_t>(fplmns.size(), 4);
+ std::vector<uint8_t> serializedFplmns(recordsCount * FPLMN_BYTE_SIZE, 0xFF);
+
+ size_t record = 0;
+ for (auto&& fplmn : fplmns) {
+ stringToBcdPlmn(fplmn, serializedFplmns, FPLMN_BYTE_SIZE * record++);
+ }
+ return serializedFplmns;
+}
+
+std::vector<uint8_t> encodeMsisdn(std::string_view phoneNumber) {
+ // 3GPP TS 31.102 4.2.26
+ std::vector<uint8_t> msisdn(ADN_FOOTER_SIZE_BYTES, ADN_UNUSED);
+ bool isInternational = phoneNumber.size() >= 1 && phoneNumber[0] == '+';
+ if (isInternational) phoneNumber = phoneNumber.substr(1);
+
+ auto encodedNumber = stringToBcd(phoneNumber);
+ constexpr int numberMaxSize = ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1;
+ if (encodedNumber.size() > numberMaxSize) {
+ encodedNumber.resize(numberMaxSize);
+ }
+
+ msisdn[ADN_BCD_NUMBER_LENGTH] = 1 + encodedNumber.size();
+
+ // 3GPP TS 24.008 Table 10.5.91:
+ // 0b1xxxxxx - mandatory bit
+ // ton (type of number):
+ // - 0bx001xxxx - international number (with +)
+ // - 0bx010xxxx - national number
+ // npi (numbering plan identification):
+ // - 0bxxxx0001 - ISDN/telephony numbering plan
+ msisdn[ADN_TON_AND_NPI] = isInternational ? 0b10010001 : 0b10100001;
+
+ std::copy(encodedNumber.begin(), encodedNumber.end(),
+ std::next(msisdn.begin(), ADN_DIALING_NUMBER_START));
+
+ return msisdn;
+}
+
+std::vector<uint8_t> encodeAd(uint8_t mncLength) {
+ // ETSI TS 131 102 4.2.18
+ CHECK(mncLength == 2 || mncLength == 3) << "Invalid MNC length: " << mncLength;
+
+ std::vector<uint8_t> ad(4);
+ ad[3] = mncLength;
+ return ad;
+}
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/sim/RadioSim.cpp b/radio/aidl/minradio/libminradio/sim/RadioSim.cpp
new file mode 100644
index 0000000..0365a88
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/RadioSim.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/sim/RadioSim.h>
+
+#include <libminradio/debug.h>
+#include <libminradio/response.h>
+#include <libminradio/sim/IccUtils.h>
+#include <libminradio/sim/apps/AraM.h>
+#include <libminradio/sim/apps/FilesystemApp.h>
+
+#define RADIO_MODULE "Sim"
+
+namespace android::hardware::radio::minimal {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::RadioError;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::sim;
+constexpr auto ok = &ScopedAStatus::ok;
+
+RadioSim::RadioSim(std::shared_ptr<SlotContext> context) : RadioSlotBase(context) {
+ mAppManager.addApp(std::make_shared<sim::apps::FilesystemApp>(mFilesystem));
+
+ mFilesystem->write(sim::paths::fplmn, sim::encodeFplmns({}));
+ mFilesystem->write(sim::paths::pl, "en");
+}
+
+void RadioSim::setIccid(std::string iccid) {
+ mFilesystem->writeBch(sim::paths::iccid, iccid);
+}
+
+std::optional<std::string> RadioSim::getIccid() const {
+ return mFilesystem->readBch(sim::paths::iccid);
+}
+
+void RadioSim::addCtsCertificate() {
+ static constexpr char CTS_UICC_2021[] =
+ "CE7B2B47AE2B7552C8F92CC29124279883041FB623A5F194A82C9BF15D492AA0";
+
+ auto aram = std::make_shared<sim::apps::AraM>();
+ mAppManager.addApp(aram);
+ aram->addRule({
+ .deviceAppID = sim::hexStringToBytes(CTS_UICC_2021),
+ .pkg = "android.carrierapi.cts",
+ });
+}
+
+ScopedAStatus RadioSim::areUiccApplicationsEnabled(int32_t serial) {
+ LOG_CALL;
+ respond()->areUiccApplicationsEnabledResponse(noError(serial), mAreUiccApplicationsEnabled);
+ return ok();
+}
+
+ScopedAStatus RadioSim::changeIccPin2ForApp(int32_t serial, const std::string& oldPin2,
+ const std::string& newPin2, const std::string& aid) {
+ LOG_NOT_SUPPORTED << oldPin2 << ' ' << newPin2 << ' ' << aid;
+ respond()->changeIccPin2ForAppResponse(notSupported(serial), -1);
+ return ok();
+}
+
+ScopedAStatus RadioSim::changeIccPinForApp(int32_t serial, const std::string& oldPin,
+ const std::string& newPin, const std::string& aid) {
+ LOG_NOT_SUPPORTED << oldPin << ' ' << newPin << ' ' << aid;
+ respond()->changeIccPinForAppResponse(notSupported(serial), -1);
+ return ok();
+}
+
+ScopedAStatus RadioSim::enableUiccApplications(int32_t serial, bool enable) {
+ LOG_CALL_IGNORED << enable;
+ mAreUiccApplicationsEnabled = enable;
+ respond()->enableUiccApplicationsResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioSim::getAllowedCarriers(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->getAllowedCarriersResponse(notSupported(serial), {}, {});
+ return ok();
+}
+
+ScopedAStatus RadioSim::getCdmaSubscription(int32_t serial) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioSim::getCdmaSubscriptionSource(int32_t serial) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioSim::getFacilityLockForApp( //
+ int32_t serial, const std::string& facility, const std::string& password,
+ int32_t serviceClass, const std::string& appId) {
+ LOG_CALL << facility << ' ' << password << ' ' << serviceClass << ' ' << appId;
+ respond()->getFacilityLockForAppResponse(noError(serial), 0); // 0 means "disabled for all"
+ return ok();
+}
+
+ScopedAStatus RadioSim::getSimPhonebookCapacity(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->getSimPhonebookCapacityResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioSim::getSimPhonebookRecords(int32_t serial) {
+ LOG_NOT_SUPPORTED;
+ respond()->getSimPhonebookRecordsResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioSim::iccCloseLogicalChannel(int32_t serial, int32_t) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioSim::iccCloseLogicalChannelWithSessionInfo(
+ int32_t serial, const aidl::SessionInfo& sessionInfo) {
+ LOG_CALL << sessionInfo;
+ auto status = mAppManager.closeLogicalChannel(sessionInfo.sessionId);
+ respond()->iccCloseLogicalChannelWithSessionInfoResponse(errorResponse(serial, status));
+ return ok();
+}
+
+ScopedAStatus RadioSim::iccIoForApp(int32_t serial, const aidl::IccIo& iccIo) {
+ LOG_CALL << iccIo;
+ respond()->iccIoForAppResponse(noError(serial), mAppManager.iccIo(iccIo));
+ return ok();
+}
+
+ScopedAStatus RadioSim::iccOpenLogicalChannel(int32_t serial, const std::string& aid, int32_t p2) {
+ LOG_CALL << aid << ' ' << p2;
+ auto [status, channel] = mAppManager.openLogicalChannel(aid, p2);
+ respond()->iccOpenLogicalChannelResponse(
+ errorResponse(serial, status), channel ? channel->getId() : 0,
+ channel ? channel->getSelectResponse() : std::vector<uint8_t>{});
+ return ok();
+}
+
+ScopedAStatus RadioSim::iccTransmitApduBasicChannel(int32_t serial, const aidl::SimApdu& message) {
+ LOG_CALL << message;
+ if (message.sessionId != 0) {
+ LOG(ERROR) << "Basic channel session ID should be zero, but was " << message.sessionId;
+ respond()->iccTransmitApduBasicChannelResponse(
+ errorResponse(serial, RadioError::INVALID_ARGUMENTS), {});
+ return ok();
+ }
+ respond()->iccTransmitApduBasicChannelResponse(noError(serial), mAppManager.transmit(message));
+ return ok();
+}
+
+ScopedAStatus RadioSim::iccTransmitApduLogicalChannel(int32_t serial,
+ const aidl::SimApdu& message) {
+ LOG_CALL << message;
+ respond()->iccTransmitApduLogicalChannelResponse(noError(serial),
+ mAppManager.transmit(message));
+ return ok();
+}
+
+ScopedAStatus RadioSim::reportStkServiceIsRunning(int32_t serial) {
+ LOG_CALL_IGNORED;
+ respond()->reportStkServiceIsRunningResponse(noError(serial));
+ return ok();
+}
+
+ScopedAStatus RadioSim::requestIccSimAuthentication( //
+ int32_t serial, int32_t authContext, const std::string& authData, const std::string& aid) {
+ LOG_NOT_SUPPORTED << authContext << ' ' << authData << ' ' << aid;
+ respond()->requestIccSimAuthenticationResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioSim::responseAcknowledgement() {
+ LOG_CALL_NOSERIAL;
+ return ok();
+}
+
+ScopedAStatus RadioSim::sendEnvelope(int32_t serial, const std::string& command) {
+ LOG_NOT_SUPPORTED << command;
+ respond()->sendEnvelopeResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioSim::sendEnvelopeWithStatus(int32_t serial, const std::string& contents) {
+ LOG_NOT_SUPPORTED << contents;
+ respond()->sendEnvelopeWithStatusResponse(notSupported(serial), {});
+ return ok();
+}
+
+ScopedAStatus RadioSim::sendTerminalResponseToSim(int32_t serial,
+ const std::string& commandResponse) {
+ LOG_NOT_SUPPORTED << commandResponse;
+ respond()->sendTerminalResponseToSimResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioSim::setAllowedCarriers( //
+ int32_t serial, const aidl::CarrierRestrictions& carriers, aidl::SimLockMultiSimPolicy mp) {
+ LOG_NOT_SUPPORTED << carriers << ' ' << mp;
+ respond()->setAllowedCarriersResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioSim::setCarrierInfoForImsiEncryption(
+ int32_t serial, const aidl::ImsiEncryptionInfo& imsiEncryptionInfo) {
+ LOG_NOT_SUPPORTED << imsiEncryptionInfo;
+ respond()->setCarrierInfoForImsiEncryptionResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioSim::setCdmaSubscriptionSource(int32_t serial, aidl::CdmaSubscriptionSource) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioSim::setFacilityLockForApp( //
+ int32_t serial, const std::string& facility, bool lockState, const std::string& password,
+ int32_t serviceClass, const std::string& appId) {
+ LOG_NOT_SUPPORTED << facility << ' ' << lockState << ' ' << password << ' ' << serviceClass
+ << ' ' << appId;
+ respond()->setFacilityLockForAppResponse(notSupported(serial), -1);
+ return ok();
+}
+
+ScopedAStatus RadioSim::setResponseFunctions(
+ const std::shared_ptr<aidl::IRadioSimResponse>& response,
+ const std::shared_ptr<aidl::IRadioSimIndication>& indication) {
+ LOG_CALL_NOSERIAL << response << ' ' << indication;
+ CHECK(response);
+ CHECK(indication);
+ respond = response;
+ indicate = indication;
+ return ok();
+}
+
+ScopedAStatus RadioSim::setSimCardPower(int32_t serial, aidl::CardPowerState powerUp) {
+ LOG_NOT_SUPPORTED << powerUp;
+ respond()->setSimCardPowerResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioSim::setUiccSubscription(int32_t serial, const aidl::SelectUiccSub&) {
+ LOG_AND_RETURN_DEPRECATED();
+}
+
+ScopedAStatus RadioSim::supplyIccPin2ForApp(int32_t serial, const std::string& pin2,
+ const std::string& aid) {
+ LOG_NOT_SUPPORTED << pin2 << ' ' << aid;
+ respond()->supplyIccPin2ForAppResponse(notSupported(serial), -1);
+ return ok();
+}
+
+ScopedAStatus RadioSim::supplyIccPinForApp(int32_t serial, const std::string& pin,
+ const std::string& aid) {
+ LOG_CALL << "string[" << pin.size() << "] " << aid
+ << " (should not be called with PinState::DISABLED)";
+ respond()->supplyIccPinForAppResponse(notSupported(serial), -1);
+ return ok();
+}
+
+ScopedAStatus RadioSim::supplyIccPuk2ForApp(int32_t serial, const std::string& puk2,
+ const std::string& pin2, const std::string& aid) {
+ LOG_NOT_SUPPORTED << puk2 << ' ' << pin2 << ' ' << aid;
+ respond()->supplyIccPuk2ForAppResponse(notSupported(serial), -1);
+ return ok();
+}
+
+ScopedAStatus RadioSim::supplyIccPukForApp(int32_t serial, const std::string& puk,
+ const std::string& pin, const std::string& aid) {
+ LOG_NOT_SUPPORTED << puk << ' ' << pin << ' ' << aid;
+ respond()->supplyIccPukForAppResponse(notSupported(serial), -1);
+ return ok();
+}
+
+ScopedAStatus RadioSim::supplySimDepersonalization(int32_t serial, aidl::PersoSubstate pss,
+ const std::string& controlKey) {
+ LOG_NOT_SUPPORTED << pss << ' ' << controlKey;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus RadioSim::updateSimPhonebookRecords(int32_t serial,
+ const aidl::PhonebookRecordInfo& recordInfo) {
+ LOG_NOT_SUPPORTED << recordInfo;
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+} // namespace android::hardware::radio::minimal
diff --git a/radio/aidl/minradio/libminradio/sim/apps/AraM.cpp b/radio/aidl/minradio/libminradio/sim/apps/AraM.cpp
new file mode 100644
index 0000000..7aa1439
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/apps/AraM.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/sim/apps/AraM.h>
+
+#include "tlv.h"
+
+#include <android-base/logging.h>
+#include <libminradio/binder_printing.h>
+#include <libminradio/sim/IccConstants.h>
+#include <libminradio/sim/IccUtils.h>
+
+namespace android::hardware::radio::minimal::sim::apps {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using namespace ::android::hardware::radio::minimal::sim::constants;
+using namespace ::android::hardware::radio::minimal::sim::tlv_operators;
+namespace aidl = ::aidl::android::hardware::radio::sim;
+
+// From https://source.android.com/docs/core/connect/uicc
+static constexpr uint16_t TAG_ALL_REF_AR_DO = 0xFF40;
+static constexpr uint8_t TAG_REF_AR_DO = 0xE2;
+static constexpr uint8_t TAG_REF_DO = 0xE1;
+static constexpr uint8_t TAG_DEVICE_APP_ID_REF_DO = 0xC1;
+static constexpr uint8_t TAG_PKG_REF_DO = 0xCA;
+static constexpr uint8_t TAG_AR_DO = 0xE3;
+static constexpr uint8_t TAG_PERM_AR_DO = 0xDB;
+
+class AraMChannel : public App::Channel {
+ public:
+ AraMChannel(int32_t channelId, std::shared_ptr<AraM> app);
+
+ aidl::IccIoResult transmit(const aidl::SimApdu& message) override;
+
+ private:
+ std::weak_ptr<AraM> mApp;
+};
+
+AraM::AraM() : App(AID) {}
+
+std::shared_ptr<App::Channel> AraM::newChannel(int32_t id) {
+ return std::make_shared<AraMChannel>(id, shared_from_this());
+}
+
+void AraM::addRule(Rule rule) {
+ mRules.push_back(rule);
+}
+
+std::span<const AraM::Rule> AraM::getRules() const {
+ return mRules;
+}
+
+AraMChannel::AraMChannel(int32_t channelId, std::shared_ptr<AraM> app)
+ : App::Channel(channelId), mApp(app) {}
+
+aidl::IccIoResult AraMChannel::transmit(const aidl::SimApdu& message) {
+ auto app = mApp.lock();
+ if (!app) {
+ LOG(ERROR) << "AraM: App shut down, channel not valid anymore.";
+ return toIccIoResult(IO_RESULT_TECHNICAL_PROBLEM);
+ }
+ if (message.instruction != COMMAND_GET_DATA) {
+ LOG(ERROR) << "AraM: Unsupported instruction: " << message;
+ return toIccIoResult(IO_RESULT_NOT_SUPPORTED);
+ }
+ if (message.p1 != (TAG_ALL_REF_AR_DO >> 8) || message.p2 != (TAG_ALL_REF_AR_DO & 0xFF)) {
+ LOG(ERROR) << "AraM: Incorrect parameters: " << std::hex << message.p1 << message.p2;
+ return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
+ }
+ if (message.p3 != 0) {
+ return toIccIoResult(IO_RESULT_INCORRECT_LENGTH | 0);
+ }
+
+ std::vector<uint8_t> rules;
+ for (auto& rule : app->getRules()) {
+ // Encoding rules as described in https://source.android.com/docs/core/connect/uicc
+ // clang-format off
+ rules = rules + makeTlv(TAG_REF_AR_DO,
+ makeTlv(TAG_REF_DO,
+ makeTlv(TAG_DEVICE_APP_ID_REF_DO, rule.deviceAppID) +
+ makeTlv(TAG_PKG_REF_DO, std::vector<uint8_t>(rule.pkg.begin(), rule.pkg.end()))
+ ) +
+ makeTlv(TAG_AR_DO,
+ makeTlv(TAG_PERM_AR_DO, std::vector<uint8_t>{0, 0, 0, 0, 0, 0, 0, 1})
+ )
+ );
+ // clang-format on
+ }
+
+ return toIccIoResult(bytesToHexString(makeTlv(TAG_ALL_REF_AR_DO, rules)));
+}
+
+} // namespace android::hardware::radio::minimal::sim::apps
diff --git a/radio/aidl/minradio/libminradio/sim/apps/FilesystemApp.cpp b/radio/aidl/minradio/libminradio/sim/apps/FilesystemApp.cpp
new file mode 100644
index 0000000..0a32e6c
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/apps/FilesystemApp.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include <libminradio/sim/apps/FilesystemApp.h>
+
+#include "tlv.h"
+
+#include <android-base/logging.h>
+#include <libminradio/binder_printing.h>
+#include <libminradio/sim/IccConstants.h>
+#include <libminradio/sim/IccUtils.h>
+
+#include <unordered_set>
+
+namespace android::hardware::radio::minimal::sim::apps {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using namespace ::android::hardware::radio::minimal::sim::constants;
+namespace aidl = ::aidl::android::hardware::radio::sim;
+
+// ETSI TS 102 221 11.1.1.2 Table 11.1: Coding of P1 for SELECT
+static constexpr uint8_t SELECT_BY_FILE_ID = 0x00;
+
+// ETSI TS 102 221 11.1.1.2 Table 11.2: Coding of P2 for SELECT
+static constexpr uint8_t SELECT_RETURN_FCP_TEMPLATE = 0x04;
+static constexpr uint8_t SELECT_RETURN_NOTHING = 0x0C;
+
+// From android.carrierapi.cts.FcpTemplate
+static constexpr uint8_t BER_TAG_FCP_TEMPLATE = 0x62;
+static constexpr uint8_t FILE_IDENTIFIER = 0x83;
+
+static const std::unordered_set<int32_t> kLinearFixedFiles{EF_MSISDN};
+
+class FilesystemApp::FilesystemChannel : public App::Channel {
+ public:
+ FilesystemChannel(int32_t channelId, std::shared_ptr<Filesystem> filesystem);
+
+ void select(Filesystem::Path path);
+ aidl::IccIoResult transmit(const aidl::SimApdu& message) override;
+
+ private:
+ std::shared_ptr<Filesystem> mFilesystem;
+ Filesystem::Path mSelectedFile = paths::mf;
+
+ aidl::IccIoResult commandSelect(int32_t p1, int32_t p2, int32_t p3, const std::string& data);
+ aidl::IccIoResult commandStatus(int32_t p1) const;
+ aidl::IccIoResult commandReadBinary(int32_t p1, int32_t p2) const;
+ aidl::IccIoResult commandUpdateBinary(int32_t p1, int32_t p2, std::string_view data);
+ aidl::IccIoResult commandReadRecord(int32_t p1, int32_t p2, int32_t p3);
+ aidl::IccIoResult commandGetResponse() const;
+};
+
+FilesystemApp::FilesystemApp(const std::shared_ptr<Filesystem>& filesystem)
+ : App(AID), mFilesystem(filesystem) {}
+
+std::shared_ptr<App::Channel> FilesystemApp::newChannel(int32_t id) {
+ auto channel = std::make_shared<FilesystemApp::FilesystemChannel>(id, mFilesystem);
+ if (id == 0) mBasicChannel = channel;
+ return channel;
+}
+
+FilesystemApp::FilesystemChannel::FilesystemChannel( //
+ int32_t channelId, std::shared_ptr<Filesystem> filesystem)
+ : App::Channel(channelId), mFilesystem(filesystem) {}
+
+void FilesystemApp::FilesystemChannel::select(Filesystem::Path path) {
+ mSelectedFile = path;
+}
+
+// android.carrierapi.cts.FcpTemplate.parseFcpTemplate (inversion)
+static std::vector<uint8_t> makeFcpTemplate(const Filesystem::Path& path) {
+ // clang-format off
+ return makeTlv(BER_TAG_FCP_TEMPLATE,
+ makeTlv(FILE_IDENTIFIER, uint16ToBytes(path.fileId))
+ );
+ // clang-format on
+}
+
+// ETSI TS 102 221 11.1.1
+aidl::IccIoResult FilesystemApp::FilesystemChannel::commandSelect( //
+ int32_t p1, int32_t p2, int32_t length, const std::string& data) {
+ if (p1 != SELECT_BY_FILE_ID ||
+ (p2 != SELECT_RETURN_FCP_TEMPLATE && p2 != SELECT_RETURN_NOTHING)) {
+ return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
+ }
+ if (length != 2) { // file ids are 2 byte long
+ return toIccIoResult(IO_RESULT_INCORRECT_LENGTH | 2);
+ }
+
+ auto fileId = strtol(data.c_str(), nullptr, 16);
+ if (fileId <= 0 || fileId > 0xFFFF) {
+ LOG(WARNING) << "Incorrect file ID: " << data;
+ return toIccIoResult(IO_RESULT_INCORRECT_DATA);
+ }
+
+ auto path = mFilesystem->find(fileId);
+ if (!path.has_value()) {
+ LOG(WARNING) << "FilesystemChannel: file " << std::hex << fileId << " not found";
+ return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
+ }
+ select(*path);
+
+ if (p2 == SELECT_RETURN_FCP_TEMPLATE) {
+ return toIccIoResult(bytesToHexString(makeFcpTemplate(mSelectedFile)));
+ }
+ return toIccIoResult("");
+}
+
+// ETSI TS 102 221 11.1.2
+aidl::IccIoResult FilesystemApp::FilesystemChannel::commandStatus(int32_t p1) const {
+ if (p1 != 0x00 && p1 != 0x01) { // 0x02 (termination) not implemented
+ return toIccIoResult(IO_RESULT_INCORRECT_P1_P2);
+ }
+ return toIccIoResult(bytesToHexString(makeFcpTemplate(mSelectedFile)));
+}
+
+// ETSI TS 102 221 11.1.3
+aidl::IccIoResult FilesystemApp::FilesystemChannel::commandReadBinary( //
+ int32_t offsetHi, int32_t offsetLo) const {
+ CHECK(offsetHi == 0 && offsetLo == 0) << "Offset not supported";
+ if (auto contents = mFilesystem->read(mSelectedFile); contents.has_value()) {
+ return toIccIoResult(*contents);
+ }
+ LOG(DEBUG) << "Missing ICC file (READ_BINARY): " << mSelectedFile.toString();
+ return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
+}
+
+// ETSI TS 102 221 11.1.4
+aidl::IccIoResult FilesystemApp::FilesystemChannel::commandUpdateBinary( //
+ int32_t offsetHi, int32_t offsetLo, std::string_view data) {
+ CHECK(offsetHi == 0 && offsetLo == 0) << "Offset not supported";
+ mFilesystem->write(mSelectedFile, hexStringToBytes(data));
+ return toIccIoResult("");
+}
+
+// ETSI TS 102 221 11.1.5
+aidl::IccIoResult FilesystemApp::FilesystemChannel::commandReadRecord( //
+ int32_t recordId, int32_t mode, int32_t length) {
+ CHECK(recordId == 1) << "Records other than no 1 are not supported";
+ CHECK(mode == 4) << "Unsupported record mode"; // absolute is the only currently supported mode
+ CHECK(length >= 0);
+ if (auto contents = mFilesystem->read(mSelectedFile); contents.has_value()) {
+ CHECK(static_cast<size_t>(length) == contents->size())
+ << "Partial reads not supported (" << length << " != " << contents->size() << ")";
+ return toIccIoResult(*contents);
+ }
+ LOG(DEBUG) << "Missing ICC file (READ_RECORD): " << mSelectedFile.toString();
+ return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
+}
+
+// com.android.internal.telephony.uicc.IccFileHandler (inversion)
+// ETSI TS 102 221 12.1.1
+aidl::IccIoResult FilesystemApp::FilesystemChannel::commandGetResponse() const {
+ auto file = mSelectedFile;
+ auto contents = mFilesystem->read(file);
+ if (!contents.has_value()) {
+ LOG(DEBUG) << "Missing ICC file (GET_RESPONSE): " << file.toString();
+ return toIccIoResult(IO_RESULT_FILE_NOT_FOUND);
+ }
+ auto fileSize = contents->size();
+ CHECK(fileSize <= 0xFFFF) << "File size won't fit in GET_RESPONSE";
+
+ // 3GPP TS 51.011 9.2.1
+ std::vector<uint8_t> response(GET_RESPONSE_EF_SIZE_BYTES, 0);
+ response[RESPONSE_DATA_FILE_SIZE_1] = fileSize >> 8;
+ response[RESPONSE_DATA_FILE_SIZE_2] = 0xFF & fileSize;
+ response[RESPONSE_DATA_FILE_ID_1] = file.fileId >> 8;
+ response[RESPONSE_DATA_FILE_ID_2] = 0xFF & file.fileId;
+ response[RESPONSE_DATA_FILE_TYPE] = TYPE_EF;
+ response[RESPONSE_DATA_LENGTH] = GET_RESPONSE_EF_SIZE_BYTES - RESPONSE_DATA_STRUCTURE;
+ if (kLinearFixedFiles.contains(file.fileId)) {
+ response[RESPONSE_DATA_STRUCTURE] = EF_TYPE_LINEAR_FIXED;
+ response[RESPONSE_DATA_RECORD_LENGTH] = fileSize; // single record support only
+ } else {
+ response[RESPONSE_DATA_STRUCTURE] = EF_TYPE_TRANSPARENT;
+ }
+
+ return toIccIoResult(response);
+}
+
+aidl::IccIoResult FilesystemApp::FilesystemChannel::transmit(const aidl::SimApdu& message) {
+ switch (message.instruction) {
+ case COMMAND_SELECT:
+ return commandSelect(message.p1, message.p2, message.p3, message.data);
+ case COMMAND_STATUS:
+ return commandStatus(message.p1);
+ case COMMAND_READ_BINARY:
+ return commandReadBinary(message.p1, message.p2);
+ case COMMAND_UPDATE_BINARY:
+ return commandUpdateBinary(message.p1, message.p2, message.data);
+ case COMMAND_READ_RECORD:
+ return commandReadRecord(message.p1, message.p2, message.p3);
+ case COMMAND_GET_RESPONSE:
+ return commandGetResponse();
+ default:
+ LOG(ERROR) << "Unsupported filesystem instruction: " << message;
+ return toIccIoResult(IO_RESULT_NOT_SUPPORTED);
+ }
+}
+
+aidl::IccIoResult FilesystemApp::iccIo(const aidl::IccIo& iccIo) {
+ CHECK(mBasicChannel) << "Basic channel must always be present";
+
+ if (iccIo.fileId != 0) {
+ mBasicChannel->select({iccIo.fileId, iccIo.path});
+ }
+
+ aidl::SimApdu message = {
+ .instruction = iccIo.command,
+ .p1 = iccIo.p1,
+ .p2 = iccIo.p2,
+ .p3 = iccIo.p3,
+ .data = iccIo.data,
+ };
+ return mBasicChannel->transmit(message);
+}
+
+} // namespace android::hardware::radio::minimal::sim::apps
diff --git a/radio/aidl/minradio/libminradio/sim/apps/tlv.cpp b/radio/aidl/minradio/libminradio/sim/apps/tlv.cpp
new file mode 100644
index 0000000..28f00e7
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/apps/tlv.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include "tlv.h"
+
+#include <android-base/logging.h>
+
+namespace android::hardware::radio::minimal::sim {
+
+std::vector<uint8_t> makeTlv(uint32_t tag, std::span<uint8_t const> value) {
+ // If needed, implement ISO 7816 5.2.2.1
+ CHECK(tag <= 0xFFFF) << "3-byte tag numbers (" << tag << ") are not implemented";
+
+ // If we end up needing more, implement ISO 7816 5.2.2.2
+ CHECK(value.size() <= 0x7F) << "Large tag lengths are not implemented: " << value.size()
+ << " for " << tag;
+
+ std::vector<uint8_t> serialized;
+ if (tag <= 0xFF) {
+ serialized = {static_cast<uint8_t>(tag), static_cast<uint8_t>(value.size())};
+ } else {
+ serialized = {static_cast<uint8_t>(tag >> 8), static_cast<uint8_t>(tag & 0xFF),
+ static_cast<uint8_t>(value.size())};
+ }
+
+ serialized.insert(serialized.end(), value.begin(), value.end());
+ return serialized;
+}
+
+namespace tlv_operators {
+
+std::vector<uint8_t> operator+(std::span<uint8_t const> a, std::span<uint8_t const> b) {
+ std::vector<uint8_t> concatenated;
+ concatenated.insert(concatenated.end(), a.begin(), a.end());
+ concatenated.insert(concatenated.end(), b.begin(), b.end());
+ return concatenated;
+}
+
+} // namespace tlv_operators
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/libminradio/sim/apps/tlv.h b/radio/aidl/minradio/libminradio/sim/apps/tlv.h
new file mode 100644
index 0000000..6d39bc7
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/apps/tlv.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#pragma once
+
+#include <span>
+#include <vector>
+
+namespace android::hardware::radio::minimal::sim {
+
+/* makeTlv and operator+ are a very inefficient (and incomplete) implementation of
+ * BER-TLV encoding. This is fine here, because the data set is very small and used infrequently.
+ *
+ * @tag Tag number (already encoded per ISO 7816 5.2.2.1)
+ */
+std::vector<uint8_t> makeTlv(uint32_t tag, std::span<uint8_t const> value);
+
+namespace tlv_operators {
+
+std::vector<uint8_t> operator+(std::span<uint8_t const> a, std::span<uint8_t const> b);
+
+} // namespace tlv_operators
+
+} // namespace android::hardware::radio::minimal::sim
diff --git a/radio/aidl/minradio/minradio-example/Android.bp b/radio/aidl/minradio/minradio-example/Android.bp
new file mode 100644
index 0000000..7051972
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/Android.bp
@@ -0,0 +1,77 @@
+// Copyright (C) 2024 The Android Open Source 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+ name: "android.hardware.radio-service.minradio-example",
+ defaults: ["android.hardware.radio-minradio@defaults"],
+ vintf_fragment_modules: ["android.hardware.radio-service.minradio-example.vintf"],
+ installable: false,
+ apex_available: ["com.android.hardware.radio.minradio.virtual"],
+ shared_libs: [
+ "android.hardware.radio-library.minradio",
+ ],
+ static_libs: [
+ "libnetdevice",
+ ],
+ srcs: [
+ "impl/RadioConfig.cpp",
+ "impl/RadioData.cpp",
+ "impl/RadioModem.cpp",
+ "impl/RadioNetwork.cpp",
+ "impl/RadioSim.cpp",
+ "service.cpp",
+ ],
+}
+
+vintf_fragment {
+ name: "android.hardware.radio-service.minradio-example.vintf",
+ src: "minradio-example.xml",
+ vendor: true,
+}
+
+apex {
+ name: "com.android.hardware.radio.minradio.virtual",
+ manifest: "apex_manifest.json",
+ file_contexts: "file_contexts",
+ key: "com.android.hardware.key",
+ certificate: ":com.android.hardware.certificate",
+ updatable: false,
+ soc_specific: true,
+
+ binaries: [
+ "android.hardware.radio-service.minradio-example",
+ ],
+ prebuilts: [
+ "android.hardware.telephony.data.prebuilt.xml",
+
+ // TODO(b/369726708): adding to init_rc field of cc_binary doesn't work in apex yet
+ "minradio-example.rc",
+ ],
+ overrides: ["rild"],
+}
+
+prebuilt_etc {
+ name: "minradio-example.rc",
+ src: "minradio-example.rc",
+ installable: false,
+}
diff --git a/radio/aidl/minradio/minradio-example/apex_manifest.json b/radio/aidl/minradio/minradio-example/apex_manifest.json
new file mode 100644
index 0000000..85ba9a3
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.hardware.radio.minradio.virtual",
+ "version": 1
+}
diff --git a/radio/aidl/minradio/minradio-example/file_contexts b/radio/aidl/minradio/minradio-example/file_contexts
new file mode 100644
index 0000000..1b7544d
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/file_contexts
@@ -0,0 +1,3 @@
+(/.*)? u:object_r:vendor_file:s0
+/etc(/.*)? u:object_r:vendor_configs_file:s0
+/bin/hw/android.hardware.radio-service.minradio-.* u:object_r:hal_radio_default_exec:s0
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioConfig.cpp b/radio/aidl/minradio/minradio-example/impl/RadioConfig.cpp
new file mode 100644
index 0000000..12e8ede
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioConfig.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include "RadioConfig.h"
+
+#include <aidl/android/hardware/radio/sim/CardStatus.h>
+#include <libminradio/debug.h>
+#include <libminradio/response.h>
+
+#define RADIO_MODULE "ConfigImpl"
+
+namespace android::hardware::radio::service {
+
+using ::android::hardware::radio::minimal::noError;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::config;
+namespace aidlSim = ::aidl::android::hardware::radio::sim;
+constexpr auto ok = &ScopedAStatus::ok;
+
+ScopedAStatus RadioConfig::getSimSlotsStatus(int32_t serial) {
+ LOG_CALL;
+ aidl::SimSlotStatus simslot1Status{
+ .cardState = aidlSim::CardStatus::STATE_PRESENT,
+ .atr = "",
+ .eid = "eUICC-simslot1",
+ .portInfo = {{
+ .iccId = "12345678901234567890",
+ .logicalSlotId = 0,
+ .portActive = true,
+ }},
+ };
+ respond()->getSimSlotsStatusResponse(noError(serial), {simslot1Status});
+ return ok();
+}
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioConfig.h b/radio/aidl/minradio/minradio-example/impl/RadioConfig.h
new file mode 100644
index 0000000..e31ce19
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioConfig.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/config/RadioConfig.h>
+
+namespace android::hardware::radio::service {
+
+class RadioConfig : public minimal::RadioConfig {
+ protected:
+ ::ndk::ScopedAStatus getSimSlotsStatus(int32_t serial) override;
+};
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioData.cpp b/radio/aidl/minradio/minradio-example/impl/RadioData.cpp
new file mode 100644
index 0000000..1335bd9
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioData.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include "RadioData.h"
+
+#include <aidl/android/hardware/radio/RadioConst.h>
+#include <libminradio/debug.h>
+#include <libminradio/response.h>
+#include <libnetdevice/libnetdevice.h>
+
+#define RADIO_MODULE "DataImpl"
+
+namespace android::hardware::radio::service {
+
+using namespace ::android::hardware::radio::minimal::binder_printing;
+using ::aidl::android::hardware::radio::RadioConst;
+using ::aidl::android::hardware::radio::RadioError;
+using ::android::hardware::radio::minimal::errorResponse;
+using ::android::hardware::radio::minimal::noError;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::data;
+namespace aidlCommon = ::aidl::android::hardware::radio;
+constexpr auto ok = &ScopedAStatus::ok;
+
+ScopedAStatus RadioData::setupDataCall(int32_t serial, aidlCommon::AccessNetwork accessNetwork,
+ const aidl::DataProfileInfo& dataProfileInfo,
+ bool roamingAllowed, aidl::DataRequestReason reason,
+ const std::vector<aidl::LinkAddress>& addresses,
+ const std::vector<std::string>& dnses, int32_t pduSessId,
+ const std::optional<aidl::SliceInfo>& sliceInfo,
+ bool matchAllRuleAllowed) {
+ LOG_CALL << accessNetwork //
+ << " {" << dataProfileInfo.profileId << '}' //
+ << ' ' << roamingAllowed //
+ << ' ' << reason //
+ << ' ' << addresses.size() //
+ << ' ' << dnses.size() << ' ' << pduSessId //
+ << ' ' << sliceInfo.has_value() //
+ << ' ' << matchAllRuleAllowed;
+
+ bool ifaceOk = netdevice::setAddr4("buried_eth0", "192.168.97.2", 30);
+ ifaceOk = ifaceOk && netdevice::up("buried_eth0");
+ if (!ifaceOk) {
+ respond()->setupDataCallResponse(errorResponse(serial, RadioError::INTERNAL_ERR), {});
+ return ok();
+ }
+
+ aidl::SetupDataCallResult result{
+ .cause = aidl::DataCallFailCause::NONE,
+ .suggestedRetryTime = RadioConst::VALUE_UNAVAILABLE_LONG,
+ .cid = setupDataCallCid(),
+ .active = aidl::SetupDataCallResult::DATA_CONNECTION_STATUS_ACTIVE,
+ .type = aidl::PdpProtocolType::IP,
+ .ifname = "buried_eth0",
+ .addresses = {{
+ .address = "192.168.97.2/30",
+ .addressProperties = 0,
+ .deprecationTime = RadioConst::VALUE_UNAVAILABLE_LONG,
+ .expirationTime = RadioConst::VALUE_UNAVAILABLE_LONG,
+ }},
+ .dnses = {"8.8.8.8"},
+ .gateways = {"192.168.97.1"},
+ .pcscf = {},
+ .mtuV4 = 0,
+ .mtuV6 = 0,
+ .defaultQos = {},
+ .qosSessions = {},
+ .handoverFailureMode = aidl::SetupDataCallResult::HANDOVER_FAILURE_MODE_LEGACY,
+ .pduSessionId = 0,
+ .sliceInfo = std::nullopt,
+ .trafficDescriptors = {},
+ };
+
+ setupDataCallBase(result);
+
+ respond()->setupDataCallResponse(noError(serial), result);
+ return ok();
+}
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioData.h b/radio/aidl/minradio/minradio-example/impl/RadioData.h
new file mode 100644
index 0000000..89b331e
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioData.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/data/RadioData.h>
+
+namespace android::hardware::radio::service {
+
+class RadioData : public minimal::RadioData {
+ public:
+ using minimal::RadioData::RadioData;
+
+ protected:
+ ::ndk::ScopedAStatus setupDataCall(
+ int32_t serial, ::aidl::android::hardware::radio::AccessNetwork accessNetwork,
+ const ::aidl::android::hardware::radio::data::DataProfileInfo& dataProfileInfo,
+ bool roamingAllowed, ::aidl::android::hardware::radio::data::DataRequestReason reason,
+ const std::vector<::aidl::android::hardware::radio::data::LinkAddress>& addresses,
+ const std::vector<std::string>& dnses, int32_t pduSessionId,
+ const std::optional<::aidl::android::hardware::radio::data::SliceInfo>& sliceInfo,
+ bool matchAllRuleAllowed) override;
+};
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioModem.cpp b/radio/aidl/minradio/minradio-example/impl/RadioModem.cpp
new file mode 100644
index 0000000..dc8c1aa
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioModem.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include "RadioModem.h"
+
+#include <libminradio/debug.h>
+#include <libminradio/response.h>
+
+#define RADIO_MODULE "ModemImpl"
+
+namespace android::hardware::radio::service {
+
+using ::aidl::android::hardware::radio::RadioTechnology;
+using ::android::hardware::radio::minimal::noError;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::modem;
+constexpr auto ok = &ScopedAStatus::ok;
+
+RadioModem::RadioModem(std::shared_ptr<minimal::SlotContext> context)
+ : minimal::RadioModem(context, {{RadioTechnology::LTE, RadioTechnology::HSPA}}) {}
+
+ScopedAStatus RadioModem::getImei(int32_t serial) {
+ LOG_CALL;
+ aidl::ImeiInfo info{
+ .type = aidl::ImeiInfo::ImeiType::PRIMARY,
+ .imei = "867400022047199",
+ .svn = "01",
+ };
+ respond()->getImeiResponse(noError(serial), info);
+ return ok();
+}
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioModem.h b/radio/aidl/minradio/minradio-example/impl/RadioModem.h
new file mode 100644
index 0000000..1102188
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioModem.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/modem/RadioModem.h>
+
+namespace android::hardware::radio::service {
+
+class RadioModem : public minimal::RadioModem {
+ public:
+ RadioModem(std::shared_ptr<minimal::SlotContext> context);
+
+ protected:
+ // Note: getBasebandVersion is optional, but recommended to implement on production devices.
+ // It's just returning a version of the cellular implementation (e.g. modem software).
+ ::ndk::ScopedAStatus getImei(int32_t serial) override;
+};
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioNetwork.cpp b/radio/aidl/minradio/minradio-example/impl/RadioNetwork.cpp
new file mode 100644
index 0000000..4ad9eb8
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioNetwork.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include "RadioNetwork.h"
+
+#include <libminradio/debug.h>
+#include <libminradio/network/structs.h>
+#include <libminradio/response.h>
+
+#define RADIO_MODULE "NetworkImpl"
+
+namespace android::hardware::radio::service {
+
+using ::aidl::android::hardware::radio::RadioConst;
+using ::android::hardware::radio::minimal::noError;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::network;
+namespace aidlRadio = ::aidl::android::hardware::radio;
+constexpr auto ok = &ScopedAStatus::ok;
+
+ScopedAStatus RadioNetwork::getDataRegistrationState(int32_t serial) {
+ LOG_CALL;
+
+ aidl::CellIdentityLte cellid{
+ .mcc = "310",
+ .mnc = "555",
+ .ci = 12345,
+ .pci = 102,
+ .tac = 1040,
+ .earfcn = 103,
+ .operatorNames =
+ {
+ .alphaLong = "Minradio",
+ .alphaShort = "MR",
+ .operatorNumeric = "310555",
+ .status = aidl::OperatorInfo::STATUS_CURRENT,
+ },
+ .bandwidth = 1400,
+ .additionalPlmns = {},
+ .csgInfo = std::nullopt,
+ .bands =
+ {
+ aidl::EutranBands::BAND_1,
+ aidl::EutranBands::BAND_88,
+ },
+ };
+ aidl::RegStateResult res{
+ .regState = aidl::RegState::REG_HOME,
+ .rat = aidlRadio::RadioTechnology::LTE,
+ .reasonForDenial = aidl::RegistrationFailCause::NONE,
+ .cellIdentity = cellid,
+ .registeredPlmn = "310555",
+ .accessTechnologySpecificInfo = aidl::EutranRegistrationInfo{},
+ };
+ respond()->getDataRegistrationStateResponse(noError(serial), res);
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getSignalStrength(int32_t serial) {
+ LOG_CALL;
+
+ auto signal = minimal::structs::makeSignalStrength();
+ signal.lte = {
+ 30, // (0-31, 99)
+ 100, // Range: 44 to 140 dBm
+ 10, // Range: 20 to 3 dB
+ 100, 10, RadioConst::VALUE_UNAVAILABLE, RadioConst::VALUE_UNAVAILABLE,
+ };
+
+ respond()->getSignalStrengthResponse(noError(serial), signal);
+ return ok();
+}
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioNetwork.h b/radio/aidl/minradio/minradio-example/impl/RadioNetwork.h
new file mode 100644
index 0000000..c07b281
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioNetwork.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/network/RadioNetwork.h>
+
+namespace android::hardware::radio::service {
+
+class RadioNetwork : public minimal::RadioNetwork {
+ public:
+ using minimal::RadioNetwork::RadioNetwork;
+
+ protected:
+ ::ndk::ScopedAStatus getDataRegistrationState(int32_t serial) override;
+ ::ndk::ScopedAStatus getSignalStrength(int32_t serial) override;
+};
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioSim.cpp b/radio/aidl/minradio/minradio-example/impl/RadioSim.cpp
new file mode 100644
index 0000000..a5e1167
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioSim.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+
+#include "RadioSim.h"
+
+#include <libminradio/debug.h>
+#include <libminradio/response.h>
+#include <libminradio/sim/IccUtils.h>
+
+#define RADIO_MODULE "SimImpl"
+
+namespace android::hardware::radio::service {
+
+using ::android::hardware::radio::minimal::noError;
+using ::ndk::ScopedAStatus;
+namespace aidl = ::aidl::android::hardware::radio::sim;
+namespace aidlConfig = ::aidl::android::hardware::radio::config;
+constexpr auto ok = &ScopedAStatus::ok;
+
+RadioSim::RadioSim(std::shared_ptr<minimal::SlotContext> context) : minimal::RadioSim(context) {
+ addCtsCertificate(); // do NOT call on real device's production build
+ setIccid("98683081462002318379");
+ mFilesystem->write(minimal::sim::paths::msisdn, minimal::sim::encodeMsisdn("+16500000000"));
+}
+
+ScopedAStatus RadioSim::getIccCardStatus(int32_t serial) {
+ LOG_CALL;
+
+ aidl::CardStatus cardStatus{
+ .cardState = aidl::CardStatus::STATE_PRESENT,
+ .universalPinState = aidl::PinState::DISABLED,
+ .gsmUmtsSubscriptionAppIndex = 0,
+ .imsSubscriptionAppIndex = -1,
+ .applications =
+ {
+ aidl::AppStatus{
+ .appType = aidl::AppStatus::APP_TYPE_USIM,
+ .appState = aidl::AppStatus::APP_STATE_READY,
+ .persoSubstate = aidl::PersoSubstate::READY,
+ },
+ },
+ .atr = "",
+ .iccid = getIccid().value_or(""),
+ .eid = "eUICC-simslot1",
+ .slotMap =
+ {
+ .physicalSlotId = 0,
+ .portId = 0,
+ },
+ .supportedMepMode = aidlConfig::MultipleEnabledProfilesMode::NONE,
+ };
+ respond()->getIccCardStatusResponse(noError(serial), cardStatus);
+ return ok();
+}
+
+ScopedAStatus RadioSim::getImsiForApp(int32_t serial, const std::string& aid) {
+ LOG_CALL << aid;
+ // 6-digit IMSI prefix has to be a valid mccmnc
+ respond()->getImsiForAppResponse(noError(serial), "311740123456789");
+ return ok();
+}
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/impl/RadioSim.h b/radio/aidl/minradio/minradio-example/impl/RadioSim.h
new file mode 100644
index 0000000..138c7fd
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/impl/RadioSim.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source 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.
+ */
+#pragma once
+
+#include <libminradio/sim/RadioSim.h>
+
+namespace android::hardware::radio::service {
+
+class RadioSim : public minimal::RadioSim {
+ public:
+ RadioSim(std::shared_ptr<minimal::SlotContext> context);
+
+ protected:
+ ::ndk::ScopedAStatus getIccCardStatus(int32_t serial) override;
+ ::ndk::ScopedAStatus getImsiForApp(int32_t serial, const std::string& aid) override;
+};
+
+} // namespace android::hardware::radio::service
diff --git a/radio/aidl/minradio/minradio-example/minradio-example.rc b/radio/aidl/minradio/minradio-example/minradio-example.rc
new file mode 100644
index 0000000..47e7da3
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/minradio-example.rc
@@ -0,0 +1,5 @@
+service vendor.minradio-example /apex/com.android.hardware.radio.minradio.virtual/bin/hw/android.hardware.radio-service.minradio-example
+ class main
+ user radio
+ group radio inet misc audio log readproc wakelock
+ capabilities BLOCK_SUSPEND NET_ADMIN NET_RAW
diff --git a/radio/aidl/minradio/minradio-example/minradio-example.xml b/radio/aidl/minradio/minradio-example/minradio-example.xml
new file mode 100644
index 0000000..3ef129e
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/minradio-example.xml
@@ -0,0 +1,27 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.radio.config</name>
+ <fqname>IRadioConfig/default</fqname>
+ <version>4</version>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.radio.data</name>
+ <fqname>IRadioData/slot1</fqname>
+ <version>4</version>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.radio.modem</name>
+ <fqname>IRadioModem/slot1</fqname>
+ <version>4</version>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.radio.network</name>
+ <fqname>IRadioNetwork/slot1</fqname>
+ <version>4</version>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.radio.sim</name>
+ <fqname>IRadioSim/slot1</fqname>
+ <version>4</version>
+ </hal>
+</manifest>
diff --git a/radio/aidl/minradio/minradio-example/service.cpp b/radio/aidl/minradio/minradio-example/service.cpp
new file mode 100644
index 0000000..6d3c020
--- /dev/null
+++ b/radio/aidl/minradio/minradio-example/service.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source 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.
+ */
+
+#include "impl/RadioConfig.h"
+#include "impl/RadioData.h"
+#include "impl/RadioModem.h"
+#include "impl/RadioNetwork.h"
+#include "impl/RadioSim.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+namespace android::hardware::radio::service {
+
+using namespace std::string_literals;
+
+static std::vector<std::shared_ptr<ndk::ICInterface>> gPublishedHals;
+
+static void publishRadioConfig() {
+ auto aidlHal = ndk::SharedRefBase::make<RadioConfig>();
+ gPublishedHals.push_back(aidlHal);
+ const auto instance = RadioConfig::descriptor + "/default"s;
+ const auto status = AServiceManager_addService(aidlHal->asBinder().get(), instance.c_str());
+ CHECK_EQ(status, STATUS_OK);
+}
+
+template <typename T>
+static void publishRadioHal(const std::string& slot,
+ std::shared_ptr<minimal::SlotContext> context) {
+ const auto instance = T::descriptor + "/"s + slot;
+ if (!AServiceManager_isDeclared(instance.c_str())) {
+ LOG(INFO) << instance << " is not declared in VINTF (this may be intentional)";
+ return;
+ }
+ LOG(DEBUG) << "Publishing " << instance;
+
+ auto aidlHal = ndk::SharedRefBase::make<T>(context);
+ gPublishedHals.push_back(aidlHal);
+ const auto status = AServiceManager_addService(aidlHal->asBinder().get(), instance.c_str());
+ CHECK_EQ(status, STATUS_OK);
+}
+
+void main() {
+ base::InitLogging(nullptr, base::LogdLogger(base::RADIO));
+ base::SetDefaultTag("minradio");
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ LOG(DEBUG) << "Minimal Radio HAL service starting...";
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ auto slot1Context = std::make_shared<minimal::SlotContext>(1);
+
+ publishRadioConfig();
+ publishRadioHal<RadioData>("slot1", slot1Context);
+ publishRadioHal<RadioModem>("slot1", slot1Context);
+ publishRadioHal<RadioNetwork>("slot1", slot1Context);
+ publishRadioHal<RadioSim>("slot1", slot1Context);
+
+ LOG(DEBUG) << "Minimal Radio HAL service is operational";
+ ABinderProcess_joinThreadPool();
+ LOG(FATAL) << "Minimal Radio HAL service has stopped";
+}
+
+} // namespace android::hardware::radio::service
+
+int main() {
+ android::hardware::radio::service::main();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp b/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
index 1623960..5df39ed 100644
--- a/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
+++ b/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
@@ -209,4 +209,15 @@
INSTANTIATE_TEST_SUITE_P(
PerInstance, SecureElementHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
- android::hardware::PrintInstanceNameToString);
\ No newline at end of file
+ android::hardware::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ std::system("svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ std::system("svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
+}
diff --git a/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp b/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
index d7e4546..106ee29 100644
--- a/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
+++ b/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
@@ -94,3 +94,14 @@
PerInstance, SecureElementHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
android::hardware::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ std::system("svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ std::system("svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
+}
diff --git a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
index 26b2ded..98c8a9c 100644
--- a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
+++ b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
@@ -108,3 +108,14 @@
PerInstance, SecureElementHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
android::hardware::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ std::system("svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ std::system("svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
+}
diff --git a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
index 9678da4..da69b37 100644
--- a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
+++ b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
@@ -320,5 +320,10 @@
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
- return RUN_ALL_TESTS();
+ std::system("/system/bin/svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ std::system("/system/bin/svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
}
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index 5236e90..195e47b 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -52,7 +52,7 @@
},
],
-
+ min_sdk_version: "35",
}
// An aidl_interface_defaults that includes the latest KeyMint AIDL interface.
diff --git a/security/rkp/aidl/Android.bp b/security/rkp/aidl/Android.bp
index adc63f6..df8a0ef 100644
--- a/security/rkp/aidl/Android.bp
+++ b/security/rkp/aidl/Android.bp
@@ -35,6 +35,7 @@
"//apex_available:platform",
"com.android.virt",
],
+ min_sdk_version: "35",
},
},
versions_with_info: [
diff --git a/security/secureclock/aidl/Android.bp b/security/secureclock/aidl/Android.bp
index d7e7b43..1d4ec58 100644
--- a/security/secureclock/aidl/Android.bp
+++ b/security/secureclock/aidl/Android.bp
@@ -28,4 +28,5 @@
},
},
versions: ["1"],
+ min_sdk_version: "35",
}
diff --git a/security/see/authmgr/aidl/README.md b/security/see/authmgr/aidl/README.md
index 97b2b1d..d7bb5e4 100644
--- a/security/see/authmgr/aidl/README.md
+++ b/security/see/authmgr/aidl/README.md
@@ -16,6 +16,9 @@
requirements that are specific to Android release versions.
### Android 16
-If implementing `IAuthMgrAuthorization` in Android 16 only one AuthMgr Backend is
+- If implementing `IAuthMgrAuthorization` in Android 16 only one AuthMgr Backend is
supported and dynamic service discovery is not supported. The AuthMgr Backend
-service must be exposed on secure partition ID 0x8001 over VSOCK port 1.
\ No newline at end of file
+service must be exposed on secure partition ID 0x8001 over VSOCK port 1.
+
+- AuthMgr Front Ends must implement the "android.16" profile as described in the
+[Android Profile for DICE](https://pigweed.googlesource.com/open-dice/+/HEAD/docs/android.md#versions)
\ No newline at end of file
diff --git a/security/see/authmgr/aidl/default/Android.bp b/security/see/authmgr/aidl/default/Android.bp
new file mode 100644
index 0000000..35fce32
--- /dev/null
+++ b/security/see/authmgr/aidl/default/Android.bp
@@ -0,0 +1,20 @@
+//
+// Copyright (C) 2025 The Android Open-Source 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.
+
+vintf_fragment {
+ name: "android.hardware.security.see.authmgr.xml",
+ src: "android.hardware.security.see.authmgr.xml",
+ vendor: true,
+}
diff --git a/security/see/authmgr/aidl/default/android.hardware.security.see.authmgr.xml b/security/see/authmgr/aidl/default/android.hardware.security.see.authmgr.xml
new file mode 100644
index 0000000..17fcc21
--- /dev/null
+++ b/security/see/authmgr/aidl/default/android.hardware.security.see.authmgr.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl" exclusive-to="virtual-machine">
+ <name>android.hardware.security.see.authmgr</name>
+ <version>1</version>
+ <interface>
+ <name>IAuthMgrAuthorization</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/security/see/hwcrypto/aidl/vts/functional/Android.bp b/security/see/hwcrypto/aidl/vts/functional/Android.bp
index eb2eba1..fc63878 100644
--- a/security/see/hwcrypto/aidl/vts/functional/Android.bp
+++ b/security/see/hwcrypto/aidl/vts/functional/Android.bp
@@ -53,9 +53,11 @@
rust_test {
name: "VtsAidlHwCryptoTests",
srcs: ["hwcryptokey_tests.rs"],
+ test_config: "AndroidKeyOperations.xml",
require_root: true,
defaults: [
"hw_crypto_hal_aidl_rust_defaults",
+ "rdroidtest.defaults",
],
rustlibs: [
"libhwcryptohal_vts_test",
@@ -69,9 +71,11 @@
rust_test {
name: "VtsAidlHwCryptoOperationsTests",
srcs: ["hwcrypto_operations_tests.rs"],
+ test_config: "AndroidTestOperations.xml",
require_root: true,
defaults: [
"hw_crypto_hal_aidl_rust_defaults",
+ "rdroidtest.defaults",
],
rustlibs: [
"libhwcryptohal_vts_test",
diff --git a/security/see/hwcrypto/aidl/vts/functional/AndroidKeyOperations.xml b/security/see/hwcrypto/aidl/vts/functional/AndroidKeyOperations.xml
new file mode 100644
index 0000000..57229d7
--- /dev/null
+++ b/security/see/hwcrypto/aidl/vts/functional/AndroidKeyOperations.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source 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.
+-->
+<configuration description="Config for HwCrypto HAL operations VTS tests.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="VtsAidlHwCryptoTests" value="/data/local/tmp/VtsAidlHwCryptoTests" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+ <option name="test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsAidlHwCryptoTests" />
+ <!-- Rust tests are run in parallel by default. Run these ones
+ single-threaded. -->
+ <option name="native-test-flag" value="--test-threads=1" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/security/see/hwcrypto/aidl/vts/functional/AndroidTestOperations.xml b/security/see/hwcrypto/aidl/vts/functional/AndroidTestOperations.xml
new file mode 100644
index 0000000..f069b3b
--- /dev/null
+++ b/security/see/hwcrypto/aidl/vts/functional/AndroidTestOperations.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source 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.
+-->
+<configuration description="Config for HwCrypto HAL device key VTS tests.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="VtsAidlHwCryptoOperationsTests" value="/data/local/tmp/VtsAidlHwCryptoOperationsTests" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+ <option name="test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsAidlHwCryptoOperationsTests" />
+ <!-- Rust tests are run in parallel by default. Run these ones
+ single-threaded. -->
+ <option name="native-test-flag" value="--test-threads=1" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/security/see/hwcrypto/aidl/vts/functional/hwcrypto_operations_tests.rs b/security/see/hwcrypto/aidl/vts/functional/hwcrypto_operations_tests.rs
index 521fb73..69a34e3 100644
--- a/security/see/hwcrypto/aidl/vts/functional/hwcrypto_operations_tests.rs
+++ b/security/see/hwcrypto/aidl/vts/functional/hwcrypto_operations_tests.rs
@@ -27,8 +27,10 @@
KeyPolicy::KeyPolicy,CryptoOperation::CryptoOperation,CryptoOperationSet::CryptoOperationSet,
OperationParameters::OperationParameters, PatternParameters::PatternParameters,
};
+use rdroidtest::{ignore_if, rdroidtest};
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_key_operations_connection() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -36,7 +38,8 @@
assert!(hw_crypto_operations.is_ok(), "Couldn't get back a hwcrypto operations binder object");
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_key_operations_simple_aes_test() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -123,7 +126,8 @@
assert_eq!(decrypted_msg, "string to be encrypted", "couldn't retrieve original message");
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_key_operations_simple_hmac_test() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -183,7 +187,8 @@
assert_eq!(mac, mac2, "got a different mac");
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_key_operations_aes_simple_cbcs_test_non_block_multiple() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -280,7 +285,8 @@
);
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_key_operations_aes_simple_all_encrypted_cbcs_test() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -402,7 +408,8 @@
);
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn check_cbcs_wrong_key_types() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -473,7 +480,8 @@
assert!(process_result.is_err(), "Should not be able to use cbcs mode with this key type");
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn aes_simple_cbcs_test() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -584,3 +592,5 @@
"couldn't retrieve original message"
);
}
+
+rdroidtest::test_main!();
diff --git a/security/see/hwcrypto/aidl/vts/functional/hwcryptokey_tests.rs b/security/see/hwcrypto/aidl/vts/functional/hwcryptokey_tests.rs
index fcce839..8b4d924 100644
--- a/security/see/hwcrypto/aidl/vts/functional/hwcryptokey_tests.rs
+++ b/security/see/hwcrypto/aidl/vts/functional/hwcryptokey_tests.rs
@@ -26,14 +26,17 @@
};
use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::KeyPolicy::KeyPolicy;
use hwcryptohal_common;
+use rdroidtest::{ignore_if, rdroidtest};
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_key_connection() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey();
assert!(hw_crypto_key.is_ok(), "Couldn't get back a hwcryptokey binder object");
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_key_get_current_dice_policy() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -41,7 +44,8 @@
assert!(!dice_policy.is_empty(), "received empty dice policy");
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_get_keyslot_data() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -55,7 +59,8 @@
);
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_import_clear_key() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -83,7 +88,8 @@
assert!(key.is_err(), "imported keys should be of type PORTABLE");
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_token_export_import() {
// This test is not representative of the complete flow because here the exporter and importer
// are the same client, which is not something we would usually do
@@ -107,7 +113,8 @@
// TODO: Use operations to verify that the keys match
}
-#[test]
+#[rdroidtest]
+#[ignore_if(hwcryptohal_vts_test::ignore_test())]
fn test_hwcrypto_android_invalid_calls() {
let hw_crypto_key = hwcryptohal_vts_test::get_hwcryptokey()
.expect("Couldn't get back a hwcryptokey binder object");
@@ -163,3 +170,5 @@
"wrong error type received"
);
}
+
+rdroidtest::test_main!();
diff --git a/security/see/hwcrypto/aidl/vts/functional/lib.rs b/security/see/hwcrypto/aidl/vts/functional/lib.rs
index 465dde7..43676f6 100644
--- a/security/see/hwcrypto/aidl/vts/functional/lib.rs
+++ b/security/see/hwcrypto/aidl/vts/functional/lib.rs
@@ -18,11 +18,26 @@
//! It provides the base clases necessaries to write HwCrypto VTS tests
use anyhow::Result;
-use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::IHwCryptoKey::BpHwCryptoKey;
use android_hardware_security_see_hwcrypto::aidl::android::hardware::security::see::hwcrypto::IHwCryptoKey::IHwCryptoKey;
+pub const HWCRYPTO_SERVICE: &str = "android.hardware.security.see.hwcrypto.IHwCryptoKey";
+
/// Get a HwCryptoKey binder service object using the service manager
pub fn get_hwcryptokey() -> Result<binder::Strong<dyn IHwCryptoKey>, binder::Status> {
- let interface_name = <BpHwCryptoKey as IHwCryptoKey>::get_descriptor().to_owned() + "/default";
+ let interface_name = HWCRYPTO_SERVICE.to_owned() + "/default";
Ok(binder::get_interface(&interface_name)?)
}
+
+pub fn get_supported_instances() -> Vec<(String, String)> {
+ // Determine which instances are available.
+ binder::get_declared_instances(HWCRYPTO_SERVICE)
+ .unwrap_or_default()
+ .into_iter()
+ .map(|v| (v.clone(), v))
+ .collect()
+}
+
+pub fn ignore_test() -> bool {
+ let instances = get_supported_instances();
+ instances.len() == 0
+}
diff --git a/security/see/storage/default/Android.bp b/security/see/storage/default/Android.bp
index 7ea7739..33d93bb 100644
--- a/security/see/storage/default/Android.bp
+++ b/security/see/storage/default/Android.bp
@@ -13,9 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-prebuilt_etc {
+vintf_fragment {
name: "android.hardware.security.see.storage-service.trusty.xml",
- sub_dir: "vintf",
- vendor: true,
src: "android.hardware.security.see.storage-service.trusty.xml",
+ vendor: true,
}
diff --git a/sensors/aidl/multihal/android.hardware.sensors-service-multihal.rc b/sensors/aidl/multihal/android.hardware.sensors-service-multihal.rc
index 5aecc54..d1acd6f 100644
--- a/sensors/aidl/multihal/android.hardware.sensors-service-multihal.rc
+++ b/sensors/aidl/multihal/android.hardware.sensors-service-multihal.rc
@@ -1,3 +1,6 @@
+on boot
+ setprop vendor.sensors.dynamic_sensor_op_timeout_ms 1600
+
service vendor.sensors-hal-multihal /vendor/bin/hw/android.hardware.sensors-service.multihal
class hal
user system
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
index 8bc9d1a..87e6d95 100644
--- a/wifi/aidl/default/aidl_struct_util.cpp
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -66,6 +66,8 @@
return IWifiChip::FeatureSetMask::SET_VOIP_MODE;
case WIFI_FEATURE_MLO_SAP:
return IWifiChip::FeatureSetMask::MLO_SAP;
+ case WIFI_FEATURE_MULTIPLE_MLD_ON_SAP:
+ return IWifiChip::FeatureSetMask::MULTIPLE_MLD_ON_SAP;
};
CHECK(false) << "Unknown legacy feature: " << feature;
return {};
@@ -122,7 +124,8 @@
WIFI_FEATURE_P2P_RAND_MAC,
WIFI_FEATURE_AFC_CHANNEL,
WIFI_FEATURE_SET_VOIP_MODE,
- WIFI_FEATURE_MLO_SAP};
+ WIFI_FEATURE_MLO_SAP,
+ WIFI_FEATURE_MULTIPLE_MLD_ON_SAP};
for (const auto feature : features) {
if (feature & legacy_feature_set) {
*aidl_feature_set |= static_cast<uint32_t>(convertLegacyChipFeatureToAidl(feature));
diff --git a/wifi/legacy_headers/include/hardware_legacy/wifi_hal.h b/wifi/legacy_headers/include/hardware_legacy/wifi_hal.h
index dbcc152..4cabbe4 100644
--- a/wifi/legacy_headers/include/hardware_legacy/wifi_hal.h
+++ b/wifi/legacy_headers/include/hardware_legacy/wifi_hal.h
@@ -499,6 +499,8 @@
#define WIFI_FEATURE_SET_VOIP_MODE (uint64_t)0x1000000000 // Support Voip mode setting
#define WIFI_FEATURE_CACHED_SCAN_RESULTS (uint64_t)0x2000000000 // Support cached scan result report
#define WIFI_FEATURE_MLO_SAP (uint64_t)0x4000000000 // Support MLO SoftAp
+#define WIFI_FEATURE_MULTIPLE_MLD_ON_SAP \
+ (uint64_t)0x8000000000 // Support Multiple MLD SoftAp (Bridged Dual 11be SoftAp)
// Add more features here
#define IS_MASK_SET(mask, flags) (((flags) & (mask)) == (mask))