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

Commit f3262f97 authored by Gabriel Biren's avatar Gabriel Biren Committed by Mahesh KKV
Browse files

Add AIDL implementation for the vendor HAL service.

Bug: 205044134
Test: Pass AIDL VTS tests and regression tests.
Change-Id: Iad04ce01f71fc220443e05a4be05d7d5545227e8
parent da9b3895
Loading
Loading
Loading
Loading
+214 −0
Original line number Diff line number Diff line
// Copyright (C) 2022 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 {
    default_applicable_licenses: ["hardware_interfaces_license"],
}

soong_config_module_type {
    name: "wifi_hal_cc_defaults",
    module_type: "cc_defaults",
    config_namespace: "wifi",
    bool_variables: [
        "hidl_feature_aware", // WIFI_HIDL_FEATURE_AWARE
        "hidl_feature_dual_interface", // WIFI_HIDL_FEATURE_DUAL_INTERFACE
        "hidl_feature_disable_ap", // WIFI_HIDL_FEATURE_DISABLE_AP
        "hidl_feature_disable_ap_mac_randomization", // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
        "avoid_iface_reset_mac_change", // WIFI_AVOID_IFACE_RESET_MAC_CHANGE
    ],
    value_variables: [
        "hal_interface_combinations", // WIFI_HAL_INTERFACE_COMBINATIONS
    ],
    properties: [
        "cppflags",
    ],
}

wifi_hal_cc_defaults {
    name: "android.hardware.wifi-service-cppflags-defaults",
    soong_config_variables: {
        hidl_feature_aware: {
            cppflags: ["-DWIFI_HIDL_FEATURE_AWARE"],
        },
        hidl_feature_dual_interface: {
            cppflags: ["-DWIFI_HIDL_FEATURE_DUAL_INTERFACE"],
        },
        hidl_feature_disable_ap: {
            cppflags: ["-DWIFI_HIDL_FEATURE_DISABLE_AP"],
        },
        hidl_feature_disable_ap_mac_randomization: {
            cppflags: ["-DWIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION"],
        },
        avoid_iface_reset_mac_change: {
            cppflags: ["-DWIFI_AVOID_IFACE_RESET_MAC_CHANGE"],
        },
        hal_interface_combinations: {
            cppflags: ["-DWIFI_HAL_INTERFACE_COMBINATIONS=%s"],
        },
    },
}

cc_library_static {
    name: "android.hardware.wifi-service-lib",
    defaults: ["android.hardware.wifi-service-cppflags-defaults"],
    proprietary: true,
    compile_multilib: "first",
    cppflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],
    // Allow implicit fallthroughs in wifi_legacy_hal.cpp until they are fixed.
    cflags: ["-Wno-error=implicit-fallthrough"],
    srcs: [
        "aidl_struct_util.cpp",
        "aidl_sync_util.cpp",
        "ringbuffer.cpp",
        "wifi.cpp",
        "wifi_ap_iface.cpp",
        "wifi_chip.cpp",
        "wifi_feature_flags.cpp",
        "wifi_iface_util.cpp",
        "wifi_legacy_hal.cpp",
        "wifi_legacy_hal_factory.cpp",
        "wifi_legacy_hal_stubs.cpp",
        "wifi_mode_controller.cpp",
        "wifi_nan_iface.cpp",
        "wifi_p2p_iface.cpp",
        "wifi_rtt_controller.cpp",
        "wifi_sta_iface.cpp",
        "wifi_status_util.cpp",
    ],

    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libcutils",
        "liblog",
        "libnl",
        "libutils",
        "libwifi-hal",
        "libwifi-system-iface",
        "libxml2",
        "android.hardware.wifi-V1-ndk",
    ],

    export_include_dirs: ["."],
}

cc_binary {
    name: "android.hardware.wifi-service",
    vintf_fragments: ["android.hardware.wifi-service.xml"],
    relative_install_path: "hw",
    proprietary: true,
    cppflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],
    srcs: ["service.cpp"],
    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libcutils",
        "liblog",
        "libnl",
        "libutils",
        "libwifi-hal",
        "libwifi-system-iface",
        "libxml2",
        "android.hardware.wifi-V1-ndk",
    ],
    static_libs: ["android.hardware.wifi-service-lib"],
    init_rc: ["android.hardware.wifi-service.rc"],
}

cc_binary {
    name: "android.hardware.wifi-service-lazy",
    vintf_fragments: ["android.hardware.wifi-service.xml"],
    overrides: ["android.hardware.wifi-service"],
    cflags: ["-DLAZY_SERVICE"],
    relative_install_path: "hw",
    proprietary: true,
    cppflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],
    srcs: ["service.cpp"],
    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libcutils",
        "liblog",
        "libnl",
        "libutils",
        "libwifi-hal",
        "libwifi-system-iface",
        "libxml2",
        "android.hardware.wifi-V1-ndk",
    ],
    static_libs: ["android.hardware.wifi-service-lib"],
    init_rc: ["android.hardware.wifi-service-lazy.rc"],
}

