1 /*
2  * Copyright 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 //! Handle parsing of APK manifest files.
18 //! The manifest file is written as XML text, but is stored in the APK
19 //! as Android binary compressed XML. This library is a wrapper around
20 //! a thin C++ wrapper around libandroidfw, which contains the same
21 //! parsing code as used by package manager and aapt2 (amongst other
22 //! things).
23 
24 use anyhow::{bail, Context, Result};
25 use apkmanifest_bindgen::{extractManifestInfo, freeManifestInfo, getPackageName, getVersionCode};
26 use std::ffi::CStr;
27 use std::fs::File;
28 use std::path::Path;
29 
30 /// Information extracted from the Android manifest inside an APK.
31 #[derive(Debug, Default, Eq, PartialEq)]
32 pub struct ApkManifestInfo {
33     /// The package name of the app.
34     pub package: String,
35     /// The version code of the app.
36     pub version_code: u64,
37 }
38 
39 const ANDROID_MANIFEST: &str = "AndroidManifest.xml";
40 
41 /// Find the manifest inside the given APK and return information from it.
get_manifest_info<P: AsRef<Path>>(apk_path: P) -> Result<ApkManifestInfo>42 pub fn get_manifest_info<P: AsRef<Path>>(apk_path: P) -> Result<ApkManifestInfo> {
43     let apk = File::open(apk_path.as_ref())?;
44     let manifest = apkzip::read_file(apk, ANDROID_MANIFEST)?;
45 
46     // Safety: The function only reads the memory range we specify and does not hold
47     // any reference to it.
48     let native_info = unsafe { extractManifestInfo(manifest.as_ptr() as _, manifest.len()) };
49     if native_info.is_null() {
50         bail!("Failed to parse manifest")
51     };
52 
53     scopeguard::defer! {
54         // Safety: The value we pass is the result of calling extractManifestInfo as required.
55         // We must call this exactly once, after we have finished using it, which the scopeguard
56         // ensures.
57         unsafe { freeManifestInfo(native_info); }
58     }
59 
60     // Safety: It is always safe to call this with a valid native_info, which we have,
61     // and it always returns a valid nul-terminated C string with the same lifetime as native_info.
62     // We immediately make a copy.
63     let package = unsafe { CStr::from_ptr(getPackageName(native_info)) };
64     let package = package.to_str().context("Invalid package name")?.to_string();
65 
66     // Safety: It is always safe to call this with a valid native_info, which we have.
67     let version_code = unsafe { getVersionCode(native_info) };
68 
69     Ok(ApkManifestInfo { package, version_code })
70 }
71