cmd/gomobile: remove binary XML
It is a full of unused code. Remove it.
Change-Id: I086f244adde57c8c9f1fc73b46e25550f15562f7
Reviewed-on: https://go-review.googlesource.com/c/mobile/+/369197
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/cmd/gomobile/binary_xml.go b/cmd/gomobile/binary_xml.go
deleted file mode 100644
index e8b620a..0000000
--- a/cmd/gomobile/binary_xml.go
+++ /dev/null
@@ -1,784 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "encoding/xml"
- "fmt"
- "io"
- "sort"
- "strconv"
- "strings"
- "unicode/utf16"
-)
-
-// binaryXML converts XML into Android's undocumented binary XML format.
-//
-// The best source of information on this format seems to be the source code
-// in AOSP frameworks-base. Android "resource" types seem to describe the
-// encoded bytes, in particular:
-//
-// ResChunk_header
-// ResStringPool_header
-// ResXMLTree_node
-//
-// These are defined in:
-//
-// https://android.googlesource.com/platform/frameworks/base/+/master/include/androidfw/ResourceTypes.h
-//
-// The rough format of the file is a resource chunk containing a sequence of
-// chunks. Each chunk is made up of a header and a body. The header begins with
-// the contents of the ResChunk_header struct, which includes the size of both
-// the header and the body.
-//
-// Both the header and body are 4-byte aligned.
-//
-// Values are encoded as little-endian.
-//
-// The android source code for encoding is done in the aapt tool. Its source
-// code lives in AOSP:
-//
-// https://android.googlesource.com/platform/frameworks/base.git/+/master/tools/aapt
-//
-// A sample layout:
-//
-// File Header (ResChunk_header, type XML)
-// Chunk: String Pool (type STRING_POOL)
-// Sequence of strings, each with the format:
-// uint16 length
-// uint16 extended_length -- only if top bit set on length
-// UTF-16LE string
-// two zero bytes
-// Resource Map
-// The [i]th 4-byte entry in the resource map corresponds with
-// the [i]th string from the string pool. The 4-bytes are a
-// Resource ID constant defined:
-// http://developer.android.com/reference/android/R.attr.html
-// This appears to be a way to map strings onto enum values.
-// Chunk: Namespace Start (ResXMLTree_node; ResXMLTree_namespaceExt)
-// Chunk: Element Start
-// ResXMLTree_node
-// ResXMLTree_attrExt
-// ResXMLTree_attribute (repeated attributeCount times)
-// Chunk: Element End
-// (ResXMLTree_node; ResXMLTree_endElementExt)
-// ...
-// Chunk: Namespace End
-func binaryXML(r io.Reader) ([]byte, error) {
- lr := &lineReader{r: r}
- d := xml.NewDecoder(lr)
-
- pool := new(binStringPool)
- depth := 0
- elements := []chunk{}
- namespaceEnds := make(map[int][]binEndNamespace)
-
- for {
- line := lr.line(d.InputOffset())
- tok, err := d.Token()
- if err != nil {
- if err == io.EOF {
- break
- }
- return nil, err
- }
- switch tok := tok.(type) {
- case xml.StartElement:
- // uses-sdk is synthesized by the writer, disallow declaration in manifest.
- if tok.Name.Local == "uses-sdk" {
- return nil, fmt.Errorf("unsupported manifest tag <uses-sdk .../>")
- } else if tok.Name.Local == "application" {
- // synthesize <uses-sdk/> before handling <application> token
- attr := xml.Attr{
- Name: xml.Name{
- Space: "http://schemas.android.com/apk/res/android",
- Local: "minSdkVersion",
- },
- Value: "15",
- }
- ba, err := pool.getAttr(attr)
- if err != nil {
- return nil, fmt.Errorf("failed to synthesize attr minSdkVersion=\"15\"")
- }
- elements = append(elements,
- &binStartElement{
- line: line - 1, // current testing strategy is not friendly to synthesized tags, -1 for would-be location
- ns: pool.getNS(""),
- name: pool.get("uses-sdk"),
- attr: []*binAttr{ba},
- },
- &binEndElement{
- line: line - 1,
- ns: pool.getNS(""),
- name: pool.get("uses-sdk"),
- })
- }
- // Intercept namespace definitions.
- var attr []*binAttr
- for _, a := range tok.Attr {
- if a.Name.Space == "xmlns" {
- elements = append(elements, binStartNamespace{
- line: line,
- prefix: pool.get(a.Name.Local),
- url: pool.get(a.Value),
- })
- namespaceEnds[depth] = append([]binEndNamespace{{
- line: line,
- prefix: pool.get(a.Name.Local),
- url: pool.get(a.Value),
- }}, namespaceEnds[depth]...)
- continue
- }
- ba, err := pool.getAttr(a)
- if err != nil {
- return nil, fmt.Errorf("%d: %s: %v", line, a.Name.Local, err)
- }
- attr = append(attr, ba)
- }
-
- depth++
- elements = append(elements, &binStartElement{
- line: line,
- ns: pool.getNS(tok.Name.Space),
- name: pool.get(tok.Name.Local),
- attr: attr,
- })
- case xml.EndElement:
- elements = append(elements, &binEndElement{
- line: line,
- ns: pool.getNS(tok.Name.Space),
- name: pool.get(tok.Name.Local),
- })
- depth--
- if nsEnds := namespaceEnds[depth]; len(nsEnds) > 0 {
- delete(namespaceEnds, depth)
- for _, nsEnd := range nsEnds {
- elements = append(elements, nsEnd)
- }
- }
- case xml.CharData:
- // The aapt tool appears to "compact" leading and
- // trailing whitepsace. See XMLNode::removeWhitespace in
- // https://android.googlesource.com/platform/frameworks/base.git/+/master/tools/aapt/XMLNode.cpp
- if len(tok) == 0 {
- continue
- }
- start, end := 0, len(tok)
- for start < len(tok) && isSpace(tok[start]) {
- start++
- }
- for end > start && isSpace(tok[end-1]) {
- end--
- }
- if start == end {
- continue // all whitespace, skip it
- }
-
- // Preserve one character of whitespace.
- if start > 0 {
- start--
- }
- if end < len(tok) {
- end++
- }
-
- elements = append(elements, &binCharData{
- line: line,
- data: pool.get(string(tok[start:end])),
- })
- case xml.Comment:
- // Ignored by Anroid Binary XML format.
- case xml.ProcInst:
- // Ignored by Anroid Binary XML format?
- case xml.Directive:
- // Ignored by Anroid Binary XML format.
- default:
- return nil, fmt.Errorf("apk: unexpected token: %v (%T)", tok, tok)
- }
- }
-
- sortPool(pool)
- for _, e := range elements {
- if e, ok := e.(*binStartElement); ok {
- sortAttr(e, pool)
- }
- }
-
- resMap := &binResMap{pool}
-
- size := 8 + pool.size() + resMap.size()
- for _, e := range elements {
- size += e.size()
- }
-
- b := make([]byte, 0, size)
- b = appendHeader(b, headerXML, size)
- b = pool.append(b)
- b = resMap.append(b)
- for _, e := range elements {
- b = e.append(b)
- }
-
- return b, nil
-}
-
-func isSpace(b byte) bool {
- switch b {
- case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0:
- return true
- }
- return false
-}
-
-type headerType uint16
-
-const (
- headerXML headerType = 0x0003
- headerStringPool = 0x0001
- headerResourceMap = 0x0180
- headerStartNamespace = 0x0100
- headerEndNamespace = 0x0101
- headerStartElement = 0x0102
- headerEndElement = 0x0103
- headerCharData = 0x0104
-)
-
-func appendU16(b []byte, v uint16) []byte {
- return append(b, byte(v), byte(v>>8))
-}
-
-func appendU32(b []byte, v uint32) []byte {
- return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24))
-}
-
-func appendHeader(b []byte, typ headerType, size int) []byte {
- b = appendU16(b, uint16(typ))
- b = appendU16(b, 8)
- b = appendU16(b, uint16(size))
- b = appendU16(b, 0)
- return b
-}
-
-// Attributes of the form android:key are mapped to resource IDs, which are
-// embedded into the Binary XML format.
-//
-// http://developer.android.com/reference/android/R.attr.html
-var resourceCodes = map[string]uint32{
- "versionCode": 0x0101021b,
- "versionName": 0x0101021c,
- "minSdkVersion": 0x0101020c,
- "windowFullscreen": 0x0101020d,
- "theme": 0x01010000,
- "label": 0x01010001,
- "hasCode": 0x0101000c,
- "debuggable": 0x0101000f,
- "name": 0x01010003,
- "screenOrientation": 0x0101001e,
- "configChanges": 0x0101001f,
- "value": 0x01010024,
-}
-
-// http://developer.android.com/reference/android/R.attr.html#configChanges
-var configChanges = map[string]uint32{
- "mcc": 0x0001,
- "mnc": 0x0002,
- "locale": 0x0004,
- "touchscreen": 0x0008,
- "keyboard": 0x0010,
- "keyboardHidden": 0x0020,
- "navigation": 0x0040,
- "orientation": 0x0080,
- "screenLayout": 0x0100,
- "uiMode": 0x0200,
- "screenSize": 0x0400,
- "smallestScreenSize": 0x0800,
- "layoutDirection": 0x2000,
- "fontScale": 0x40000000,
-}
-
-// http://developer.android.com/reference/android/R.attr.html#screenOrientation
-var screenOrientation = map[string]int{
- "unspecified": -1,
- "landscape": 0,
- "portrait": 1,
- "user": 2,
- "behind": 3,
- "sensor": 4,
- "nosensor": 5,
- "sensorLandscape": 6,
- "sensorPortrait": 7,
- "reverseLandscape": 8,
- "reversePortrait": 9,
- "fullSensor": 10,
- "userLandscape": 11,
- "userPortrait": 12,
- "fullUser": 13,
- "locked": 14,
-}
-
-// reference is an alias used to write out correct type in bin.
-type reference uint32
-
-// http://developer.android.com/reference/android/R.style.html
-var theme = map[string]reference{
- "Theme": 0x01030005,
- "Theme_NoTitleBar": 0x01030006,
- "Theme_NoTitleBar_Fullscreen": 0x01030007,
- "Theme_Black": 0x01030008,
- "Theme_Black_NoTitleBar": 0x01030009,
- "Theme_Black_NoTitleBar_Fullscreen": 0x0103000a,
- "Theme_Light": 0x0103000c,
- "Theme_Light_NoTitleBar": 0x0103000d,
- "Theme_Light_NoTitleBar_Fullscreen": 0x0103000e,
- "Theme_Translucent": 0x0103000f,
- "Theme_Translucent_NoTitleBar": 0x01030010,
- "Theme_Translucent_NoTitleBar_Fullscreen": 0x01030011,
-}
-
-type lineReader struct {
- off int64
- lines []int64
- r io.Reader
-}
-
-func (r *lineReader) Read(p []byte) (n int, err error) {
- n, err = r.r.Read(p)
- for i := 0; i < n; i++ {
- if p[i] == '\n' {
- r.lines = append(r.lines, r.off+int64(i))
- }
- }
- r.off += int64(n)
- return n, err
-}
-
-func (r *lineReader) line(pos int64) int {
- return sort.Search(len(r.lines), func(i int) bool {
- return pos < r.lines[i]
- }) + 1
-}
-
-type bstring struct {
- ind uint32
- str string
- enc []byte // 2-byte length, utf16le, 2-byte zero
-}
-
-type chunk interface {
- size() int
- append([]byte) []byte
-}
-
-type binResMap struct {
- pool *binStringPool
-}
-
-func (p *binResMap) append(b []byte) []byte {
- b = appendHeader(b, headerResourceMap, p.size())
- for _, bstr := range p.pool.s {
- c, ok := resourceCodes[bstr.str]
- if !ok {
- break
- }
- b = appendU32(b, c)
- }
- return b
-}
-
-func (p *binResMap) size() int {
- count := 0
- for _, bstr := range p.pool.s {
- if _, ok := resourceCodes[bstr.str]; !ok {
- break
- }
- count++
- }
- return 8 + 4*count
-}
-
-type binStringPool struct {
- s []*bstring
- m map[string]*bstring
-}
-
-func (p *binStringPool) get(str string) *bstring {
- if p.m == nil {
- p.m = make(map[string]*bstring)
- }
- res := p.m[str]
- if res != nil {
- return res
- }
- res = &bstring{
- ind: uint32(len(p.s)),
- str: str,
- }
- p.s = append(p.s, res)
- p.m[str] = res
-
- if len(str)>>16 > 0 {
- panic(fmt.Sprintf("string lengths over 1<<15 not yet supported, got len %d for string that starts %q", len(str), str[:100]))
- }
- strUTF16 := utf16.Encode([]rune(str))
- res.enc = appendU16(nil, uint16(len(strUTF16)))
- for _, w := range strUTF16 {
- res.enc = appendU16(res.enc, w)
- }
- res.enc = appendU16(res.enc, 0)
- return res
-}
-
-func (p *binStringPool) getNS(ns string) *bstring {
- if ns == "" {
- // Register empty string for inclusion in output (like aapt),
- // but do not reference it from namespace elements.
- p.get("")
- return nil
- }
- return p.get(ns)
-}
-
-func (p *binStringPool) getAttr(attr xml.Attr) (*binAttr, error) {
- a := &binAttr{
- ns: p.getNS(attr.Name.Space),
- name: p.get(attr.Name.Local),
- }
- if attr.Name.Space != "http://schemas.android.com/apk/res/android" {
- a.data = p.get(attr.Value)
- return a, nil
- }
-
- // Some android attributes have interesting values.
- switch attr.Name.Local {
- case "versionCode", "minSdkVersion":
- v, err := strconv.Atoi(attr.Value)
- if err != nil {
- return nil, err
- }
- a.data = int(v)
- case "hasCode", "debuggable":
- v, err := strconv.ParseBool(attr.Value)
- if err != nil {
- return nil, err
- }
- a.data = v
- case "configChanges":
- v := uint32(0)
- for _, c := range strings.Split(attr.Value, "|") {
- v |= configChanges[c]
- }
- a.data = v
- case "screenOrientation":
- v := 0
- for _, c := range strings.Split(attr.Value, "|") {
- v |= screenOrientation[c]
- }
- a.data = v
- case "theme":
- v := attr.Value
- // strip prefix if present as only platform themes are supported
- if idx := strings.Index(attr.Value, "/"); idx != -1 {
- v = v[idx+1:]
- }
- v = strings.Replace(v, ".", "_", -1)
- a.data = theme[v]
- default:
- a.data = p.get(attr.Value)
- }
- return a, nil
-}
-
-const stringPoolPreamble = 0 +
- 8 + // chunk header
- 4 + // string count
- 4 + // style count
- 4 + // flags
- 4 + // strings start
- 4 + // styles start
- 0
-
-func (p *binStringPool) unpaddedSize() int {
- strLens := 0
- for _, s := range p.s {
- strLens += len(s.enc)
- }
- return stringPoolPreamble + 4*len(p.s) + strLens
-}
-
-func (p *binStringPool) size() int {
- size := p.unpaddedSize()
- size += size % 0x04
- return size
-}
-
-// overloaded for testing.
-var (
- sortPool = func(p *binStringPool) {
- sort.Sort(p)
-
- // Move resourceCodes to the front.
- s := make([]*bstring, 0)
- m := make(map[string]*bstring)
- for str := range resourceCodes {
- bstr := p.m[str]
- if bstr == nil {
- continue
- }
- bstr.ind = uint32(len(s))
- s = append(s, bstr)
- m[str] = bstr
- delete(p.m, str)
- }
- for _, bstr := range p.m {
- bstr.ind = uint32(len(s))
- s = append(s, bstr)
- }
- p.s = s
- p.m = m
- }
- sortAttr = func(e *binStartElement, p *binStringPool) {}
-)
-
-func (b *binStringPool) Len() int { return len(b.s) }
-func (b *binStringPool) Less(i, j int) bool { return b.s[i].str < b.s[j].str }
-func (b *binStringPool) Swap(i, j int) {
- b.s[i], b.s[j] = b.s[j], b.s[i]
- b.s[i].ind, b.s[j].ind = b.s[j].ind, b.s[i].ind
-}
-
-func (p *binStringPool) append(b []byte) []byte {
- stringsStart := uint32(stringPoolPreamble + 4*len(p.s))
- b = appendU16(b, uint16(headerStringPool))
- b = appendU16(b, 0x1c) // chunk header size
- b = appendU16(b, uint16(p.size()))
- b = appendU16(b, 0)
- b = appendU32(b, uint32(len(p.s)))
- b = appendU32(b, 0) // style count
- b = appendU32(b, 0) // flags
- b = appendU32(b, stringsStart)
- b = appendU32(b, 0) // styles start
-
- off := 0
- for _, bstr := range p.s {
- b = appendU32(b, uint32(off))
- off += len(bstr.enc)
- }
- for _, bstr := range p.s {
- b = append(b, bstr.enc...)
- }
-
- for i := p.unpaddedSize() % 0x04; i > 0; i-- {
- b = append(b, 0)
- }
- return b
-}
-
-type binStartElement struct {
- line int
- ns *bstring
- name *bstring
- attr []*binAttr
-}
-
-func (e *binStartElement) size() int {
- return 8 + // chunk header
- 4 + // line number
- 4 + // comment
- 4 + // ns
- 4 + // name
- 2 + 2 + 2 + // attribute start, size, count
- 2 + 2 + 2 + // id/class/style index
- len(e.attr)*(4+4+4+4+4)
-}
-
-func (e *binStartElement) append(b []byte) []byte {
- b = appendU16(b, uint16(headerStartElement))
- b = appendU16(b, 0x10) // chunk header size
- b = appendU16(b, uint16(e.size()))
- b = appendU16(b, 0)
- b = appendU32(b, uint32(e.line))
- b = appendU32(b, 0xffffffff) // comment
- if e.ns == nil {
- b = appendU32(b, 0xffffffff)
- } else {
- b = appendU32(b, e.ns.ind)
- }
- b = appendU32(b, e.name.ind)
- b = appendU16(b, 0x14) // attribute start
- b = appendU16(b, 0x14) // attribute size
- b = appendU16(b, uint16(len(e.attr)))
- b = appendU16(b, 0) // ID index (none)
- b = appendU16(b, 0) // class index (none)
- b = appendU16(b, 0) // style index (none)
- for _, a := range e.attr {
- b = a.append(b)
- }
- return b
-}
-
-type binAttr struct {
- ns *bstring
- name *bstring
- data interface{} // either int (INT_DEC) or *bstring (STRING)
-}
-
-func (a *binAttr) append(b []byte) []byte {
- if a.ns != nil {
- b = appendU32(b, a.ns.ind)
- } else {
- b = appendU32(b, 0xffffffff)
- }
- b = appendU32(b, a.name.ind)
- switch v := a.data.(type) {
- case int:
- b = appendU32(b, 0xffffffff) // raw value
- b = appendU16(b, 8) // size
- b = append(b, 0) // unused padding
- b = append(b, 0x10) // INT_DEC
- b = appendU32(b, uint32(v))
- case bool:
- b = appendU32(b, 0xffffffff) // raw value
- b = appendU16(b, 8) // size
- b = append(b, 0) // unused padding
- b = append(b, 0x12) // INT_BOOLEAN
- if v {
- b = appendU32(b, 0xffffffff)
- } else {
- b = appendU32(b, 0)
- }
- case uint32:
- b = appendU32(b, 0xffffffff) // raw value
- b = appendU16(b, 8) // size
- b = append(b, 0) // unused padding
- b = append(b, 0x11) // INT_HEX
- b = appendU32(b, uint32(v))
- case reference:
- b = appendU32(b, 0xffffffff) // raw value
- b = appendU16(b, 8) // size
- b = append(b, 0) // unused padding
- b = append(b, 0x01) // REFERENCE
- b = appendU32(b, uint32(v))
- case *bstring:
- b = appendU32(b, v.ind) // raw value
- b = appendU16(b, 8) // size
- b = append(b, 0) // unused padding
- b = append(b, 0x03) // STRING
- b = appendU32(b, v.ind)
- default:
- panic(fmt.Sprintf("unexpected attr type: %T (%v)", v, v))
- }
- return b
-}
-
-type binEndElement struct {
- line int
- ns *bstring
- name *bstring
- attr []*binAttr
-}
-
-func (*binEndElement) size() int {
- return 8 + // chunk header
- 4 + // line number
- 4 + // comment
- 4 + // ns
- 4 // name
-}
-
-func (e *binEndElement) append(b []byte) []byte {
- b = appendU16(b, uint16(headerEndElement))
- b = appendU16(b, 0x10) // chunk header size
- b = appendU16(b, uint16(e.size()))
- b = appendU16(b, 0)
- b = appendU32(b, uint32(e.line))
- b = appendU32(b, 0xffffffff) // comment
- if e.ns == nil {
- b = appendU32(b, 0xffffffff)
- } else {
- b = appendU32(b, e.ns.ind)
- }
- b = appendU32(b, e.name.ind)
- return b
-}
-
-type binStartNamespace struct {
- line int
- prefix *bstring
- url *bstring
-}
-
-func (binStartNamespace) size() int {
- return 8 + // chunk header
- 4 + // line number
- 4 + // comment
- 4 + // prefix
- 4 // url
-}
-
-func (e binStartNamespace) append(b []byte) []byte {
- b = appendU16(b, uint16(headerStartNamespace))
- b = appendU16(b, 0x10) // chunk header size
- b = appendU16(b, uint16(e.size()))
- b = appendU16(b, 0)
- b = appendU32(b, uint32(e.line))
- b = appendU32(b, 0xffffffff) // comment
- b = appendU32(b, e.prefix.ind)
- b = appendU32(b, e.url.ind)
- return b
-}
-
-type binEndNamespace struct {
- line int
- prefix *bstring
- url *bstring
-}
-
-func (binEndNamespace) size() int {
- return 8 + // chunk header
- 4 + // line number
- 4 + // comment
- 4 + // prefix
- 4 // url
-}
-
-func (e binEndNamespace) append(b []byte) []byte {
- b = appendU16(b, uint16(headerEndNamespace))
- b = appendU16(b, 0x10) // chunk header size
- b = appendU16(b, uint16(e.size()))
- b = appendU16(b, 0)
- b = appendU32(b, uint32(e.line))
- b = appendU32(b, 0xffffffff) // comment
- b = appendU32(b, e.prefix.ind)
- b = appendU32(b, e.url.ind)
- return b
-}
-
-type binCharData struct {
- line int
- data *bstring
-}
-
-func (*binCharData) size() int {
- return 8 + // chunk header
- 4 + // line number
- 4 + // comment
- 4 + // data
- 8 // junk
-}
-
-func (e *binCharData) append(b []byte) []byte {
- b = appendU16(b, uint16(headerCharData))
- b = appendU16(b, 0x10) // chunk header size
- b = appendU16(b, 0x1c) // size
- b = appendU16(b, 0)
- b = appendU32(b, uint32(e.line))
- b = appendU32(b, 0xffffffff) // comment
- b = appendU32(b, e.data.ind)
- b = appendU16(b, 0x08)
- b = appendU16(b, 0)
- b = appendU16(b, 0)
- b = appendU16(b, 0)
- return b
-}
diff --git a/cmd/gomobile/binary_xml_test.go b/cmd/gomobile/binary_xml_test.go
deleted file mode 100644
index d4705ac..0000000
--- a/cmd/gomobile/binary_xml_test.go
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "archive/zip"
- "bytes"
- "flag"
- "fmt"
- "io/ioutil"
- "log"
- "os"
- "os/exec"
- "path/filepath"
- "testing"
-)
-
-var dump = flag.Bool("dump", false, "dump junk.bin binary output")
-
-var (
- origSortPool = sortPool
- origSortAttr = sortAttr
-)
-
-func TestBinaryXML(t *testing.T) {
- sortPool, sortAttr = sortToMatchTest, sortAttrToMatchTest
- defer func() { sortPool, sortAttr = origSortPool, origSortAttr }()
-
- bin, err := binaryXML(bytes.NewBufferString(fmt.Sprintf(input, "")))
- if err != nil {
- t.Fatal(err)
- }
- if *dump {
- if err := ioutil.WriteFile("junk.bin", bin, 0660); err != nil {
- t.Fatal(err)
- }
- }
-
- if exec.Command("which", "aapt").Run() != nil {
- t.Skip("command aapt not found, skipping")
- }
-
- apiPath, err := androidAPIPath()
- if err != nil {
- t.Fatal(err)
- }
- androidJar := filepath.Join(apiPath, "android.jar")
-
- tmpdir, err := ioutil.TempDir("", "gomobile-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(tmpdir)
-
- buf := new(bytes.Buffer)
- zw := zip.NewWriter(buf)
- zf, err := zw.Create("AndroidManifest.xml")
- if err != nil {
- t.Fatal(err)
- }
- if _, err := zf.Write(bin); err != nil {
- t.Fatal(err)
- }
- if err := zw.Close(); err != nil {
- t.Fatal(err)
- }
-
- mblapk := filepath.Join(tmpdir, "mbl.apk")
- if err := ioutil.WriteFile(mblapk, buf.Bytes(), 0755); err != nil {
- t.Fatal(err)
- }
-
- got, err := exec.Command("aapt", "d", "xmltree", mblapk, "AndroidManifest.xml").Output()
- if err != nil {
- t.Fatal(err)
- }
-
- manifest := filepath.Join(tmpdir, "AndroidManifest.xml")
- inp := fmt.Sprintf(input, "<uses-sdk android:minSdkVersion=\"15\" />")
- if err := ioutil.WriteFile(manifest, []byte(inp), 0755); err != nil {
- t.Fatal(err)
- }
-
- sdkapk := filepath.Join(tmpdir, "sdk.apk")
- if _, err := exec.Command("aapt", "p", "-M", manifest, "-I", androidJar, "-F", sdkapk).Output(); err != nil {
- t.Fatal(err)
- }
-
- sdkout, err := exec.Command("aapt", "d", "xmltree", sdkapk, "AndroidManifest.xml").Output()
- if err != nil {
- t.Fatal(err)
- }
- if *dump {
- b, err := ioutil.ReadFile(sdkapk)
- if err != nil {
- t.Fatal(err)
- }
- if err := ioutil.WriteFile("junk.sdk.apk", b, 0755); err != nil {
- t.Fatal(err)
- }
- }
-
- // manifests contain platformBuildVersionCode and platformBuildVersionName
- // which are not present in gomobile output.
- var fo [][]byte
- for _, line := range bytes.Split(sdkout, []byte{'\n'}) {
- if bytes.Contains(line, []byte("platformBuildVersionCode")) || bytes.Contains(line, []byte("platformBuildVersionName")) {
- continue
- }
- fo = append(fo, line)
- }
- want := bytes.Join(fo, []byte{'\n'})
-
- if !bytes.Equal(want, got) {
- t.Fatalf("output does not match\nWANT\n%s\nGOT\n%s\n", want, got)
- }
-}
-
-// The output of the Android encoder seems to be arbitrary. So for testing,
-// we sort the string pool order to match the output we have seen.
-func sortToMatchTest(p *binStringPool) {
- var names = []string{
- "versionCode",
- "versionName",
- "minSdkVersion",
- "theme",
- "label",
- "hasCode",
- "debuggable",
- "name",
- "screenOrientation",
- "configChanges",
- "value",
- "android",
- "http://schemas.android.com/apk/res/android",
- "",
- "package",
- "manifest",
- "com.zentus.balloon",
- "1.0",
- "uses-sdk",
- "application",
- "Balloon世界",
- "activity",
- "android.app.NativeActivity",
- "Balloon",
- "meta-data",
- "android.app.lib_name",
- "balloon",
- "intent-filter",
- "\there is some text\n",
- "action",
- "android.intent.action.MAIN",
- "category",
- "android.intent.category.LAUNCHER",
- }
-
- s := make([]*bstring, 0)
- m := make(map[string]*bstring)
-
- for _, str := range names {
- bstr := p.m[str]
- if bstr == nil {
- log.Printf("missing %q", str)
- continue
- }
- bstr.ind = uint32(len(s))
- s = append(s, bstr)
- m[str] = bstr
- delete(p.m, str)
- }
- // add unexpected strings
- for str, bstr := range p.m {
- log.Printf("unexpected %q", str)
- bstr.ind = uint32(len(s))
- s = append(s, bstr)
- }
- p.s = s
- p.m = m
-}
-
-func sortAttrToMatchTest(e *binStartElement, p *binStringPool) {
- order := []string{
- "versionCode",
- "versionName",
- "versionPackage",
-
- "theme",
- "label",
- "name",
- "screenOrientation",
- "configChanges",
- }
- ordered := make([]*binAttr, len(order))
-
-outer:
- for i, n := range order {
- for j, a := range e.attr {
- if a != nil && a.name.str == n {
- ordered[i] = a
- e.attr[j] = nil
- continue outer
- }
- }
- }
- var attr []*binAttr
- for _, a := range ordered {
- if a != nil {
- attr = append(attr, a)
- }
- }
- for _, a := range e.attr {
- if a != nil {
- attr = append(attr, a)
- }
- }
- e.attr = attr
-}
-
-const input = `<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 The Go Authors. All rights reserved.
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file.
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.zentus.balloon"
- android:versionCode="1"
- android:versionName="1.0">
-
- %s
- <application android:label="Balloon世界" android:hasCode="false" android:debuggable="true">
- <activity android:name="android.app.NativeActivity"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:label="Balloon"
- android:screenOrientation="portrait"
- android:configChanges="orientation|keyboardHidden">
- <meta-data android:name="android.app.lib_name" android:value="balloon" />
- <intent-filter>
- here is some text
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>`