* Copyright (C) 2024 Huawei Device Co., Ltd.
* 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 LOG_TAG
#define LOG_TAG "bt_fwk_switch_module"
#endif
#include "bluetooth_switch_module.h"
#include <algorithm>
#include "bluetooth_errorcode.h"
#include "bluetooth_log.h"
namespace OHOS {
namespace Bluetooth {
static const char *ToString(BluetoothSwitchEvent event)
{
switch (event) {
case BluetoothSwitchEvent::ENABLE_BLUETOOTH: return "ENABLE_BLUETOOTH";
case BluetoothSwitchEvent::DISABLE_BLUETOOTH: return "DISABLE_BLUETOOTH";
case BluetoothSwitchEvent::ENABLE_BLUETOOTH_TO_RESTRICE_MODE: return "ENABLE_BLUETOOTH_TO_RESTRICE_MODE";
case BluetoothSwitchEvent::BLUETOOTH_ON: return "BLUETOOTH_ON";
case BluetoothSwitchEvent::BLUETOOTH_OFF: return "BLUETOOTH_OFF";
case BluetoothSwitchEvent::BLUETOOTH_HALF: return "BLUETOOTH_HALF";
default: break;
}
return "Unknown";
}
void BluetoothSwitchModule::LogBluetoothSwitchEvent(BluetoothSwitchEvent event)
{
bool needLog = (event == BluetoothSwitchEvent::BLUETOOTH_ON ||
event == BluetoothSwitchEvent::BLUETOOTH_OFF ||
event == BluetoothSwitchEvent::BLUETOOTH_HALF) ? isBtSwitchProcessing_.load() : true;
if (needLog) {
HILOGI("Process Event: %{public}s", ToString(event));
}
}
int BluetoothSwitchModule::ProcessBluetoothSwitchEvent(
BluetoothSwitchEvent event, std::string callingName, bool isAsync)
{
CHECK_AND_RETURN_LOG_RET(switchAction_, BT_ERR_INTERNAL_ERROR, "switchAction is nullptr");
std::lock_guard<std::mutex> lock(bluetoothSwitchEventMutex_);
LogBluetoothSwitchEvent(event);
switch (event) {
case BluetoothSwitchEvent::ENABLE_BLUETOOTH:
return ProcessEnableBluetoothEvent(callingName, isAsync);
case BluetoothSwitchEvent::DISABLE_BLUETOOTH:
return ProcessDisableBluetoothEvent(callingName, isAsync);
case BluetoothSwitchEvent::ENABLE_BLUETOOTH_TO_RESTRICE_MODE:
return ProcessEnableBluetoothToRestrictModeEvent(callingName);
case BluetoothSwitchEvent::BLUETOOTH_ON:
return ProcessBluetoothOnEvent();
case BluetoothSwitchEvent::BLUETOOTH_OFF:
return ProcessBluetoothOffEvent();
case BluetoothSwitchEvent::BLUETOOTH_HALF:
return ProcessBluetoothHalfEvent();
default: break;
}
HILOGI("Invalid event: %{public}s", ToString(event));
return BT_ERR_INTERNAL_ERROR;
}
void BluetoothSwitchModule::OnTaskTimeout(void)
{
HILOGW("Bluetooth switch action timeout, clear resources");
std::lock_guard<std::mutex> lock(bluetoothSwitchEventMutex_);
isBtSwitchProcessing_ = false;
cachedEventVec_.clear();
}
int BluetoothSwitchModule::ProcessBluetoothSwitchAction(
std::function<int(void)> action, BluetoothSwitchCacheEvent cachedEvent)
{
if (isBtSwitchProcessing_.load()) {
cachedEventVec_.push_back(cachedEvent);
HILOGW("BtSwich action is processing, cache the %{public}s event", ToString(cachedEvent.event));
return BT_NO_ERROR;
}
ffrt::task_attr taskAttr;
taskAttr.name("bt_switch").delay(taskTimeout_);
taskTimeoutHandle_ = ffrtQueue_.submit_h([switchWptr = weak_from_this()]() {
auto switchSptr = switchWptr.lock();
if (switchSptr == nullptr) {
HILOGE("switchSptr is nullptr");
return;
}
switchSptr->OnTaskTimeout();
}, taskAttr);
isBtSwitchProcessing_ = true;
int ret = action();
if (ret != BT_NO_ERROR) {
isBtSwitchProcessing_ = false;
ffrtQueue_.cancel(taskTimeoutHandle_);
}
if (ret == BT_ERR_DIALOG_FOR_USER_CONFIRM || ret == BT_ERR_SWITCH_OP_TRANSFERRED) {
ret = BT_NO_ERROR;
}
return ret;
}
int BluetoothSwitchModule::ProcessEnableBluetoothEvent(const std::string &callingName, bool isAsync)
{
BluetoothSwitchCacheEvent cachedEvent = {
.event = BluetoothSwitchEvent::ENABLE_BLUETOOTH,
.callingName = callingName,
};
return ProcessBluetoothSwitchAction(
[this, callingName, isAsync]() {
bool noAutoConnect = noAutoConnect_.load();
if (noAutoConnect) {
SetNoAutoConnect(false);
}
return switchAction_->EnableBluetooth(noAutoConnect, callingName, isAsync);
},
cachedEvent);
}
int BluetoothSwitchModule::ProcessDisableBluetoothEvent(const std::string &callingName, bool isAsync)
{
BluetoothSwitchCacheEvent cachedEvent = {
.event = BluetoothSwitchEvent::DISABLE_BLUETOOTH,
.callingName = callingName,
};
return ProcessBluetoothSwitchAction(
[this, callingName, isAsync]() { return switchAction_->DisableBluetooth(callingName, isAsync); },
cachedEvent);
}
int BluetoothSwitchModule::ProcessEnableBluetoothToRestrictModeEvent(const std::string &callingName)
{
BluetoothSwitchCacheEvent cachedEvent = {
.event = BluetoothSwitchEvent::ENABLE_BLUETOOTH_TO_RESTRICE_MODE,
.callingName = callingName,
};
return ProcessBluetoothSwitchAction(
[this, callingName]() { return switchAction_->EnableBluetoothToRestrictMode(callingName); },
cachedEvent);
}
int BluetoothSwitchModule::ProcessBluetoothOnEvent(void)
{
return ProcessBluetoothSwitchActionEnd(
BluetoothSwitchEvent::ENABLE_BLUETOOTH,
{BluetoothSwitchEvent::DISABLE_BLUETOOTH});
}
int BluetoothSwitchModule::ProcessBluetoothOffEvent(void)
{
return ProcessBluetoothSwitchActionEnd(
BluetoothSwitchEvent::DISABLE_BLUETOOTH,
{BluetoothSwitchEvent::ENABLE_BLUETOOTH, BluetoothSwitchEvent::ENABLE_BLUETOOTH_TO_RESTRICE_MODE});
}
int BluetoothSwitchModule::ProcessBluetoothHalfEvent(void)
{
return ProcessBluetoothSwitchActionEnd(
BluetoothSwitchEvent::ENABLE_BLUETOOTH_TO_RESTRICE_MODE,
{BluetoothSwitchEvent::ENABLE_BLUETOOTH, BluetoothSwitchEvent::DISABLE_BLUETOOTH});
}
int BluetoothSwitchModule::ProcessBluetoothSwitchActionEnd(
BluetoothSwitchEvent curSwitchActionEvent, std::vector<BluetoothSwitchEvent> expectedEventVec)
{
isBtSwitchProcessing_ = false;
ffrtQueue_.cancel(taskTimeoutHandle_);
DeduplicateCacheEvent(curSwitchActionEvent);
auto it = std::find_if(cachedEventVec_.begin(), cachedEventVec_.end(), [&expectedEventVec](auto cacheEvent) {
return std::find(expectedEventVec.begin(), expectedEventVec.end(), cacheEvent.event) != expectedEventVec.end();
});
if (it != cachedEventVec_.end()) {
if (it != cachedEventVec_.begin()) {
LogCacheEventIgnored(std::vector<BluetoothSwitchCacheEvent>(cachedEventVec_.begin(), it));
}
BluetoothSwitchEvent event = (*it).event;
std::string callingName = (*it).callingName;
cachedEventVec_.erase(cachedEventVec_.begin(), it + 1);
return ProcessBluetoothSwitchCachedEvent(event, callingName);
}
cachedEventVec_.clear();
return BT_NO_ERROR;
}
int BluetoothSwitchModule::ProcessBluetoothSwitchCachedEvent(BluetoothSwitchEvent event, const std::string &callingName)
{
HILOGI("Auto process cached %{public}s event", ToString(event));
ffrtQueue_.submit([switchWptr = weak_from_this(), event, callingName]() {
auto switchSptr = switchWptr.lock();
if (switchSptr == nullptr) {
HILOGE("switchSptr is nullptr");
return;
}
switchSptr->ProcessBluetoothSwitchEvent(event, callingName);
});
return BT_NO_ERROR;
}
void BluetoothSwitchModule::DeduplicateCacheEvent(BluetoothSwitchEvent curEvent)
{
auto it = std::find_if(cachedEventVec_.rbegin(), cachedEventVec_.rend(),
[&curEvent](const BluetoothSwitchCacheEvent &cachedEvent) { return cachedEvent.event == curEvent; });
if (it != cachedEventVec_.rend()) {
size_t pos = static_cast<size_t>(std::distance(cachedEventVec_.begin(), it.base())) - 1;
LogCacheEventIgnored(
std::vector<BluetoothSwitchCacheEvent>(cachedEventVec_.begin(), cachedEventVec_.begin() + pos + 1));
cachedEventVec_.erase(cachedEventVec_.begin(), cachedEventVec_.begin() + pos + 1);
}
}
void BluetoothSwitchModule::LogCacheEventIgnored(std::vector<BluetoothSwitchCacheEvent> eventVec)
{
std::string log = "";
for (size_t i = 0; i < eventVec.size(); i++) {
log += ToString(eventVec[i].event);
log += " ";
}
if (!log.empty()) {
HILOGW("Ignore cache event: %{public}s", log.c_str());
}
}
void BluetoothSwitchModule::SetNoAutoConnect(bool noAutoConnect)
{
noAutoConnect_ = noAutoConnect;
}
}
}