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