// Copyright (C) 2023 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 querying and searching functionality.
//!
//! This module provides various methods for retrieving and searching task
//! information, including filtering tasks by different criteria and handling
//! query-related events.
pub(crate) use ffi::TaskFilter;
use super::events::QueryEvent;
use super::TaskManager;
use crate::config::{Action, Mode};
use crate::manage::database::RequestDb;
use crate::service::permission::ManagerPermission;
use crate::task::config::TaskConfig;
use crate::task::info::{State, TaskInfo};
use crate::utils::is_valid_bundle_name;
/// Retrieves a task configuration by ID and token.
///
/// # Arguments
///
/// * `task_id` - The ID of the task to retrieve
/// * `token` - The authentication token for the task
///
/// # Returns
///
/// Returns `Some(TaskConfig)` if a task with the given ID exists and the token
/// matches, otherwise `None`.
pub(crate) fn get_task(task_id: u32, token: String) -> Option<TaskConfig> {
if let Some(config) = RequestDb::get_instance().get_task_config(task_id) {
if config.token.eq(token.as_str()) {
return Some(config);
}
return None;
}
None
}
/// Searches for tasks matching the given filter.
///
/// Supports both user-specific and system-wide searches.
///
/// # Arguments
///
/// * `filter` - The filter criteria for the search
/// * `method` - The search method to use (user-specific or system-wide)
///
/// # Returns
///
/// Returns a vector of task IDs that match the search criteria.
pub(crate) fn search(filter: TaskFilter, method: SearchMethod) -> Vec<u32> {
let database = RequestDb::get_instance();
match method {
SearchMethod::User(uid) => database.search_task(filter, uid),
SearchMethod::System(bundle_name) => database.system_search_task(filter, bundle_name),
}
}
impl TaskManager {
/// Handles a query event by processing the appropriate query operation.
///
/// Processes different types of query events (Show, Query, Touch) and sends
/// the result back through the provided channel.
///
/// # Arguments
///
/// * `event` - The query event to handle
pub(crate) fn handle_query_event(&self, event: QueryEvent) {
let (info, tx) = match event {
QueryEvent::Show(task_id, uid, tx) => {
let info = self.show(uid, task_id);
(info, tx)
}
QueryEvent::Query(task_id, action, tx) => {
let info = self.query(task_id, action);
(info, tx)
}
QueryEvent::Touch(task_id, uid, token, tx) => {
let info = self.touch(uid, task_id, token);
(info, tx)
}
};
let _ = tx.send(info);
}
/// Retrieves task information for a specific user.
///
/// Updates the task's progress in the database if the task is currently
/// running, then retrieves the task information if the UIDs match.
///
/// # Arguments
///
/// * `uid` - The user ID to verify ownership
/// * `task_id` - The ID of the task to retrieve
///
/// # Returns
///
/// Returns `Some(TaskInfo)` if the task exists and is owned by the
/// specified user, otherwise `None`.
pub(crate) fn show(&self, uid: u64, task_id: u32) -> Option<TaskInfo> {
if let Some(task) = self.scheduler.get_task(uid, task_id) {
task.update_progress_in_database()
}
match RequestDb::get_instance().get_task_info(task_id) {
Some(info) if info.uid() == uid => Some(info),
_ => {
info!("TaskManger Show: no task found");
None
}
}
}
/// Retrieves task information with token authentication.
///
/// Updates the task's progress in the database if the task is currently
/// running, then retrieves and sanitizes the task information if the
/// UIDs and token match.
///
/// # Arguments
///
/// * `uid` - The user ID to verify ownership
/// * `task_id` - The ID of the task to retrieve
/// * `token` - The authentication token for the task
///
/// # Returns
///
/// Returns `Some(TaskInfo)` with the bundle name sanitized if the task
/// exists, is owned by the specified user, and the token matches,
/// otherwise `None`.
pub(crate) fn touch(&self, uid: u64, task_id: u32, token: String) -> Option<TaskInfo> {
if let Some(task) = self.scheduler.get_task(uid, task_id) {
task.update_progress_in_database()
}
let mut info = match RequestDb::get_instance().get_task_info(task_id) {
Some(info) => info,
None => {
info!("TaskManger Touch: no task found");
return None;
}
};
if info.uid() == uid && info.token() == token {
info.bundle = "".to_string();
Some(info)
} else {
info!("TaskManger Touch: no task found");
None
}
}
/// Queries task information with action permission checking.
///
/// Updates the task's progress in the database if the task is currently
/// running, then retrieves and sanitizes the task information if the
/// action has sufficient permissions.
///
/// # Arguments
///
/// * `task_id` - The ID of the task to retrieve
/// * `action` - The action to check permissions against
///
/// # Returns
///
/// Returns `Some(TaskInfo)` with sensitive data sanitized if the task
/// exists and the action has sufficient permissions, otherwise `None`.
pub(crate) fn query(&self, task_id: u32, action: Action) -> Option<TaskInfo> {
if let Some(task) = self
.scheduler
.tasks()
.find(|task| task.task_id() == task_id)
{
task.update_progress_in_database()
}
let mut info = match RequestDb::get_instance().get_task_info(task_id) {
Some(info) => info,
None => {
info!("TaskManger Query: no task found");
return None;
}
};
let task_action = info.action();
if ManagerPermission::check_action(action, task_action) {
info.data = "".to_string();
info.url = "".to_string();
Some(info)
} else {
info!("TaskManger Query: no task found");
None
}
}
}
impl RequestDb {
/// Searches for tasks belonging to a specific user that match filter
/// criteria.
///
/// # Arguments
///
/// * `filter` - The filter criteria for the search
/// * `uid` - The user ID to filter by
///
/// # Returns
///
/// Returns a vector of task IDs that match the user and filter criteria.
pub(crate) fn search_task(&self, filter: TaskFilter, uid: u64) -> Vec<u32> {
let mut sql = format!("SELECT task_id from request_task WHERE uid = {} AND ", uid);
Self::search_filter(&mut sql, &filter);
self.query_integer(&sql)
}
/// Searches for tasks across the system with optional bundle filtering.
///
/// # Arguments
///
/// * `filter` - The filter criteria for the search
/// * `bundle_name` - The bundle name to filter by, or "*" for all bundles
///
/// # Returns
///
/// Returns a vector of task IDs that match the bundle and filter criteria.
pub(crate) fn system_search_task(&self, filter: TaskFilter, bundle_name: String) -> Vec<u32> {
if bundle_name != "*" && !is_valid_bundle_name(&bundle_name) {
error!("system_search_task: invalid bundle name {}", bundle_name);
return vec![];
}
let mut sql = "SELECT task_id from request_task WHERE ".to_string();
if bundle_name != "*" {
sql.push_str(&format!("bundle = '{}' AND ", bundle_name));
}
Self::search_filter(&mut sql, &filter);
self.query_integer(&sql)
}
/// Appends filter conditions to an SQL query string.
///
/// Adds conditions for time range, state, action, and mode to the provided
/// SQL query.
///
/// # Arguments
///
/// * `sql` - The SQL query string to modify
/// * `filter` - The filter criteria to apply
fn search_filter(sql: &mut String, filter: &TaskFilter) {
// Always include time range filtering
sql.push_str(&format!(
"ctime BETWEEN {} AND {} ",
filter.after, filter.before
));
// Only add state filter if not matching all states
if filter.state != State::Any.repr {
sql.push_str(&format!("AND state = {} ", filter.state));
}
// Only add action filter if not matching all actions
if filter.action != Action::Any.repr {
sql.push_str(&format!("AND action = {} ", filter.action));
}
// Only add mode filter if not matching all modes
if filter.mode != Mode::Any.repr {
sql.push_str(&format!("AND mode = {} ", filter.mode));
}
}
}
/// Retrieves the MIME type of a task belonging to a specific user.
///
/// # Arguments
///
/// * `uid` - The user ID to verify ownership
/// * `task_id` - The ID of the task to retrieve the MIME type for
///
/// # Returns
///
/// Returns the MIME type as a string if the task exists and is owned by the
/// specified user, otherwise an empty string.
pub(crate) fn query_mime_type(uid: u64, task_id: u32) -> String {
match RequestDb::get_instance().get_task_info(task_id) {
Some(info) if info.uid() == uid => info.mime_type(),
_ => {
info!("TaskManger QueryMimeType: no task found");
"".into()
}
}
}
/// Method for searching tasks, either by user or system-wide.
///
/// Used to determine whether a search should be restricted to a specific user
/// or performed system-wide with optional bundle filtering.
#[derive(Debug)]
pub(crate) enum SearchMethod {
/// Search tasks belonging to a specific user
User(u64),
/// Search tasks system-wide with bundle name filtering
System(String),
}
#[allow(unreachable_pub)]
#[cxx::bridge(namespace = "OHOS::Request")]
mod ffi {
// Filter criteria for searching tasks
#[derive(Debug)]
struct TaskFilter {
before: i64,
after: i64,
state: u8,
action: u8,
mode: u8,
}
}
#[cfg(test)]
// Unit tests for the query module
mod ut_query {
include!("../../tests/ut/manage/ut_query.rs");
}