Skip to main content

andysu_cli/
axml.rs

1//! Binary Android XML (AXML) as an editable tree: parse, modify, write.
2//!
3//! Enough fidelity for AndroidManifest.xml: namespaces, elements, attributes (raw strings and
4//! typed values, with their resource ids), and text. On write the string pool and the resource
5//! map are rebuilt (attribute names that have resource ids come first, as aapt2 does) and
6//! attributes are sorted by resource id, which the framework's attribute lookup relies on.
7
8use std::collections::HashMap;
9
10use crate::R;
11
12pub const ANDROID_NS: &str = "http://schemas.android.com/apk/res/android";
13pub const TYPE_REFERENCE: u8 = 0x01;
14pub const TYPE_STRING: u8 = 0x03;
15pub const TYPE_INT_DEC: u8 = 0x10;
16pub const TYPE_INT_BOOLEAN: u8 = 0x12;
17
18/// Resource ids of the android: attributes this crate writes.
19pub mod attr {
20    pub const NAME: u32 = 0x0101_0003;
21    pub const PERMISSION: u32 = 0x0101_0006;
22    pub const EXPORTED: u32 = 0x0101_0010;
23    pub const PROCESS: u32 = 0x0101_0011;
24    pub const AUTHORITIES: u32 = 0x0101_0018;
25    pub const THEME: u32 = 0x0101_0000;
26    pub const HAS_CODE: u32 = 0x0101_000c;
27    pub const NO_HISTORY: u32 = 0x0101_022d;
28    pub const EXCLUDE_FROM_RECENTS: u32 = 0x0101_0017;
29    pub const GRANT_URI_PERMISSIONS: u32 = 0x0101_001b;
30    pub const EXTRACT_NATIVE_LIBS: u32 = 0x0101_04ea;
31    pub const APP_COMPONENT_FACTORY: u32 = 0x0101_057a;
32}
33
34#[derive(Debug, Clone, PartialEq)]
35pub enum Value {
36    /// A string (raw value; typed as TYPE_STRING).
37    Str(String),
38    /// Any other typed value.
39    Typed { dtype: u8, data: u32 },
40}
41
42#[derive(Debug, Clone, PartialEq)]
43pub struct Attr {
44    pub ns: Option<String>,
45    pub name: String,
46    pub res_id: Option<u32>,
47    pub value: Value,
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub enum Node {
52    Element(Element),
53    Text(String),
54}
55
56#[derive(Debug, Clone, PartialEq)]
57pub struct Element {
58    pub ns: Option<String>,
59    pub name: String,
60    pub attrs: Vec<Attr>,
61    pub children: Vec<Node>,
62}
63
64#[derive(Debug, Clone, PartialEq)]
65pub struct Doc {
66    /// (prefix, uri) declared on the root.
67    pub namespaces: Vec<(String, String)>,
68    pub root: Element,
69}
70
71impl Attr {
72    pub fn android(name: &str, res_id: u32, value: Value) -> Attr {
73        Attr { ns: Some(ANDROID_NS.into()), name: name.into(), res_id: Some(res_id), value }
74    }
75    pub fn str(&self) -> Option<&str> {
76        if let Value::Str(s) = &self.value {
77            Some(s)
78        } else {
79            None
80        }
81    }
82}
83
84impl Element {
85    pub fn new(name: &str, attrs: Vec<Attr>) -> Element {
86        Element { ns: None, name: name.into(), attrs, children: vec![] }
87    }
88    pub fn attr(&self, res_id: u32) -> Option<&Attr> {
89        self.attrs.iter().find(|a| a.res_id == Some(res_id))
90    }
91    pub fn android_name(&self) -> Option<&str> {
92        self.attr(attr::NAME).and_then(|a| a.str())
93    }
94    pub fn set(&mut self, a: Attr) {
95        self.attrs.retain(|x| {
96            !(x.res_id.is_some() && x.res_id == a.res_id || x.res_id.is_none() && x.name == a.name && x.ns == a.ns)
97        });
98        self.attrs.push(a);
99    }
100    pub fn remove(&mut self, res_id: u32) {
101        self.attrs.retain(|x| x.res_id != Some(res_id));
102    }
103    pub fn elements(&self) -> impl Iterator<Item = &Element> {
104        self.children.iter().filter_map(|n| if let Node::Element(e) = n { Some(e) } else { None })
105    }
106    pub fn elements_mut(&mut self) -> impl Iterator<Item = &mut Element> {
107        self.children.iter_mut().filter_map(|n| if let Node::Element(e) = n { Some(e) } else { None })
108    }
109    pub fn child_mut(&mut self, name: &str) -> Option<&mut Element> {
110        self.elements_mut().find(|e| e.name == name)
111    }
112    pub fn push(&mut self, e: Element) {
113        self.children.push(Node::Element(e));
114    }
115}
116
117// ------------------------------------------------------------------------------------- parse
118
119fn u16_at(b: &[u8], o: usize) -> R<u16> {
120    b.get(o..o + 2).map(|x| u16::from_le_bytes([x[0], x[1]])).ok_or_else(|| "AXML truncated".into())
121}
122fn u32_at(b: &[u8], o: usize) -> R<u32> {
123    b.get(o..o + 4).map(|x| u32::from_le_bytes([x[0], x[1], x[2], x[3]])).ok_or_else(|| "AXML truncated".into())
124}
125
126fn read_pool(b: &[u8], c: usize) -> R<Vec<String>> {
127    let count = u32_at(b, c + 8)? as usize;
128    let utf8 = u32_at(b, c + 16)? & 0x100 != 0;
129    let start = u32_at(b, c + 20)? as usize;
130    let hs = u16_at(b, c + 2)? as usize;
131    let mut out = Vec::with_capacity(count);
132    for i in 0..count {
133        let mut o = c + start + u32_at(b, c + hs + i * 4)? as usize;
134        let s = if utf8 {
135            o += if b.get(o).ok_or("AXML truncated")? & 0x80 != 0 { 2 } else { 1 };
136            let mut l = *b.get(o).ok_or("AXML truncated")? as usize;
137            if l & 0x80 != 0 {
138                l = ((l & 0x7f) << 8) | *b.get(o + 1).ok_or("AXML truncated")? as usize;
139                o += 2;
140            } else {
141                o += 1;
142            }
143            String::from_utf8_lossy(b.get(o..o + l).ok_or("AXML string out of range")?).to_string()
144        } else {
145            let mut l = u16_at(b, o)? as usize;
146            o += 2;
147            if l & 0x8000 != 0 {
148                l = ((l & 0x7fff) << 16) | u16_at(b, o)? as usize;
149                o += 2;
150            }
151            let units: Vec<u16> = (0..l).map(|k| u16_at(b, o + 2 * k)).collect::<R<_>>()?;
152            String::from_utf16_lossy(&units)
153        };
154        out.push(s);
155    }
156    Ok(out)
157}
158
159pub fn parse(b: &[u8]) -> R<Doc> {
160    if u16_at(b, 0)? != 0x0003 {
161        return Err("not a binary XML document".into());
162    }
163    let mut pos = u16_at(b, 2)? as usize;
164    let mut pool: Vec<String> = vec![];
165    let mut resmap: Vec<u32> = vec![];
166    let mut namespaces = vec![];
167    let mut stack: Vec<Element> = vec![];
168    let mut root: Option<Element> = None;
169    let s = |pool: &Vec<String>, i: u32| -> Option<String> {
170        if i == u32::MAX {
171            None
172        } else {
173            pool.get(i as usize).cloned()
174        }
175    };
176    while pos + 8 <= b.len() {
177        let ty = u16_at(b, pos)?;
178        let hs = u16_at(b, pos + 2)? as usize;
179        let size = u32_at(b, pos + 4)? as usize;
180        if size < 8 || pos + size > b.len() {
181            return Err("AXML chunk out of range".into());
182        }
183        match ty {
184            0x0001 => pool = read_pool(b, pos)?,
185            0x0180 => resmap = (0..(size - hs) / 4).map(|i| u32_at(b, pos + hs + i * 4)).collect::<R<_>>()?,
186            0x0100 => {
187                let prefix = s(&pool, u32_at(b, pos + hs)?).unwrap_or_default();
188                let uri = s(&pool, u32_at(b, pos + hs + 4)?).unwrap_or_default();
189                if !namespaces.iter().any(|(_, u)| *u == uri) {
190                    namespaces.push((prefix, uri));
191                }
192            }
193            0x0102 => {
194                let x = pos + hs;
195                let ns = s(&pool, u32_at(b, x)?);
196                let name = s(&pool, u32_at(b, x + 4)?).ok_or("element without name")?;
197                let (astart, asize, acount) =
198                    (u16_at(b, x + 8)? as usize, u16_at(b, x + 10)? as usize, u16_at(b, x + 12)? as usize);
199                let mut attrs = vec![];
200                for k in 0..acount {
201                    let a = x + astart + k * asize;
202                    let ni = u32_at(b, a + 4)?;
203                    let raw = u32_at(b, a + 8)?;
204                    let dtype = *b.get(a + 15).ok_or("AXML truncated")?;
205                    let data = u32_at(b, a + 16)?;
206                    let value = if raw != u32::MAX {
207                        Value::Str(s(&pool, raw).unwrap_or_default())
208                    } else if dtype == TYPE_STRING {
209                        Value::Str(s(&pool, data).unwrap_or_default())
210                    } else {
211                        Value::Typed { dtype, data }
212                    };
213                    attrs.push(Attr {
214                        ns: s(&pool, u32_at(b, a)?),
215                        name: s(&pool, ni).unwrap_or_default(),
216                        res_id: resmap.get(ni as usize).copied(),
217                        value,
218                    });
219                }
220                stack.push(Element { ns, name, attrs, children: vec![] });
221            }
222            0x0103 => {
223                let e = stack.pop().ok_or("unbalanced AXML")?;
224                match stack.last_mut() {
225                    Some(p) => p.children.push(Node::Element(e)),
226                    None => root = Some(e),
227                }
228            }
229            0x0104 => {
230                let t = s(&pool, u32_at(b, pos + hs)?).unwrap_or_default();
231                if let Some(p) = stack.last_mut() {
232                    p.children.push(Node::Text(t));
233                }
234            }
235            _ => {}
236        }
237        pos += size;
238    }
239    Ok(Doc { namespaces, root: root.ok_or("AXML without a root element")? })
240}
241
242// ------------------------------------------------------------------------------------- write
243
244struct Pool {
245    strings: Vec<String>,
246    index: HashMap<String, u32>,
247}
248
249impl Pool {
250    fn idx(&self, s: &str) -> u32 {
251        self.index[s]
252    }
253    fn opt(&self, s: &Option<String>) -> u32 {
254        s.as_ref().map_or(u32::MAX, |x| self.idx(x))
255    }
256}
257
258fn collect(e: &Element, attr_names: &mut Vec<(String, u32)>, other: &mut Vec<String>) {
259    let push = |v: &mut Vec<String>, s: &str| {
260        if !v.iter().any(|x| x == s) {
261            v.push(s.to_string());
262        }
263    };
264    if let Some(n) = &e.ns {
265        push(other, n);
266    }
267    push(other, &e.name);
268    for a in &e.attrs {
269        match a.res_id {
270            Some(id) if !attr_names.iter().any(|(n, i)| n == &a.name && *i == id) => {
271                attr_names.push((a.name.clone(), id))
272            }
273            Some(_) => {}
274            None => push(other, &a.name),
275        }
276        if let Some(n) = &a.ns {
277            push(other, n);
278        }
279        if let Value::Str(s) = &a.value {
280            push(other, s);
281        }
282    }
283    for c in &e.children {
284        match c {
285            Node::Element(x) => collect(x, attr_names, other),
286            Node::Text(t) => push(other, t),
287        }
288    }
289}
290
291fn utf16_entry(s: &str) -> Vec<u8> {
292    let units: Vec<u16> = s.encode_utf16().collect();
293    let mut b = vec![];
294    if units.len() > 0x7fff {
295        b.extend((((units.len() >> 16) as u16) | 0x8000).to_le_bytes());
296        b.extend(((units.len() & 0xffff) as u16).to_le_bytes());
297    } else {
298        b.extend((units.len() as u16).to_le_bytes());
299    }
300    for u in units {
301        b.extend(u.to_le_bytes());
302    }
303    b.extend([0, 0]);
304    b
305}
306
307fn chunk(ty: u16, header: &[u8], body: &[u8]) -> Vec<u8> {
308    let hs = 8 + header.len();
309    let mut c = vec![];
310    c.extend(ty.to_le_bytes());
311    c.extend((hs as u16).to_le_bytes());
312    c.extend(((hs + body.len()) as u32).to_le_bytes());
313    c.extend(header);
314    c.extend(body);
315    c
316}
317
318fn write_el(e: &Element, p: &Pool, out: &mut Vec<u8>) {
319    let mut attrs = e.attrs.clone();
320    // Resource-id attributes sorted by id, then the rest (aapt2 order).
321    attrs.sort_by_key(|a| (a.res_id.is_none(), a.res_id.unwrap_or(0)));
322    let mut ext = vec![];
323    ext.extend(p.opt(&e.ns).to_le_bytes());
324    ext.extend(p.idx(&e.name).to_le_bytes());
325    ext.extend(20u16.to_le_bytes()); // attributeStart
326    ext.extend(20u16.to_le_bytes()); // attributeSize
327    ext.extend((attrs.len() as u16).to_le_bytes());
328    let id_index = attrs.iter().position(|a| a.ns.is_none() && a.name == "id").map_or(0, |i| i + 1) as u16;
329    ext.extend(id_index.to_le_bytes());
330    ext.extend(0u16.to_le_bytes()); // classIndex
331    ext.extend(0u16.to_le_bytes()); // styleIndex
332    for a in &attrs {
333        ext.extend(p.opt(&a.ns).to_le_bytes());
334        ext.extend(p.idx(&a.name).to_le_bytes());
335        let (raw, dtype, data) = match &a.value {
336            Value::Str(s) => (p.idx(s), TYPE_STRING, p.idx(s)),
337            Value::Typed { dtype, data } => (u32::MAX, *dtype, *data),
338        };
339        ext.extend(raw.to_le_bytes());
340        ext.extend(8u16.to_le_bytes());
341        ext.push(0);
342        ext.push(dtype);
343        ext.extend(data.to_le_bytes());
344    }
345    let node_header = [1u32.to_le_bytes(), u32::MAX.to_le_bytes()].concat(); // line, comment
346    out.extend(chunk(0x0102, &node_header, &ext));
347    for c in &e.children {
348        match c {
349            Node::Element(x) => write_el(x, p, out),
350            Node::Text(t) => {
351                let mut body = p.idx(t).to_le_bytes().to_vec();
352                body.extend(8u16.to_le_bytes());
353                body.push(0);
354                body.push(0);
355                body.extend(0u32.to_le_bytes());
356                out.extend(chunk(0x0104, &node_header, &body));
357            }
358        }
359    }
360    let mut end = p.opt(&e.ns).to_le_bytes().to_vec();
361    end.extend(p.idx(&e.name).to_le_bytes());
362    out.extend(chunk(0x0103, &node_header, &end));
363}
364
365pub fn write(d: &Doc) -> Vec<u8> {
366    let mut attr_names = vec![];
367    let mut other = vec![];
368    for (pfx, uri) in &d.namespaces {
369        other.push(pfx.clone());
370        other.push(uri.clone());
371    }
372    collect(&d.root, &mut attr_names, &mut other);
373    // Attribute names with resource ids first (indices line up with the resource map). A name
374    // that is also used as a plain string keeps its first (attribute) index.
375    let mut strings: Vec<String> = attr_names.iter().map(|(n, _)| n.clone()).collect();
376    let mut index: HashMap<String, u32> = HashMap::new();
377    for (i, s) in strings.iter().enumerate() {
378        index.entry(s.clone()).or_insert(i as u32);
379    }
380    for s in other {
381        if !index.contains_key(&s) {
382            index.insert(s.clone(), strings.len() as u32);
383            strings.push(s);
384        }
385    }
386    let pool = Pool { strings, index };
387
388    // String pool (UTF-16).
389    let mut offsets = vec![];
390    let mut data = vec![];
391    for s in &pool.strings {
392        offsets.push(data.len() as u32);
393        data.extend(utf16_entry(s));
394    }
395    while data.len() % 4 != 0 {
396        data.push(0);
397    }
398    let n = pool.strings.len() as u32;
399    let strings_start = 28 + 4 * n;
400    let mut ph = vec![];
401    ph.extend(n.to_le_bytes());
402    ph.extend(0u32.to_le_bytes()); // styles
403    ph.extend(0u32.to_le_bytes()); // flags: UTF-16
404    ph.extend(strings_start.to_le_bytes());
405    ph.extend(0u32.to_le_bytes());
406    let mut pb = vec![];
407    for o in offsets {
408        pb.extend(o.to_le_bytes());
409    }
410    pb.extend(data);
411    let mut body = chunk(0x0001, &ph, &pb);
412    let mut rm = vec![];
413    for (_, id) in &attr_names {
414        rm.extend(id.to_le_bytes());
415    }
416    body.extend(chunk(0x0180, &[], &rm));
417    let node_header = [1u32.to_le_bytes(), u32::MAX.to_le_bytes()].concat();
418    for (pfx, uri) in &d.namespaces {
419        let mut b = pool.idx(pfx).to_le_bytes().to_vec();
420        b.extend(pool.idx(uri).to_le_bytes());
421        body.extend(chunk(0x0100, &node_header, &b));
422    }
423    write_el(&d.root, &pool, &mut body);
424    for (pfx, uri) in d.namespaces.iter().rev() {
425        let mut b = pool.idx(pfx).to_le_bytes().to_vec();
426        b.extend(pool.idx(uri).to_le_bytes());
427        body.extend(chunk(0x0101, &node_header, &b));
428    }
429    chunk(0x0003, &[], &body)
430}
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435
436    fn sample() -> Doc {
437        let mut app = Element::new("application", vec![Attr::android("name", attr::NAME, Value::Str("a.App".into()))]);
438        app.ns = None;
439        let mut act = Element::new(
440            "activity",
441            vec![
442                Attr::android("exported", attr::EXPORTED, Value::Typed { dtype: TYPE_INT_BOOLEAN, data: u32::MAX }),
443                Attr::android("name", attr::NAME, Value::Str("a.Main".into())),
444            ],
445        );
446        act.push(Element::new("intent-filter", vec![]));
447        app.push(act);
448        let mut root = Element::new(
449            "manifest",
450            vec![Attr { ns: None, name: "package".into(), res_id: None, value: Value::Str("a.b".into()) }],
451        );
452        root.push(app);
453        Doc { namespaces: vec![("android".into(), ANDROID_NS.into())], root }
454    }
455
456    #[test]
457    fn roundtrip_and_sorting() {
458        let d = sample();
459        let b = write(&d);
460        let p = parse(&b).unwrap();
461        // attributes come back sorted by resource id (name 0x01010003 before exported 0x01010010)
462        let act = p.root.elements().next().unwrap().elements().next().unwrap();
463        assert_eq!(act.attrs[0].res_id, Some(attr::NAME));
464        assert_eq!(act.android_name(), Some("a.Main"));
465        assert_eq!(write(&p), b, "write is stable");
466        // the flat reader used by split agrees
467        let mi = crate::apk::manifest_info(&b).unwrap();
468        assert_eq!(mi.package, "a.b");
469        assert_eq!(mi.activities, vec!["a.Main".to_string()]);
470    }
471}