Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 610ca0e6 authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk
Browse files

Implement Minimal Telephony

Bug: 310710841
Test: atest CtsTelephonyTestCases
Test: atest CtsCarrierApiTestCases
Change-Id: Ia1a5567419871a9c64abb38f2f0e0951cad3fd7b
parent f6b6eccc
Loading
Loading
Loading
Loading
+85 −0
Original line number Diff line number Diff line
// 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",
    ],
}
+23 −0
Original line number Diff line number Diff line
/*
 * 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
+165 −0
Original line number Diff line number Diff line
/*
 * 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
+27 −0
Original line number Diff line number Diff line
/*
 * 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
+119 −0
Original line number Diff line number Diff line
/*
 * 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
Loading