1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //! Provides AApexInfo (name, version) from the calling process
18
19 use apex_manifest::apex_manifest::ApexManifest;
20 use protobuf::Message;
21 use std::env;
22 use std::ffi::CString;
23 use std::fs::File;
24 use std::path::{Path, PathBuf};
25
26 #[derive(Debug)]
27 pub enum AApexInfoError {
28 /// The executable's path is not from an APEX
29 PathNotFromApex(PathBuf),
30 /// Fail to get the current executable's path
31 ExePathUnavailable(std::io::Error),
32 /// Fail to read apex_manifest.pb from an APEX
33 InvalidApex(String),
34 }
35
36 /// AApexInfo is used as an opaque object from FFI clients. This just wraps
37 /// ApexManifest protobuf message.
38 ///
39 /// NOTE: that we don't want to provide too many details about APEX. It provides
40 /// minimal information (for now, name and version) only for the APEX where the
41 /// current process is loaded from.
42 pub struct AApexInfo {
43 pub name: CString,
44 pub version: i64,
45 }
46
47 impl AApexInfo {
48 /// Returns AApexInfo object when called by an executable from an APEX.
create() -> Result<Self, AApexInfoError>49 pub fn create() -> Result<Self, AApexInfoError> {
50 let exe_path = env::current_exe().map_err(AApexInfoError::ExePathUnavailable)?;
51 let manifest_path = get_apex_manifest_path(exe_path)?;
52 let manifest = parse_apex_manifest(manifest_path)?;
53 Ok(AApexInfo {
54 name: CString::new(manifest.name)
55 .map_err(|err| AApexInfoError::InvalidApex(format!("{err:?}")))?,
56 version: manifest.version,
57 })
58 }
59 }
60
61 /// Returns the apex_manifest.pb path when a given path belongs to an apex.
get_apex_manifest_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, AApexInfoError>62 fn get_apex_manifest_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, AApexInfoError> {
63 let remain = path
64 .as_ref()
65 .strip_prefix("/apex")
66 .map_err(|_| AApexInfoError::PathNotFromApex(path.as_ref().to_owned()))?;
67 let apex_name = remain
68 .iter()
69 .next()
70 .ok_or_else(|| AApexInfoError::PathNotFromApex(path.as_ref().to_owned()))?;
71 Ok(Path::new("/apex").join(apex_name).join("apex_manifest.pb"))
72 }
73
74 /// Parses the apex_manifest.pb protobuf message from a given path.
parse_apex_manifest<P: AsRef<Path>>(path: P) -> Result<ApexManifest, AApexInfoError>75 fn parse_apex_manifest<P: AsRef<Path>>(path: P) -> Result<ApexManifest, AApexInfoError> {
76 let mut f = File::open(path).map_err(|err| AApexInfoError::InvalidApex(format!("{err:?}")))?;
77 Message::parse_from_reader(&mut f).map_err(|err| AApexInfoError::InvalidApex(format!("{err:?}")))
78 }
79
80 #[cfg(test)]
81 mod test {
82 use super::*;
83
84 #[test]
test_get_apex_manifest_path()85 fn test_get_apex_manifest_path() {
86 assert_eq!(
87 get_apex_manifest_path("/apex/com.android.foo/bin/foo").unwrap(),
88 PathBuf::from("/apex/com.android.foo/apex_manifest.pb")
89 );
90 assert!(get_apex_manifest_path("/apex/").is_err());
91 assert!(get_apex_manifest_path("/apex").is_err());
92 assert!(get_apex_manifest_path("/com.android.foo/bin/foo").is_err());
93 assert!(get_apex_manifest_path("/system/apex/com.android.foo/bin/foo").is_err());
94 }
95 }
96