|
| 1 | +// Jackson Coxson |
| 2 | + |
| 3 | +use std::ptr::null_mut; |
| 4 | + |
| 5 | +use idevice::{ |
| 6 | + ReadWrite, |
| 7 | + dvt::energy_monitor::{EnergyMonitorClient, EnergySample}, |
| 8 | +}; |
| 9 | + |
| 10 | +use crate::{IdeviceFfiError, dvt::remote_server::RemoteServerHandle, ffi_err, run_sync}; |
| 11 | + |
| 12 | +pub struct EnergyMonitorHandle<'a>(pub EnergyMonitorClient<'a, Box<dyn ReadWrite>>); |
| 13 | + |
| 14 | +/// A parsed per-PID energy sample |
| 15 | +#[repr(C)] |
| 16 | +pub struct IdeviceEnergySample { |
| 17 | + pub pid: u32, |
| 18 | + pub timestamp: i64, |
| 19 | + pub total_energy: f64, |
| 20 | + pub cpu_energy: f64, |
| 21 | + pub gpu_energy: f64, |
| 22 | + pub networking_energy: f64, |
| 23 | + pub display_energy: f64, |
| 24 | + pub location_energy: f64, |
| 25 | + pub appstate_energy: f64, |
| 26 | +} |
| 27 | + |
| 28 | +/// Creates a new EnergyMonitorClient from a RemoteServerClient |
| 29 | +/// |
| 30 | +/// # Safety |
| 31 | +/// `server` must be a valid pointer to a handle allocated by this library |
| 32 | +/// `handle` must be a valid pointer to a location where the handle will be stored |
| 33 | +#[unsafe(no_mangle)] |
| 34 | +pub unsafe extern "C" fn energy_monitor_new( |
| 35 | + server: *mut RemoteServerHandle, |
| 36 | + handle: *mut *mut EnergyMonitorHandle<'static>, |
| 37 | +) -> *mut IdeviceFfiError { |
| 38 | + if server.is_null() || handle.is_null() { |
| 39 | + return ffi_err!(IdeviceError::FfiInvalidArg); |
| 40 | + } |
| 41 | + |
| 42 | + let server = unsafe { &mut (*server).0 }; |
| 43 | + let res = run_sync(async move { EnergyMonitorClient::new(server).await }); |
| 44 | + |
| 45 | + match res { |
| 46 | + Ok(client) => { |
| 47 | + let boxed = Box::new(EnergyMonitorHandle(client)); |
| 48 | + unsafe { *handle = Box::into_raw(boxed) }; |
| 49 | + null_mut() |
| 50 | + } |
| 51 | + Err(e) => ffi_err!(e), |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +/// Frees an EnergyMonitorClient handle |
| 56 | +/// |
| 57 | +/// # Safety |
| 58 | +/// `handle` must be a valid pointer to a handle allocated by this library or NULL |
| 59 | +#[unsafe(no_mangle)] |
| 60 | +pub unsafe extern "C" fn energy_monitor_free(handle: *mut EnergyMonitorHandle<'static>) { |
| 61 | + if !handle.is_null() { |
| 62 | + let _ = unsafe { Box::from_raw(handle) }; |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +/// Starts energy sampling for the given PIDs. |
| 67 | +/// |
| 68 | +/// # Safety |
| 69 | +/// `handle` must be a valid pointer to a handle allocated by this library. |
| 70 | +/// If `pids` is non-null it must point to at least `pids_count` readable `u32` values. |
| 71 | +#[unsafe(no_mangle)] |
| 72 | +pub unsafe extern "C" fn energy_monitor_start_sampling( |
| 73 | + handle: *mut EnergyMonitorHandle<'static>, |
| 74 | + pids: *const u32, |
| 75 | + pids_count: usize, |
| 76 | +) -> *mut IdeviceFfiError { |
| 77 | + if handle.is_null() { |
| 78 | + return ffi_err!(IdeviceError::FfiInvalidArg); |
| 79 | + } |
| 80 | + |
| 81 | + let pids_vec: Vec<u32> = if pids.is_null() || pids_count == 0 { |
| 82 | + Vec::new() |
| 83 | + } else { |
| 84 | + unsafe { std::slice::from_raw_parts(pids, pids_count) }.to_vec() |
| 85 | + }; |
| 86 | + |
| 87 | + let client = unsafe { &mut (*handle).0 }; |
| 88 | + let res = run_sync(async move { client.start_sampling(&pids_vec).await }); |
| 89 | + |
| 90 | + match res { |
| 91 | + Ok(_) => null_mut(), |
| 92 | + Err(e) => ffi_err!(e), |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +/// Stops energy sampling for the given PIDs. |
| 97 | +/// |
| 98 | +/// # Safety |
| 99 | +/// `handle` must be a valid pointer to a handle allocated by this library. |
| 100 | +/// If `pids` is non-null it must point to at least `pids_count` readable `u32` values. |
| 101 | +#[unsafe(no_mangle)] |
| 102 | +pub unsafe extern "C" fn energy_monitor_stop_sampling( |
| 103 | + handle: *mut EnergyMonitorHandle<'static>, |
| 104 | + pids: *const u32, |
| 105 | + pids_count: usize, |
| 106 | +) -> *mut IdeviceFfiError { |
| 107 | + if handle.is_null() { |
| 108 | + return ffi_err!(IdeviceError::FfiInvalidArg); |
| 109 | + } |
| 110 | + |
| 111 | + let pids_vec: Vec<u32> = if pids.is_null() || pids_count == 0 { |
| 112 | + Vec::new() |
| 113 | + } else { |
| 114 | + unsafe { std::slice::from_raw_parts(pids, pids_count) }.to_vec() |
| 115 | + }; |
| 116 | + |
| 117 | + let client = unsafe { &mut (*handle).0 }; |
| 118 | + let res = run_sync(async move { client.stop_sampling(&pids_vec).await }); |
| 119 | + |
| 120 | + match res { |
| 121 | + Ok(_) => null_mut(), |
| 122 | + Err(e) => ffi_err!(e), |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +/// Requests a one-shot energy sample and parses the response. |
| 127 | +/// |
| 128 | +/// # Arguments |
| 129 | +/// * [`handle`] - The EnergyMonitorClient handle |
| 130 | +/// * [`pids`] - Pointer to an array of u32 PIDs to sample |
| 131 | +/// * [`pids_count`] - Number of elements in `pids` |
| 132 | +/// * [`samples_out`] - On success, set to a heap-allocated array of IdeviceEnergySample |
| 133 | +/// * [`samples_count_out`] - On success, set to the number of samples |
| 134 | +/// |
| 135 | +/// # Returns |
| 136 | +/// An IdeviceFfiError on error, null on success |
| 137 | +/// |
| 138 | +/// # Safety |
| 139 | +/// All output pointers must be valid and non-null. Free the array with |
| 140 | +/// `energy_monitor_samples_free`. |
| 141 | +#[unsafe(no_mangle)] |
| 142 | +pub unsafe extern "C" fn energy_monitor_sample_attributes( |
| 143 | + handle: *mut EnergyMonitorHandle<'static>, |
| 144 | + pids: *const u32, |
| 145 | + pids_count: usize, |
| 146 | + samples_out: *mut *mut IdeviceEnergySample, |
| 147 | + samples_count_out: *mut usize, |
| 148 | +) -> *mut IdeviceFfiError { |
| 149 | + if handle.is_null() || samples_out.is_null() || samples_count_out.is_null() { |
| 150 | + return ffi_err!(IdeviceError::FfiInvalidArg); |
| 151 | + } |
| 152 | + |
| 153 | + let pids_vec: Vec<u32> = if pids.is_null() || pids_count == 0 { |
| 154 | + Vec::new() |
| 155 | + } else { |
| 156 | + unsafe { std::slice::from_raw_parts(pids, pids_count) }.to_vec() |
| 157 | + }; |
| 158 | + |
| 159 | + let client = unsafe { &mut (*handle).0 }; |
| 160 | + let bytes = match run_sync(async move { client.sample_attributes(&pids_vec).await }) { |
| 161 | + Ok(b) => b, |
| 162 | + Err(e) => return ffi_err!(e), |
| 163 | + }; |
| 164 | + |
| 165 | + let samples = match EnergySample::from_bytes(&bytes) { |
| 166 | + Ok(v) => v, |
| 167 | + Err(e) => return ffi_err!(e), |
| 168 | + }; |
| 169 | + |
| 170 | + let mut c_samples: Box<[IdeviceEnergySample]> = samples |
| 171 | + .into_iter() |
| 172 | + .map(|s| IdeviceEnergySample { |
| 173 | + pid: s.pid, |
| 174 | + timestamp: s.timestamp, |
| 175 | + total_energy: s.total_energy, |
| 176 | + cpu_energy: s.cpu_energy, |
| 177 | + gpu_energy: s.gpu_energy, |
| 178 | + networking_energy: s.networking_energy, |
| 179 | + display_energy: s.display_energy, |
| 180 | + location_energy: s.location_energy, |
| 181 | + appstate_energy: s.appstate_energy, |
| 182 | + }) |
| 183 | + .collect::<Vec<_>>() |
| 184 | + .into_boxed_slice(); |
| 185 | + |
| 186 | + unsafe { |
| 187 | + *samples_out = c_samples.as_mut_ptr(); |
| 188 | + *samples_count_out = c_samples.len(); |
| 189 | + } |
| 190 | + std::mem::forget(c_samples); |
| 191 | + null_mut() |
| 192 | +} |
| 193 | + |
| 194 | +/// Frees an array of IdeviceEnergySample allocated by `energy_monitor_sample_attributes`. |
| 195 | +/// |
| 196 | +/// # Safety |
| 197 | +/// `samples` must be a pointer returned by this library with the matching `count`, or NULL |
| 198 | +#[unsafe(no_mangle)] |
| 199 | +pub unsafe extern "C" fn energy_monitor_samples_free( |
| 200 | + samples: *mut IdeviceEnergySample, |
| 201 | + count: usize, |
| 202 | +) { |
| 203 | + if samples.is_null() { |
| 204 | + return; |
| 205 | + } |
| 206 | + let _ = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(samples, count)) }; |
| 207 | +} |
0 commit comments