Skip to main content

android_native_keyring_store/by_service/
store.rs

1use std::{collections::HashMap, sync::Arc};
2
3use jni::JavaVM;
4use keyring_core::{Entry, api::CredentialStoreApi};
5
6use crate::{error::AndroidKeyringResult, shared_preferences::Context};
7
8use super::{Cred, HasJavaVm};
9
10pub struct Store {
11    java_vm: Arc<JavaVM>,
12    context: Context,
13    instance_id: String,
14}
15
16impl std::fmt::Debug for Store {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        f.debug_struct("Store")
19            .field("vendor", &self.vendor())
20            .field("id", &self.id())
21            .field("context", &self.context.id())
22            .finish()
23    }
24}
25
26impl Store {
27    /// Initializes the store using the AndroidContext available
28    /// on the `ndk-context` crate.
29    pub fn from_ndk_context() -> AndroidKeyringResult<Arc<Self>> {
30        let ctx = ndk_context::android_context();
31        let vm = ctx.vm().cast();
32        let activity = ctx.context();
33
34        let java_vm = unsafe { JavaVM::from_raw(vm)? };
35        let env = java_vm.attach_current_thread()?;
36
37        let j_context = unsafe { jni::objects::JObject::from_raw(activity as jni::sys::jobject) };
38        let context = Context::new(&env, j_context)?;
39        let java_vm = Arc::new(env.get_java_vm()?);
40        let instance_id = generate_instance_id();
41        Ok(Arc::new(Self {
42            java_vm,
43            context,
44            instance_id,
45        }))
46    }
47}
48
49impl CredentialStoreApi for Store {
50    fn vendor(&self) -> String {
51        "Android SharedPreferences/KeyStore (Legacy), https://github.com/open-source-cooperative/android-native-keyring-store".to_string()
52    }
53
54    fn id(&self) -> String {
55        self.instance_id.clone()
56    }
57
58    fn build(
59        &self,
60        service: &str,
61        user: &str,
62        _modifiers: Option<&HashMap<&str, &str>>,
63    ) -> keyring_core::Result<Entry> {
64        let credential = Cred::new(self.java_vm.clone(), self.context.clone(), service, user);
65
66        Ok(Entry::new_with_credential(Arc::new(credential)))
67    }
68
69    fn as_any(&self) -> &dyn std::any::Any {
70        self
71    }
72
73    fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        std::fmt::Debug::fmt(self, f)
75    }
76}
77
78impl HasJavaVm for Store {
79    fn java_vm(&self) -> &JavaVM {
80        &self.java_vm
81    }
82}
83
84fn generate_instance_id() -> String {
85    use std::time::{SystemTime, UNIX_EPOCH};
86
87    let now = SystemTime::now();
88    let elapsed = if now.lt(&UNIX_EPOCH) {
89        UNIX_EPOCH.duration_since(now).unwrap()
90    } else {
91        now.duration_since(UNIX_EPOCH).unwrap()
92    };
93
94    format!(
95        "One File per Service storage, Crate version {}, Instantiated at {}",
96        env!("CARGO_PKG_VERSION"),
97        elapsed.as_secs_f64()
98    )
99}