Skip to main content

apk/xml/
mod.rs

1//! Android binary XML (`AXML`): the compiled form of `AndroidManifest.xml`
2//! and `res/xml/*` files.
3//!
4//! [`Document`] is a typed element tree. Build one in code, parse it from
5//! text with [`Document::parse`], or decode a compiled file with
6//! [`Document::decode`]; [`Document::encode`] compiles it, and its
7//! [`Display`](std::fmt::Display) implementation prints readable XML.
8//!
9//! ```
10//! use apk::android::attr;
11//! use apk::xml::{Document, Element};
12//!
13//! let manifest = Element::new("manifest")
14//!     .with_android_namespace()
15//!     .with_attr("package", "org.example.app")
16//!     .with_attr(attr::VERSION_CODE, 3)
17//!     .with_child(Element::new("uses-sdk").with_attr(attr::MIN_SDK_VERSION, 24));
18//! let document = Document::new(manifest);
19//! let compiled = document.encode(&())?;
20//! assert_eq!(Document::decode(&compiled)?, document);
21//! # Ok::<(), apk::xml::XmlError>(())
22//! ```
23
24mod decode;
25mod encode;
26mod text;
27
28use crate::android::{self, AttrDef, AttrFormat};
29use crate::res::{ChunkError, ResourceId, ResourceRef, ResourceRefError, StringPoolError, Value};
30
31pub use crate::android::NAMESPACE as ANDROID_NAMESPACE;
32
33/// Why a binary or text XML document could not be processed.
34#[derive(Debug, thiserror::Error)]
35#[non_exhaustive]
36pub enum XmlError {
37    #[error(transparent)]
38    Chunk(#[from] ChunkError),
39    #[error(transparent)]
40    StringPool(#[from] StringPoolError),
41    #[error("binary XML has no string pool before its first node")]
42    MissingStringPool,
43    #[error("string index {index} is outside the string pool")]
44    StringOutOfRange { index: u32 },
45    #[error("{kind} node at offset {offset:#x} is truncated")]
46    TruncatedNode { kind: NodeKind, offset: usize },
47    #[error("closing tag </{found}> does not match open element <{expected}>")]
48    MismatchedEnd { expected: String, found: String },
49    #[error("closing tag namespace {found:?} does not match open namespace {expected:?}")]
50    MismatchedNamespace { expected: Option<String>, found: Option<String> },
51    #[error("closing tag </{found}> has no open element")]
52    UnexpectedEnd { found: String },
53    #[error("{count} element(s) are never closed")]
54    Unclosed { count: usize },
55    #[error("document has no root element")]
56    NoRoot,
57    #[error("document has more than one root element")]
58    MultipleRoots,
59    #[error("elements nest deeper than {max} levels", max = MAX_DEPTH)]
60    TooDeep,
61    #[error("android:{name} is not a public framework attribute")]
62    UnknownAttribute { name: String },
63    #[error(transparent)]
64    Reference(#[from] ResourceRefError),
65    #[error("reference {reference} cannot be resolved")]
66    UnresolvedReference { reference: ResourceRef },
67    #[error("value {value:?} is not a valid {format:?} for android:{attribute}")]
68    InvalidAttributeValue { attribute: &'static str, value: String, format: AttrFormat },
69    #[error("android:{attribute} accepts {format:?}, not {value:?}")]
70    MistypedValue { attribute: &'static str, value: Value, format: AttrFormat },
71    #[error("{name} in {namespace} is an app-defined attribute, which this crate cannot compile")]
72    AppAttribute { namespace: String, name: String },
73    #[error("namespace prefix `{prefix}` is not declared")]
74    UndeclaredPrefix { prefix: String },
75    #[error("entity &{name}; is not defined (only the predefined XML entities are)")]
76    UnknownEntity { name: String },
77    #[error("XML syntax error: {0}")]
78    Syntax(#[from] quick_xml::Error),
79}
80
81/// The binary XML node a [`XmlError::TruncatedNode`] refers to.
82#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
83pub enum NodeKind {
84    Namespace,
85    StartElement,
86    Attribute,
87    EndElement,
88    Text,
89}
90
91impl std::fmt::Display for NodeKind {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.write_str(match self {
94            Self::Namespace => "namespace",
95            Self::StartElement => "element start",
96            Self::Attribute => "attribute",
97            Self::EndElement => "element end",
98            Self::Text => "text",
99        })
100    }
101}
102
103/// The deepest element nesting accepted when decoding, parsing, or encoding.
104pub const MAX_DEPTH: usize = 512;
105
106/// An XML document with a single root element.
107#[derive(Clone, Debug, PartialEq)]
108pub struct Document {
109    pub root: Element,
110}
111
112impl Document {
113    pub fn new(root: Element) -> Self {
114        Self { root }
115    }
116}
117
118/// An `xmlns:prefix="uri"` declaration.
119#[derive(Clone, Debug, Eq, Hash, PartialEq)]
120pub struct Namespace {
121    /// `None` for a default namespace (`xmlns="..."`).
122    pub prefix: Option<String>,
123    pub uri: String,
124}
125
126impl Namespace {
127    /// `xmlns:android="http://schemas.android.com/apk/res/android"`.
128    pub fn android() -> Self {
129        Self { prefix: Some("android".into()), uri: ANDROID_NAMESPACE.into() }
130    }
131}
132
133/// A child of an [`Element`].
134#[derive(Clone, Debug, PartialEq)]
135pub enum Node {
136    Element(Element),
137    Text(String),
138}
139
140/// An XML element.
141#[derive(Clone, Debug, Default, PartialEq)]
142pub struct Element {
143    /// The local name, e.g. `activity`.
144    pub name: String,
145    /// The namespace URI of the element name, rarely used in Android XML.
146    pub namespace: Option<String>,
147    /// Namespaces declared on this element.
148    pub namespaces: Vec<Namespace>,
149    pub attributes: Vec<Attribute>,
150    pub children: Vec<Node>,
151}
152
153/// An attribute's qualified name.
154///
155/// Framework attributes (`android:*`) carry their resource ID, which is what
156/// Android actually matches on.
157#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
158pub struct AttrName {
159    pub namespace: Option<String>,
160    pub name: String,
161    pub id: Option<ResourceId>,
162}
163
164impl AttrName {
165    /// An attribute in no namespace, such as `package`.
166    pub fn plain(name: impl Into<String>) -> Self {
167        Self { namespace: None, name: name.into(), id: None }
168    }
169
170    /// The framework attribute `def`.
171    pub fn android(def: &AttrDef) -> Self {
172        Self { namespace: Some(ANDROID_NAMESPACE.into()), name: def.name().into(), id: Some(def.id()) }
173    }
174
175    /// The framework attribute this name refers to, if it is public.
176    pub fn android_def(&self) -> Option<&'static AttrDef> {
177        if self.namespace.as_deref() != Some(ANDROID_NAMESPACE) {
178            return None;
179        }
180        match self.id {
181            Some(id) => android::attr_by_id(id),
182            None => android::find_attr(&self.name),
183        }
184    }
185
186    /// The resource ID Android matches this attribute by, if it has one.
187    pub fn resource_id(&self) -> Option<ResourceId> {
188        self.id.or_else(|| self.android_def().map(AttrDef::id))
189    }
190
191    /// The position of this attribute in an element: resource-ID attributes
192    /// first in ascending ID order, then the rest by namespace and name.
193    pub(crate) fn sort_key(&self) -> (bool, Option<ResourceId>, Option<&str>, &str) {
194        let id = self.resource_id();
195        (id.is_none(), id, self.namespace.as_deref(), &self.name)
196    }
197
198    fn matches(&self, namespace: Option<&str>, name: &str) -> bool {
199        self.namespace.as_deref() == namespace && self.name == name
200    }
201
202    /// Whether `self` and `other` name the same attribute: the same resource
203    /// ID (whatever the names, which obfuscators change), or the same name.
204    fn same(&self, other: &Self) -> bool {
205        match (self.resource_id(), other.resource_id()) {
206            (Some(a), Some(b)) => a == b,
207            _ => self.matches(other.namespace.as_deref(), &other.name),
208        }
209    }
210}
211
212impl From<&AttrDef> for AttrName {
213    fn from(def: &AttrDef) -> Self {
214        Self::android(def)
215    }
216}
217
218impl From<&str> for AttrName {
219    fn from(name: &str) -> Self {
220        Self::plain(name)
221    }
222}
223
224/// A name and a typed value.
225#[derive(Clone, Debug, PartialEq)]
226pub struct Attribute {
227    pub name: AttrName,
228    pub value: Value,
229}
230
231impl Element {
232    pub fn new(name: impl Into<String>) -> Self {
233        Self { name: name.into(), ..Self::default() }
234    }
235
236    /// Declares `xmlns:android` on this element.
237    pub fn with_android_namespace(mut self) -> Self {
238        self.namespaces.push(Namespace::android());
239        self
240    }
241
242    /// Sets an attribute, replacing any previous value.
243    pub fn with_attr(mut self, name: impl Into<AttrName>, value: impl Into<Value>) -> Self {
244        self.set_attr(name, value);
245        self
246    }
247
248    /// Sets an attribute when `value` is `Some`.
249    pub fn with_opt_attr<V: Into<Value>>(mut self, name: impl Into<AttrName>, value: Option<V>) -> Self {
250        if let Some(value) = value {
251            self.set_attr(name, value);
252        }
253        self
254    }
255
256    pub fn with_child(mut self, child: Element) -> Self {
257        self.children.push(Node::Element(child));
258        self
259    }
260
261    pub fn with_children(mut self, children: impl IntoIterator<Item = Element>) -> Self {
262        self.children.extend(children.into_iter().map(Node::Element));
263        self
264    }
265
266    /// Sets an attribute, replacing the value of any attribute with the same
267    /// resource ID or, without one, the same name.
268    ///
269    /// Attributes are kept in the order binary XML stores them: framework
270    /// attributes by resource ID, then the rest by namespace and name.
271    pub fn set_attr(&mut self, name: impl Into<AttrName>, value: impl Into<Value>) -> &mut Self {
272        let name = name.into();
273        let value = value.into();
274        match self.attributes.iter_mut().find(|a| a.name.same(&name)) {
275            Some(existing) => existing.value = value,
276            None => {
277                let key = name.sort_key();
278                let index = self.attributes.partition_point(|a| a.name.sort_key() <= key);
279                self.attributes.insert(index, Attribute { name, value });
280            }
281        }
282        self
283    }
284
285    pub fn push_child(&mut self, child: Element) -> &mut Self {
286        self.children.push(Node::Element(child));
287        self
288    }
289
290    /// The value of the attribute `name` in `namespace`.
291    pub fn attr(&self, namespace: Option<&str>, name: &str) -> Option<&Value> {
292        self.attributes.iter().find(|a| a.name.matches(namespace, name)).map(|a| &a.value)
293    }
294
295    /// The value of the framework attribute `def`.
296    pub fn android_attr(&self, def: &AttrDef) -> Option<&Value> {
297        self.attributes
298            .iter()
299            .find(|a| a.name.id == Some(def.id()) || a.name.matches(Some(ANDROID_NAMESPACE), def.name()))
300            .map(|a| &a.value)
301    }
302
303    /// Child elements.
304    pub fn elements(&self) -> impl Iterator<Item = &Element> {
305        self.children.iter().filter_map(|node| match node {
306            Node::Element(element) => Some(element),
307            Node::Text(_) => None,
308        })
309    }
310
311    /// Child elements named `name`.
312    pub fn elements_named<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a Element> + 'a {
313        self.elements().filter(move |e| e.name == name)
314    }
315
316    /// The first child element named `name`.
317    pub fn element(&self, name: &str) -> Option<&Element> {
318        self.elements().find(|e| e.name == name)
319    }
320
321    /// Mutable child elements.
322    pub fn elements_mut(&mut self) -> impl Iterator<Item = &mut Element> {
323        self.children.iter_mut().filter_map(|node| match node {
324            Node::Element(element) => Some(element),
325            Node::Text(_) => None,
326        })
327    }
328
329    /// The first child element named `name`, for editing.
330    pub fn element_mut(&mut self, name: &str) -> Option<&mut Element> {
331        self.elements_mut().find(|e| e.name == name)
332    }
333}
334
335/// A step of a depth-first walk over an element tree.
336#[derive(Clone, Copy, Debug)]
337pub(crate) enum Step<'a> {
338    Open(&'a Element),
339    Text(&'a str),
340    Close(&'a Element),
341}
342
343/// Walks the tree under `root` depth-first without recursion, so trees of
344/// any depth are safe to traverse. Each step comes with the depth of its
345/// element, or of the parent of a text node; the root is at depth 0.
346pub(crate) fn walk<'a>(root: &'a Element) -> impl Iterator<Item = (usize, Step<'a>)> {
347    // Open elements, innermost last, each with the index of its next child.
348    let mut open: Vec<(&'a Element, usize)> = Vec::new();
349    let mut start = Some(root);
350    std::iter::from_fn(move || {
351        if let Some(root) = start.take() {
352            open.push((root, 0));
353            return Some((0, Step::Open(root)));
354        }
355        let depth = open.len().checked_sub(1)?;
356        let (element, next) = open.last_mut()?;
357        // Copy the tree reference out, so `open` is free to change below.
358        let element = *element;
359        let child = element.children.get(*next);
360        *next += 1;
361        Some(match child {
362            Some(Node::Element(child)) => {
363                open.push((child, 0));
364                (depth + 1, Step::Open(child))
365            }
366            Some(Node::Text(text)) => (depth, Step::Text(text)),
367            None => {
368                open.pop();
369                (depth, Step::Close(element))
370            }
371        })
372    })
373}