cc_test {
    name: "android.hardware.wifi-service-tests",
    proprietary: true,
    compile_multilib: "first",
    cppflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],
    srcs: [
        "tests/aidl_struct_util_unit_tests.cpp",
        "tests/main.cpp",
        "tests/mock_interface_tool.cpp",
        "tests/mock_wifi_feature_flags.cpp",
        "tests/mock_wifi_iface_util.cpp",
        "tests/mock_wifi_legacy_hal.cpp",
        "tests/mock_wifi_mode_controller.cpp",
        "tests/ringbuffer_unit_tests.cpp",
        "tests/wifi_nan_iface_unit_tests.cpp",
        "tests/wifi_chip_unit_tests.cpp",
        "tests/wifi_iface_util_unit_tests.cpp",
    ],
    static_libs: [
        "libgmock",
        "libgtest",
        "android.hardware.wifi-V1-ndk",
        "android.hardware.wifi-service-lib",
    ],
    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libcutils",
        "liblog",
        "libnl",
        "libutils",
        "libwifi-hal",
        "libwifi-system-iface",
    ],
}

filegroup {
    name: "default-android.hardware.wifi-service.rc",
    srcs: ["android.hardware.wifi-service.rc"],
}

filegroup {
    name: "default-android.hardware.wifi-service.xml",
    srcs: ["android.hardware.wifi-service.xml"],
}
+35 −0
Original line number Diff line number Diff line
Vendor HAL Threading Model
==========================
The vendor HAL service has two threads:
1. AIDL thread: This is the main thread which processes all the incoming AIDL
RPC's.
2. Legacy HAL event loop thread: This is the thread forked off for processing
the legacy HAL event loop (wifi_event_loop()). This thread is used to process
any asynchronous netlink events posted by the driver. Any asynchronous
callbacks passed to the legacy HAL API's are invoked on this thread.

Synchronization Concerns
========================
wifi_legacy_hal.cpp has a bunch of global "C" style functions to handle the
legacy callbacks. Each of these "C" style functions invokes a corresponding
"std::function" version of the callback which does the actual processing.
The variables holding these "std::function" callbacks are reset from the AIDL
thread when they are no longer used. For example: stopGscan() will reset the
corresponding "on_gscan_*" callback variables which were set when startGscan()
was invoked. This is not thread safe since these callback variables are
accesed from the legacy hal event loop thread as well.

Synchronization Solution
========================
Adding a global lock seems to be the most trivial solution to the problem.
a) All of the asynchronous "C" style callbacks will acquire the global lock
before invoking the corresponding "std::function" callback variables.
b) All of the AIDL methods will also acquire the global lock before processing
(in aidl_return_util::validateAndCall()).

Note: It's important that we only acquire the global lock for asynchronous
callbacks, because there is no guarantee (or documentation to clarify) that the
synchronous callbacks are invoked on the same invocation thread. If that is not
the case in some implementation, we will end up deadlocking the system since the
AIDL thread would have acquired the global lock which is needed by the
synchronous callback executed on the legacy hal event loop thread.
+140 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

#ifndef AIDL_CALLBACK_UTIL_H_
#define AIDL_CALLBACK_UTIL_H_

#include <android-base/logging.h>

#include <set>
#include <unordered_map>

namespace {
std::unordered_map<void* /* callback */, void* /* handler */> callback_handler_map_;
}

namespace aidl {
namespace android {
namespace hardware {
namespace wifi {
namespace aidl_callback_util {

// Provides a class to manage callbacks for the various AIDL interfaces and
// handle the death of the process hosting each callback.
template <typename CallbackType>
class AidlCallbackHandler {
  public:
    AidlCallbackHandler() {
        death_handler_ = AIBinder_DeathRecipient_new(AidlCallbackHandler::onCallbackDeath);
    }
    ~AidlCallbackHandler() { invalidate(); }

    bool addCallback(const std::shared_ptr<CallbackType>& cb) {
        void* cbPtr = reinterpret_cast<void*>(cb->asBinder().get());
        const auto& cbPosition = findCbInSet(cbPtr);
        if (cbPosition != cb_set_.end()) {
            LOG(WARNING) << "Duplicate death notification registration";
            return true;
        }

        if (AIBinder_linkToDeath(cb->asBinder().get(), death_handler_, cbPtr /* cookie */) !=
            STATUS_OK) {
            LOG(ERROR) << "Failed to register death notification";
            return false;
        }

        callback_handler_map_[cbPtr] = reinterpret_cast<void*>(this);
        cb_set_.insert(cb);
        return true;
    }

    const std::set<std::shared_ptr<CallbackType>>& getCallbacks() { return cb_set_; }

    void invalidate() {
        for (auto cb : cb_set_) {
            void* cookie = reinterpret_cast<void*>(cb->asBinder().get());
            if (AIBinder_unlinkToDeath(cb->asBinder().get(), death_handler_, cookie) != STATUS_OK) {
                LOG(ERROR) << "Failed to deregister death notification";
            }
            if (!removeCbFromHandlerMap(cookie)) {
                LOG(ERROR) << "Failed to remove callback from handler map";
            }
        }
        cb_set_.clear();
    }

