Implement Minimal Telephony
Bug: 310710841
Test: atest CtsTelephonyTestCases
Test: atest CtsCarrierApiTestCases
Change-Id: Ia1a5567419871a9c64abb38f2f0e0951cad3fd7b
diff --git a/radio/aidl/minradio/libminradio/Android.bp b/radio/aidl/minradio/libminradio/Android.bp
new file mode 100644
index 0000000..d920a3a
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/Android.bp
@@ -0,0 +1,85 @@
+// 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",
+ "modem/RadioModemResponseTracker.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..739cd8c
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/modem/RadioModem.h
@@ -0,0 +1,77 @@
+/*
+ * 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/modem/RadioModemResponseTracker.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:
+ std::shared_ptr<RadioModemResponseTracker> mResponseTracker;
+ 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/modem/RadioModemResponseTracker.h b/radio/aidl/minradio/libminradio/include/libminradio/modem/RadioModemResponseTracker.h
new file mode 100644
index 0000000..e6e7b9e
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/modem/RadioModemResponseTracker.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 <libminradio/ResponseTracker.h>
+
+#include <aidl/android/hardware/radio/modem/BnRadioModemResponse.h>
+#include <aidl/android/hardware/radio/modem/IRadioModem.h>
+
+namespace android::hardware::radio::minimal {
+
+class RadioModemResponseTracker
+ : public ResponseTracker<::aidl::android::hardware::radio::modem::IRadioModem,
+ ::aidl::android::hardware::radio::modem::IRadioModemResponse> {
+ public:
+ RadioModemResponseTracker(
+ std::shared_ptr<::aidl::android::hardware::radio::modem::IRadioModem> req,
+ const std::shared_ptr<::aidl::android::hardware::radio::modem::IRadioModemResponse>&
+ resp);
+
+ // TODO(now): remove if not needed
+ ResponseTrackerResult<::aidl::android::hardware::radio::modem::ImeiInfo> getImei();
+
+ protected:
+ ::ndk::ScopedAStatus getImeiResponse(
+ const ::aidl::android::hardware::radio::RadioResponseInfo& info,
+ const std::optional<::aidl::android::hardware::radio::modem::ImeiInfo>& imei) override;
+};
+
+} // 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..489d59b
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/include/libminradio/sim/Filesystem.h
@@ -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.
+ */
+#pragma once
+
+#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
+
+#include <map>
+#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);
+
+ 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);
+};
+
+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..1c1c5fa
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/modem/RadioModem.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 = mResponseTracker =
+ ndk::SharedRefBase::make<RadioModemResponseTracker>(ref<aidl::IRadioModem>(), 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/modem/RadioModemResponseTracker.cpp b/radio/aidl/minradio/libminradio/modem/RadioModemResponseTracker.cpp
new file mode 100644
index 0000000..eb9bcf7
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/modem/RadioModemResponseTracker.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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/modem/BnRadioModemResponse.h>
+#undef __assert2
+#undef __noreturn__
+#include <cassert>
+
+#include <libminradio/modem/RadioModemResponseTracker.h>
+
+#include <libminradio/debug.h>
+
+#define RADIO_MODULE "ModemResponse"
+
+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::modem;
+
+RadioModemResponseTracker::RadioModemResponseTracker(
+ std::shared_ptr<aidl::IRadioModem> req,
+ const std::shared_ptr<aidl::IRadioModemResponse>& resp)
+ : ResponseTracker(req, resp) {}
+
+ResponseTrackerResult<aidl::ImeiInfo> RadioModemResponseTracker::getImei() {
+ auto serial = newSerial();
+ if (auto status = request()->getImei(serial); !status.isOk()) return status;
+ return getResult<aidl::ImeiInfo>(serial);
+}
+
+ScopedAStatus RadioModemResponseTracker::getImeiResponse(
+ const RadioResponseInfo& info, const std::optional<aidl::ImeiInfo>& respData) {
+ LOG_CALL_RESPONSE << respData;
+ if (isTracked(info.serial)) return handle(info, respData.value_or(aidl::ImeiInfo{}));
+ return IRadioModemResponseDelegator::getImeiResponse(info, respData);
+}
+
+} // 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..65c92b1
--- /dev/null
+++ b/radio/aidl/minradio/libminradio/sim/Filesystem.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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
+}
+
+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
+}
+
+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::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
+}