Skip to main content

starnix_consent_sync/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_settings as fsettings;
6use starnix_core::device::DeviceOps;
7use starnix_core::task::{CurrentTask, Kernel};
8use starnix_core::vfs::FileOps;
9use starnix_core::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
10use starnix_logging::log_error;
11
12use starnix_uapi::device_id::DeviceId;
13use starnix_uapi::error;
14use starnix_uapi::errors::Errno;
15use starnix_uapi::open_flags::OpenFlags;
16use std::borrow::Cow;
17use std::sync::Arc;
18use std::sync::atomic::{AtomicBool, Ordering};
19use zx;
20
21#[derive(Clone)]
22struct ConsentSyncHandle(Arc<ConsentSyncFileBackend>);
23
24impl DeviceOps for ConsentSyncHandle {
25    fn open(
26        &self,
27        _current_task: &CurrentTask,
28        _device_id: DeviceId,
29        _node: &starnix_core::vfs::NamespaceNode,
30        _flags: OpenFlags,
31    ) -> Result<Box<dyn FileOps>, Errno> {
32        Ok(Box::new(BytesFile::new(self.clone())))
33    }
34}
35
36struct ConsentSyncFileBackend {
37    consent: AtomicBool,
38    privacy_proxy: fsettings::PrivacySynchronousProxy,
39}
40
41impl BytesFileOps for ConsentSyncHandle {
42    fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
43        self.0.write(current_task, data)
44    }
45
46    fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
47        self.0.read(current_task)
48    }
49}
50
51impl BytesFileOps for ConsentSyncFileBackend {
52    fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
53        let content_str = String::from_utf8_lossy(&data);
54        let trimmed_content = content_str.trim();
55
56        let granted = match trimmed_content {
57            "0" => false,
58            "1" => true,
59            _ => {
60                log_error!(
61                    "ConsentSync: Invalid value written: {:?}. Must be '0' or '1'.",
62                    trimmed_content
63                );
64                return error!(EINVAL);
65            }
66        };
67
68        let settings = fsettings::PrivacySettings {
69            user_data_sharing_consent: Some(granted),
70            ..Default::default()
71        };
72
73        match self.privacy_proxy.set(&settings, zx::MonotonicInstant::INFINITE) {
74            Ok(Ok(())) => {
75                self.consent.store(granted, Ordering::Relaxed);
76                Ok(())
77            }
78            Ok(Err(e)) => {
79                log_error!("ConsentSync: fuchsia.settings.Privacy.Set application error: {:?}", e);
80                error!(EIO)
81            }
82            Err(e) => {
83                log_error!(
84                    "ConsentSync: FIDL call to fuchsia.settings.Privacy.Set failed: {:?}",
85                    e
86                );
87                error!(EIO)
88            }
89        }
90    }
91
92    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
93        let val = if self.consent.load(Ordering::Relaxed) { "1\n" } else { "0\n" };
94        Ok(val.as_bytes().into())
95    }
96}
97
98pub fn init(kernel: &Arc<Kernel>) {
99    let registry = &kernel.device_registry;
100
101    let privacy_proxy = fsettings::PrivacySynchronousProxy::new(
102        kernel
103            .connect_to_protocol_at_container_svc::<fsettings::PrivacyMarker>()
104            .expect("Connected to privacy service")
105            .into_channel(),
106    );
107
108    let file_backend =
109        Arc::new(ConsentSyncFileBackend { consent: AtomicBool::new(false), privacy_proxy });
110
111    let device = ConsentSyncHandle(file_backend);
112
113    registry
114        .register_dyn_device(kernel, "consent".into(), registry.objects.starnix_class(), device)
115        .expect("can register consent device");
116}