    // Entry point for the death handling logic. AIBinder_DeathRecipient
    // can only call a static function, so use the cookie to find the
    // proper handler and route the request there.
    static void onCallbackDeath(void* cookie) {
        auto cbQuery = callback_handler_map_.find(cookie);
        if (cbQuery == callback_handler_map_.end()) {
            LOG(ERROR) << "Invalid death cookie received";
            return;
        }

        AidlCallbackHandler* cbHandler = reinterpret_cast<AidlCallbackHandler*>(cbQuery->second);
        if (cbHandler == nullptr) {
            LOG(ERROR) << "Handler mapping contained an invalid handler";
            return;
        }
        cbHandler->handleCallbackDeath(cbQuery->first);
    }

  private:
    std::set<std::shared_ptr<CallbackType>> cb_set_;
    AIBinder_DeathRecipient* death_handler_;

    typename std::set<std::shared_ptr<CallbackType>>::iterator findCbInSet(void* cbPtr) {
        const auto& cbPosition = std::find_if(
                cb_set_.begin(), cb_set_.end(), [cbPtr](const std::shared_ptr<CallbackType>& p) {
                    return cbPtr == reinterpret_cast<void*>(p->asBinder().get());
                });
        return cbPosition;
    }

    bool removeCbFromHandlerMap(void* cbPtr) {
        auto cbQuery = callback_handler_map_.find(cbPtr);
        if (cbQuery != callback_handler_map_.end()) {
            callback_handler_map_.erase(cbQuery);
            return true;
        }
        return false;
    }

    void handleCallbackDeath(void* cbPtr) {
        const auto& cbPosition = findCbInSet(cbPtr);
        if (cbPosition == cb_set_.end()) {
            LOG(ERROR) << "Unknown callback death notification received";
            return;
        }
        cb_set_.erase(cbPosition);

        if (!removeCbFromHandlerMap(cbPtr)) {
            LOG(ERROR) << "Callback was not in callback handler map";
        }
    }

    DISALLOW_COPY_AND_ASSIGN(AidlCallbackHandler);
};

}  // namespace aidl_callback_util
}  // namespace wifi
}  // namespace hardware
}  // namespace android
}  // namespace aidl

#endif  // AIDL_CALLBACK_UTIL_H_
+85 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

#ifndef AIDL_RETURN_UTIL_H_
#define AIDL_RETURN_UTIL_H_

#include "aidl_sync_util.h"
#include "wifi_status_util.h"

namespace aidl {
namespace android {
namespace hardware {
namespace wifi {
namespace aidl_return_util {
using aidl::android::hardware::wifi::WifiStatusCode;
using aidl::android::hardware::wifi::aidl_sync_util::acquireGlobalLock;

/**
 * These utility functions are used to invoke a method on the provided
 * AIDL interface object.
 * These functions checks if the provided AIDL interface object is valid.
 * a) If valid, Invokes the corresponding internal implementation function of
 * the AIDL method.
 * b) If invalid, return without calling the internal implementation function.
 */

// Use for AIDL methods which return only an AIDL status.
template <typename ObjT, typename WorkFuncT, typename... Args>
::ndk::ScopedAStatus validateAndCall(ObjT* obj, WifiStatusCode status_code_if_invalid,
                                     WorkFuncT&& work, Args&&... args) {
    const auto lock = acquireGlobalLock();
    if (obj->isValid()) {
        return (obj->*work)(std::forward<Args>(args)...);
    } else {
        return createWifiStatus(status_code_if_invalid);
    }
}

// Use for AIDL methods which return only an AIDL status.
// This version passes the global lock acquired to the body of the method.
template <typename ObjT, typename WorkFuncT, typename... Args>
::ndk::ScopedAStatus validateAndCallWithLock(ObjT* obj, WifiStatusCode status_code_if_invalid,
                                             WorkFuncT&& work, Args&&... args) {
    auto lock = acquireGlobalLock();
    if (obj->isValid()) {
        return (obj->*work)(&lock, std::forward<Args>(args)...);
    } else {
        return createWifiStatus(status_code_if_invalid);
    }
}

// Use for AIDL methods which have a return value along with the AIDL status
template <typename ObjT, typename WorkFuncT, typename ReturnT, typename... Args>
::ndk::ScopedAStatus validateAndCall(ObjT* obj, WifiStatusCode status_code_if_invalid,
                                     WorkFuncT&& work, ReturnT* ret_val, Args&&... args) {
    const auto lock = acquireGlobalLock();
    if (obj->isValid()) {
        auto call_pair = (obj->*work)(std::forward<Args>(args)...);
        *ret_val = call_pair.first;
        return std::forward<::ndk::ScopedAStatus>(call_pair.second);
    } else {
        return ndk::ScopedAStatus::fromServiceSpecificError(
                static_cast<int32_t>(status_code_if_invalid));
    }
}

}  // namespace aidl_return_util
}  // namespace wifi
}  // namespace hardware
}  // namespace android
}  // namespace aidl
#endif  // AIDL_RETURN_UTIL_H_
+2775 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading