// 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.
//! Task handling utilities for notification bar management.
//!
//! This module provides functionality for handling notification-related tasks,
//! including task cancellation, group attachment, notification checking, and
//! subscription management for the notification bar.
use std::sync::atomic::Ordering;
use super::database::NotificationDb;
use super::ffi::{self, SubscribeNotification};
use super::NotificationDispatcher;
use crate::config::{Mode, Version};
use crate::error::ErrorCode;
use crate::info::{State, TaskInfo};
use crate::manage::database::RequestDb;
use crate::manage::events::TaskManagerEvent;
use crate::manage::task_manager::TaskManagerTx;
use crate::manage::TaskManager;
use crate::task::request_task::RequestTask;
use crate::utils::Recv;
/// Cancels a notification for a specific task.
///
/// Sends a request to the FFI layer to cancel a notification with the given request ID.
///
/// # Arguments
///
/// * `request_id` - ID of the task notification to cancel
pub(super) fn cancel_notification(request_id: u32) {
info!("cancel notification {}", request_id);
let ret = ffi::CancelNotification(request_id);
if ret != 0 {
error!("cancel notification failed {}", ret);
}
}
impl TaskManager {
/// Attaches multiple tasks to a notification group.
///
/// Validates that all tasks meet the requirements for group attachment and then
/// attaches them to the specified notification group.
///
/// # Arguments
///
/// * `uid` - User ID associated with the tasks
/// * `task_ids` - List of task IDs to attach to the group
/// * `group_id` - ID of the target notification group
///
/// # Returns
///
/// `ErrorCode::ErrOk` if successful, or an appropriate error code if validation fails
pub(crate) fn attach_group(&self, uid: u64, task_ids: Vec<u32>, group_id: u32) -> ErrorCode {
// Validate all tasks meet requirements for group attachment
for task_id in task_ids.iter().copied() {
// Check if task exists
let Some(mode) = RequestDb::get_instance().query_task_mode(task_id) else {
return ErrorCode::TaskNotFound;
};
// Verify task is in background mode
if mode != Mode::BackGround {
return ErrorCode::TaskModeErr;
}
// Verify task is in initialized state
let Some(state) = RequestDb::get_instance().query_task_state(task_id) else {
return ErrorCode::TaskNotFound;
};
if state != State::Initialized.repr {
return ErrorCode::TaskStateErr;
}
}
// Attempt to attach tasks to group
if !NotificationDispatcher::get_instance().attach_group(task_ids, group_id, uid) {
return ErrorCode::GroupNotFound;
}
ErrorCode::ErrOk
}
}
/// Trait for checking if a task is eligible for notifications.
///
/// Implemented for types that need to determine notification availability
/// based on their configuration and state.
pub(crate) trait NotificationCheck {
/// Checks if notifications are enabled and available for this object.
///
/// # Arguments
///
/// * `db` - Reference to the notification database for configuration lookups
///
/// # Returns
///
/// Boolean indicating whether notifications should be shown
fn notification_check(&self, db: &NotificationDb) -> bool;
}
impl NotificationCheck for RequestTask {
/// Checks if notifications should be shown for this RequestTask.
///
/// Combines configuration checks with database configuration lookup to determine
/// notification eligibility.
///
/// # Arguments
///
/// * `db` - Reference to the notification database
///
/// # Returns
///
/// Boolean indicating whether notifications should be shown
fn notification_check(&self, db: &NotificationDb) -> bool {
let mode = self.mode.load(Ordering::Acquire);
notification_check_common(
self.conf.version,
true,
Mode::from(mode),
self.conf.common_data.background,
false,
) && db.check_task_notification_available(&self.conf.common_data.task_id)
}
}
impl NotificationCheck for TaskInfo {
/// Checks if notifications should be shown for this TaskInfo.
///
/// Determines notification eligibility based on task version, gauge setting,
/// mode, background status, and database configuration.
///
/// # Arguments
///
/// * `db` - Reference to the notification database
///
/// # Returns
///
/// Boolean indicating whether notifications should be shown
fn notification_check(&self, db: &NotificationDb) -> bool {
notification_check_common(
Version::from(self.common_data.version),
self.common_data.gauge,
Mode::from(self.common_data.mode),
RequestDb::get_instance().query_task_background(self.common_data.task_id),
true,
) && db.check_task_notification_available(&self.common_data.task_id)
}
}
/// Common notification eligibility check for different task types.
///
/// Determines if notifications should be shown based on task version, mode,
/// gauge setting, background status, and completion status.
///
/// # Arguments
///
/// * `version` - API version of the task
/// * `gauge` - Whether the task should show progress gauge notifications
/// * `mode` - Current mode of the task
/// * `background` - Whether the task is running in background
/// * `completed_notify` - Whether the task should show completion notifications
///
/// # Returns
///
/// Boolean indicating whether notifications should be shown
fn notification_check_common(
version: Version,
gauge: bool,
mode: Mode,
background: bool,
completed_notify: bool,
) -> bool {
// API 10 requirements: background mode and either gauge or completed notification
// API 9 requirement: background flag only
version == Version::API10 && mode == Mode::BackGround && (gauge || completed_notify)
|| version == Version::API9 && background
}
/// Wrapper around TaskManager for notification-related task operations.
///
/// Provides a simplified interface for performing task operations triggered
/// by notification interactions.
pub struct TaskManagerWrapper {
/// Channel for sending events to the task manager
task_manager: TaskManagerTx,
}
impl TaskManagerWrapper {
/// Creates a new TaskManagerWrapper instance.
///
/// # Arguments
///
/// * `task_manager` - Channel for sending events to the task manager
fn new(task_manager: TaskManagerTx) -> Self {
Self { task_manager }
}
/// Pauses a task.
///
/// # Arguments
///
/// * `task_id` - ID of the task to pause
///
/// # Returns
///
/// Boolean indicating whether the operation was successful
pub(crate) fn pause_task(&self, task_id: u32) -> bool {
self.event_inner(task_id, TaskManagerEvent::pause)
}
/// Resumes a paused task.
///
/// # Arguments
///
/// * `task_id` - ID of the task to resume
///
/// # Returns
///
/// Boolean indicating whether the operation was successful
pub(crate) fn resume_task(&self, task_id: u32) -> bool {
self.event_inner(task_id, TaskManagerEvent::resume)
}
/// Stops a running task.
///
/// # Arguments
///
/// * `task_id` - ID of the task to stop
///
/// # Returns
///
/// Boolean indicating whether the operation was successful
pub(crate) fn stop_task(&self, task_id: u32) -> bool {
self.event_inner(task_id, TaskManagerEvent::stop)
}
/// Internal function for sending task events and handling responses.
///
/// # Type Parameters
///
/// * `F` - Function that creates a task event
///
/// # Arguments
///
/// * `task_id` - ID of the task to operate on
/// * `f` - Function that creates a task event with a receive channel for response
///
/// # Returns
///
/// Boolean indicating whether the operation was successful
fn event_inner<F>(&self, task_id: u32, f: F) -> bool
where
F: Fn(u64, u32) -> (TaskManagerEvent, Recv<ErrorCode>),
{
// Get task owner's UID
let Some(uid) = RequestDb::get_instance().query_task_uid(task_id) else {
return false;
};
// Create and send event
let (event, rx) = f(uid, task_id);
self.task_manager.send_event(event);
// Wait for and process response
let Some(ret) = rx.get() else {
return false;
};
if ret != ErrorCode::ErrOk {
error!("notification_bar {} failed: {}", task_id, ret as u32);
return false;
}
true
}
}
/// Subscribes to notification bar events and connects them to task management.
///
/// Creates a TaskManagerWrapper and registers it with the notification system
/// to handle user interactions with notifications.
///
/// # Arguments
///
/// * `task_manager` - Channel for sending task management events
pub(crate) fn subscribe_notification_bar(task_manager: TaskManagerTx) {
SubscribeNotification(Box::new(TaskManagerWrapper::new(task_manager)));
}
impl RequestDb {
/// Queries whether a task is running in background mode.
///
/// Executes a SQL query to check the task's background status from the database.
///
/// # Arguments
///
/// * `task_id` - ID of the task to query
///
/// # Returns
///
/// Boolean indicating whether the task is running in background
fn query_task_background(&self, task_id: u32) -> bool {
let sql = format!(
"SELECT background FROM request_task WHERE task_id = {}",
task_id
);
self.query_integer(&sql)
.first()
.map(|background: &i32| *background == 1)
.unwrap_or(false)
}
/// Queries the mode of a specific task.
///
/// # Arguments
///
/// * `task_id` - ID of the task to query
///
/// # Returns
///
/// `Some(Mode)` with the task's mode if found, `None` otherwise
pub(crate) fn query_task_mode(&self, task_id: u32) -> Option<Mode> {
let sql = format!("SELECT mode FROM request_task WHERE task_id = {}", task_id);
self.query_integer(&sql)
.first()
.map(|mode: &i32| Mode::from(*mode as u8))
}
}