blob: 6c31e2b8827d37cdb5275519ebc099ef0fc39bc6 [file] [log] [blame]
/*
* Copyright (C) 2020 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 ANDROID_OS_VIBRATORHALCONTROLLER_H
#define ANDROID_OS_VIBRATORHALCONTROLLER_H
#include <android-base/thread_annotations.h>
#include <android/hardware/vibrator/IVibrator.h>
#include <vibratorservice/VibratorCallbackScheduler.h>
#include <vibratorservice/VibratorHalWrapper.h>
namespace android {
namespace vibrator {
std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler);
template <typename T>
using HalFunction = std::function<T(HalWrapper*)>;
// Controller for Vibrator HAL handle.
// This relies on a given Connector to connect to the underlying Vibrator HAL service and reconnects
// after each failed api call. This also ensures connecting to the service is thread-safe.
class HalController {
public:
using Connector =
std::function<std::shared_ptr<HalWrapper>(std::shared_ptr<CallbackScheduler>)>;
HalController() : HalController(std::make_shared<CallbackScheduler>(), &connectHal) {}
HalController(std::shared_ptr<CallbackScheduler> callbackScheduler, Connector connector)
: mConnector(connector),
mConnectedHal(nullptr),
mCallbackScheduler(std::move(callbackScheduler)) {}
virtual ~HalController() = default;
/* Connects to the newest HAL version available, possibly waiting for the registered service to
* become available. This will automatically be called at the first API usage if it was not
* manually called beforehand. Calling this manually during the setup phase can avoid slowing
* the first API call later on. Returns true if any HAL version is available, false otherwise.
*/
virtual bool init();
/* Reloads HAL service instance without waiting. This relies on the HAL version found by init()
* to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
*/
virtual void tryReconnect();
/* Returns info loaded from the connected HAL. This allows partial results to be returned if any
* of the Info fields has failed, but also retried on any failure.
*/
Info getInfo() {
static Info sDefaultInfo = InfoCache().get();
return apply<Info>([](HalWrapper* hal) { return hal->getInfo(); }, sDefaultInfo, "getInfo");
}
/* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
* result has failed. Parameter functionName is for logging purposes.
*/
template <typename T>
HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, const char* functionName) {
return apply(halFn, HalResult<T>::unsupported(), functionName);
}
private:
static constexpr int MAX_RETRIES = 1;
Connector mConnector;
std::mutex mConnectedHalMutex;
// Shared pointer to allow local copies to be used by different threads.
std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
// Shared pointer to allow copies to be passed to possible recreated mConnectedHal instances.
std::shared_ptr<CallbackScheduler> mCallbackScheduler;
/* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
* result has failed. Given default value is returned when no HAL is available, and given
* function name is for logging purposes.
*/
template <typename T>
T apply(const HalFunction<T>& halFn, T defaultValue, const char* functionName) {
if (!init()) {
ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
return defaultValue;
}
std::shared_ptr<HalWrapper> hal;
{
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
hal = mConnectedHal;
}
for (int i = 0; i < MAX_RETRIES; i++) {
T result = halFn(hal.get());
if (result.checkAndLogFailure(functionName)) {
tryReconnect();
} else {
return result;
}
}
return halFn(hal.get());
}
};
}; // namespace vibrator
}; // namespace android
#endif // ANDROID_OS_VIBRATORHALCONTROLLER_H