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;
8use starnix_core::vfs::FileOps;
9use starnix_core::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
10use starnix_logging::log_error;
11use starnix_sync::{FileOpsCore, Locked};
12use starnix_uapi::device_type::DeviceType;
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        _locked: &mut Locked<FileOpsCore>,
28        _current_task: &CurrentTask,
29        _device_type: DeviceType,
30        _node: &starnix_core::vfs::NamespaceNode,
31        _flags: OpenFlags,
32    ) -> Result<Box<dyn FileOps>, Errno> {
33        Ok(Box::new(BytesFile::new(self.clone())))
34    }
35}
36
37struct ConsentSyncFileBackend {
38    consent: AtomicBool,
39    privacy_proxy: fsettings::PrivacySynchronousProxy,
40}
41
42impl BytesFileOps for ConsentSyncHandle {
43    fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
44        self.0.write(current_task, data)
45    }
46
47    fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
48        self.0.read(current_task)
49    }
50}
51
52impl BytesFileOps for ConsentSyncFileBackend {
53    fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
54        let content_str = String::from_utf8_lossy(&data);
55        let trimmed_content = content_str.trim();
56
57        let granted = match trimmed_content {
58            "0" => false,
59            "1" => true,
60            _ => {
61                log_error!(
62                    "ConsentSync: Invalid value written: {:?}. Must be '0' or '1'.",
63                    trimmed_content
64                );
65                return error!(EINVAL);
66            }
67        };
68
69        let settings = fsettings::PrivacySettings {
70            user_data_sharing_consent: Some(granted),
71            ..Default::default()
72        };
73
74        match self.privacy_proxy.set(&settings, zx::MonotonicInstant::INFINITE) {
75            Ok(Ok(())) => {
76                self.consent.store(granted, Ordering::Relaxed);
77                Ok(())
78            }
79            Ok(Err(e)) => {
80                log_error!("ConsentSync: fuchsia.settings.Privacy.Set application error: {:?}", e);
81                error!(EIO)
82            }
83            Err(e) => {
84                log_error!(
85                    "ConsentSync: FIDL call to fuchsia.settings.Privacy.Set failed: {:?}",
86                    e
87                );
88                error!(EIO)
89            }
90        }
91    }
92
93    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
94        let val = if self.consent.load(Ordering::Relaxed) { "1\n" } else { "0\n" };
95        Ok(val.as_bytes().into())
96    }
97}
98
99pub fn init(locked: &mut Locked<starnix_sync::Unlocked>, system_task: &CurrentTask) {
100    let kernel = system_task.kernel();
101    let registry = &kernel.device_registry;
102
103    let privacy_proxy = fsettings::PrivacySynchronousProxy::new(
104        kernel
105            .connect_to_protocol_at_container_svc::<fsettings::PrivacyMarker>()
106            .expect("Connected to privacy service")
107            .into_channel(),
108    );
109
110    let file_backend =
111        Arc::new(ConsentSyncFileBackend { consent: AtomicBool::new(false), privacy_proxy });
112
113    let device = ConsentSyncHandle(file_backend);
114
115    registry
116        .register_dyn_device(
117            locked,
118            system_task,
119            "consent".into(),
120            registry.objects.starnix_class(),
121            device,
122        )
123        .expect("can register consent device");
124}