// Copyright (C) 2025 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.
//! Unix Domain Socket (UDS) communication for the RequestProxy.
//!
//! This module implements methods for establishing and managing Unix Domain
//! Socket communication channels with the download service, as well as task
//! subscription and unsubscription functionality.
// Standard library dependencies
use std::fs::File;
use std::os::fd::{IntoRawFd, RawFd};
// IPC and download core dependencies
use ipc::parcel::MsgParcel;
use request_core::interface;
// Local dependencies
use super::{RequestProxy, SERVICE_TOKEN};
impl RequestProxy {
/// Opens a Unix Domain Socket communication channel with the download
/// service.
///
/// Requests the download service to create a new file descriptor for
/// establishing a direct communication channel. Returns the file
/// descriptor wrapped in a `File` object if successful.
///
/// # Returns
/// - `Ok(File)` with the file descriptor for the communication channel if
/// successful
/// - `Err(i32)` with the error code if opening the channel failed
///
/// # Examples
///
/// ```rust,no_run
/// use request_next::proxy::RequestProxy;
///
/// fn example() {
/// let proxy = RequestProxy::get_instance();
///
/// match proxy.open_channel() {
/// Ok(file) => println!("Communication channel opened successfully"),
/// Err(code) => eprintln!("Failed to open channel with error code: {}", code),
/// }
/// }
/// ```
///
/// # Errors
/// Returns an error with code from the download service if the channel
/// cannot be opened
///
/// # Panics
/// - Panics if parcel operations fail due to IPC errors
pub(crate) fn open_channel(&self) -> Result<File, i32> {
let remote = self.remote()?;
let mut data = MsgParcel::new();
// Write interface token to identify the service
data.write_interface_token(SERVICE_TOKEN).unwrap();
// Request to open a new communication channel
let mut reply = remote
.send_request(interface::OPEN_CHANNEL, &mut data)
.map_err(|_| 13400003)?;
// Check if the channel was opened successfully
let code = reply.read::<i32>().unwrap();
if code != 0 {
error!("open channel failed: {}", code);
return Err(code);
}
// Read and return the file descriptor
let file = reply.read_file().unwrap();
Ok(file)
}
/// Subscribes to updates for a specific download task.
///
/// Registers to receive status updates for the specified task ID.
///
/// # Arguments
/// * `task_id` - The unique identifier of the task to subscribe to, as a
/// string
///
/// # Returns
/// - `Ok(())` if subscription was successful
/// - `Err(i32)` with the error code if subscription failed
///
/// # Examples
///
/// ```rust,no_run
/// use request_next::proxy::RequestProxy;
///
/// fn example() {
/// let proxy = RequestProxy::get_instance();
/// let task_id = "12345678".to_string();
///
/// match proxy.subscribe(task_id) {
/// Ok(_) => println!("Successfully subscribed to task updates"),
/// Err(code) => eprintln!("Failed to subscribe with error code: {}", code),
/// }
/// }
/// ```
///
/// # Errors
/// Returns an error with code from the download service if subscription
/// fails
///
/// # Panics
/// - Panics if parcel operations fail due to IPC errors
pub(crate) fn subscribe(&self, task_id: String) -> Result<(), i32> {
let remote = self.remote()?;
let mut data = MsgParcel::new();
// Write interface token to identify the service
data.write_interface_token(SERVICE_TOKEN).unwrap();
// Write the task ID to subscribe to
data.write(&task_id).unwrap();
// Send subscription request
let mut reply = remote
.send_request(interface::SUBSCRIBE, &mut data)
.map_err(|_| 13400003)?;
// Check subscription result
let code = reply.read::<i32>().unwrap();
if code != 0 {
error!("subscribe task failed: {}", code);
return Err(code);
}
Ok(())
}
/// Unsubscribes from updates for a specific download task.
///
/// Cancels the registration to receive status updates for the specified
/// task ID.
///
/// # Arguments
/// * `task_id` - The unique identifier of the task to unsubscribe from
///
/// # Returns
/// - `Ok(())` if unsubscription was successful
/// - `Err(i32)` with the error code if unsubscription failed
///
/// # Examples
///
/// ```rust,no_run
/// use request_next::proxy::RequestProxy;
///
/// fn example() {
/// let proxy = RequestProxy::get_instance();
/// let task_id = 12345678i64;
///
/// match proxy.Unsubscribe(task_id) {
/// Ok(_) => println!("Successfully unsubscribed from task updates"),
/// Err(code) => eprintln!("Failed to unsubscribe with error code: {}", code),
/// }
/// }
/// ```
///
/// # Errors
/// Returns an error with code from the download service if unsubscription
/// fails
///
/// # Notes
/// - Note that the method name starts with an uppercase 'U', which is
/// unconventional for Rust but preserved to maintain compatibility with
/// the existing API
///
/// # Panics
/// - Panics if parcel operations fail due to IPC errors
pub(crate) fn Unsubscribe(&self, task_id: i64) -> Result<(), i32> {
let remote = self.remote()?;
let mut data = MsgParcel::new();
// Write interface token to identify the service
data.write_interface_token(SERVICE_TOKEN).unwrap();
// Convert task ID to string and write to parcel
data.write(&task_id.to_string()).unwrap();
// Send unsubscription request
let mut reply = remote
.send_request(interface::UNSUBSCRIBE, &mut data)
.map_err(|_| 13400003)?;
// Check unsubscription result
let code = reply.read::<i32>().unwrap();
if code != 0 {
error!("unsubscribe task failed: {}", code);
return Err(code);
}
Ok(())
}
}