github.com/goki/mobile@v0.0.0-20230707090321-193544ec5700/internal/binres/binres.go (about)

     1  // Copyright 2015 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:generate go run genarsc.go
     6  //go:generate stringer -output binres_string.go -type ResType,DataType
     7  
     8  // Package binres implements encoding and decoding of android binary resources.
     9  //
    10  // Binary resource structs support unmarshalling the binary output of aapt.
    11  // Implementations of marshalling for each struct must produce the exact input
    12  // sent to unmarshalling. This allows tests to validate each struct representation
    13  // of the binary format as follows:
    14  //
    15  //   - unmarshal the output of aapt
    16  //   - marshal the struct representation
    17  //   - perform byte-to-byte comparison with aapt output per chunk header and body
    18  //
    19  // This process should strive to make structs idiomatic to make parsing xml text
    20  // into structs trivial.
    21  //
    22  // Once the struct representation is validated, tests for parsing xml text
    23  // into structs can become self-referential as the following holds true:
    24  //
    25  //   - the unmarshalled input of aapt output is the only valid target
    26  //   - the unmarshalled input of xml text may be compared to the unmarshalled
    27  //     input of aapt output to identify errors, e.g. text-trims, wrong flags, etc
    28  //
    29  // This provides validation, byte-for-byte, for producing binary xml resources.
    30  //
    31  // It should be made clear that unmarshalling binary resources is currently only
    32  // in scope for proving that the BinaryMarshaler works correctly. Any other use
    33  // is currently out of scope.
    34  //
    35  // A simple view of binary xml document structure:
    36  //
    37  //	XML
    38  //	  Pool
    39  //	  Map
    40  //	  Namespace
    41  //	  [...node]
    42  //
    43  // Additional resources:
    44  // https://android.googlesource.com/platform/frameworks/base/+/master/libs/androidfw/include/androidfw/ResourceTypes.h
    45  // https://justanapplication.wordpress.com/2011/09/13/ (a series of articles, increment date)
    46  package binres
    47  
    48  import (
    49  	"encoding"
    50  	"encoding/binary"
    51  	"encoding/xml"
    52  	"fmt"
    53  	"io"
    54  	"sort"
    55  	"strconv"
    56  	"strings"
    57  	"unicode"
    58  )
    59  
    60  func errWrongType(have ResType, want ...ResType) error {
    61  	return fmt.Errorf("wrong resource type %s, want one of %v", have, want)
    62  }
    63  
    64  type ResType uint16
    65  
    66  func (t ResType) IsSupported() bool {
    67  	return t != ResNull
    68  }
    69  
    70  // explicitly defined for clarity and resolvability with apt source
    71  const (
    72  	ResNull       ResType = 0x0000
    73  	ResStringPool ResType = 0x0001
    74  	ResTable      ResType = 0x0002
    75  	ResXML        ResType = 0x0003
    76  
    77  	ResXMLStartNamespace ResType = 0x0100
    78  	ResXMLEndNamespace   ResType = 0x0101
    79  	ResXMLStartElement   ResType = 0x0102
    80  	ResXMLEndElement     ResType = 0x0103
    81  	ResXMLCharData       ResType = 0x0104
    82  
    83  	ResXMLResourceMap ResType = 0x0180
    84  
    85  	ResTablePackage           ResType = 0x0200
    86  	ResTableType              ResType = 0x0201
    87  	ResTableTypeSpec          ResType = 0x0202
    88  	ResTableLibrary           ResType = 0x0203
    89  	ResTableOverlayable       ResType = 0x0204
    90  	ResTableOverlayablePolicy ResType = 0x0205
    91  	ResTableStagedAlias       ResType = 0x0206
    92  )
    93  
    94  var (
    95  	btou16 = binary.LittleEndian.Uint16
    96  	btou32 = binary.LittleEndian.Uint32
    97  	putu16 = binary.LittleEndian.PutUint16
    98  	putu32 = binary.LittleEndian.PutUint32
    99  )
   100  
   101  // unmarshaler wraps BinaryUnmarshaler to provide byte size of decoded chunks.
   102  type unmarshaler interface {
   103  	encoding.BinaryUnmarshaler
   104  
   105  	// size returns the byte size unmarshalled after a call to
   106  	// UnmarshalBinary, or otherwise zero.
   107  	size() int
   108  }
   109  
   110  // chunkHeader appears at the front of every data chunk in a resource.
   111  type chunkHeader struct {
   112  	// Type of data that follows this header.
   113  	typ ResType
   114  
   115  	// Advance slice index by this value to find its associated data, if any.
   116  	headerByteSize uint16
   117  
   118  	// This is the header size plus the size of any data associated with the chunk.
   119  	// Advance slice index by this value to completely skip its contents, including
   120  	// any child chunks. If this value is the same as headerByteSize, there is
   121  	// no data associated with the chunk.
   122  	byteSize uint32
   123  }
   124  
   125  // size implements unmarshaler.
   126  func (hdr chunkHeader) size() int { return int(hdr.byteSize) }
   127  
   128  func (hdr *chunkHeader) UnmarshalBinary(bin []byte) error {
   129  	hdr.typ = ResType(btou16(bin))
   130  	if !hdr.typ.IsSupported() {
   131  		return fmt.Errorf("%s not supported", hdr.typ)
   132  	}
   133  	hdr.headerByteSize = btou16(bin[2:])
   134  	hdr.byteSize = btou32(bin[4:])
   135  	if len(bin) < int(hdr.byteSize) {
   136  		return fmt.Errorf("too few bytes to unmarshal chunk body, have %v, need at-least %v", len(bin), hdr.byteSize)
   137  	}
   138  	return nil
   139  }
   140  
   141  func (hdr chunkHeader) MarshalBinary() ([]byte, error) {
   142  	if !hdr.typ.IsSupported() {
   143  		return nil, fmt.Errorf("%s not supported", hdr.typ)
   144  	}
   145  
   146  	bin := make([]byte, 8)
   147  	putu16(bin, uint16(hdr.typ))
   148  	putu16(bin[2:], hdr.headerByteSize)
   149  	putu32(bin[4:], hdr.byteSize)
   150  	return bin, nil
   151  }
   152  
   153  type XML struct {
   154  	chunkHeader
   155  
   156  	Pool *Pool
   157  	Map  *Map
   158  
   159  	Namespace *Namespace
   160  	Children  []*Element
   161  
   162  	// tmp field used when unmarshalling binary
   163  	stack []*Element
   164  }
   165  
   166  // RawValueByName returns the original raw string value of first matching element attribute, or error if not exists.
   167  // Given <manifest package="VAL" ...> then RawValueByName("manifest", xml.Name{Local: "package"}) returns "VAL".
   168  func (bx *XML) RawValueByName(elname string, attrname xml.Name) (string, error) {
   169  	elref, err := bx.Pool.RefByName(elname)
   170  	if err != nil {
   171  		return "", err
   172  	}
   173  	nref, err := bx.Pool.RefByName(attrname.Local)
   174  	if err != nil {
   175  		return "", err
   176  	}
   177  	nsref := PoolRef(NoEntry)
   178  	if attrname.Space != "" {
   179  		nsref, err = bx.Pool.RefByName(attrname.Space)
   180  		if err != nil {
   181  			return "", err
   182  		}
   183  	}
   184  
   185  	for el := range bx.iterElements() {
   186  		if el.Name == elref {
   187  			for _, attr := range el.attrs {
   188  				// TODO enforce TypedValue DataString constraint?
   189  				if nsref == attr.NS && nref == attr.Name {
   190  					return bx.Pool.strings[int(attr.RawValue)], nil
   191  				}
   192  			}
   193  		}
   194  	}
   195  	return "", fmt.Errorf("no matching element %q for attribute %+v found", elname, attrname)
   196  }
   197  
   198  const (
   199  	androidSchema = "http://schemas.android.com/apk/res/android"
   200  	toolsSchema   = "http://schemas.android.com/tools"
   201  )
   202  
   203  // skipSynthesize is set true for tests to avoid synthesis of additional nodes and attributes.
   204  var skipSynthesize bool
   205  
   206  // UnmarshalXML decodes an AndroidManifest.xml document returning type XML
   207  // containing decoded resources with the given minimum and target Android SDK / API version.
   208  func UnmarshalXML(r io.Reader, withIcon bool, minSdkVersion, targetSdkVersion int) (*XML, error) {
   209  	tbl, err := OpenTable()
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	lr := &lineReader{r: r}
   215  	dec := xml.NewDecoder(lr)
   216  	bx := new(XML)
   217  
   218  	// temporary pool to resolve real poolref later
   219  	pool := new(Pool)
   220  
   221  	type ltoken struct {
   222  		xml.Token
   223  		line int
   224  	}
   225  	var q []ltoken
   226  
   227  	for {
   228  		line := lr.line(dec.InputOffset())
   229  		tkn, err := dec.Token()
   230  		if err != nil {
   231  			if err == io.EOF {
   232  				break
   233  			}
   234  			return nil, err
   235  		}
   236  		tkn = xml.CopyToken(tkn)
   237  		switch tkn := tkn.(type) {
   238  		case xml.StartElement:
   239  			switch tkn.Name.Local {
   240  			default:
   241  				q = append(q, ltoken{tkn, line})
   242  			case "uses-sdk":
   243  				return nil, fmt.Errorf("manual declaration of uses-sdk in AndroidManifest.xml not supported")
   244  			case "manifest":
   245  				// synthesize additional attributes and nodes for use during encode.
   246  				tkn.Attr = append(tkn.Attr, xml.Attr{
   247  					Name: xml.Name{
   248  						Space: "",
   249  						Local: "platformBuildVersionCode",
   250  					},
   251  					Value: strconv.Itoa(targetSdkVersion),
   252  				},
   253  					xml.Attr{
   254  						Name: xml.Name{
   255  							Space: "",
   256  							Local: "platformBuildVersionName",
   257  						},
   258  						Value: "4.1.2-1425332",
   259  					},
   260  				)
   261  
   262  				q = append(q, ltoken{tkn, line})
   263  
   264  				if !skipSynthesize {
   265  					s := xml.StartElement{
   266  						Name: xml.Name{
   267  							Space: "",
   268  							Local: "uses-sdk",
   269  						},
   270  						Attr: []xml.Attr{
   271  							{
   272  								Name: xml.Name{
   273  									Space: androidSchema,
   274  									Local: "minSdkVersion",
   275  								},
   276  								Value: strconv.Itoa(minSdkVersion),
   277  							},
   278  							{
   279  								Name: xml.Name{
   280  									Space: androidSchema,
   281  									Local: "targetSdkVersion",
   282  								},
   283  								Value: strconv.Itoa(targetSdkVersion),
   284  							},
   285  						},
   286  					}
   287  					e := xml.EndElement{Name: xml.Name{Local: "uses-sdk"}}
   288  
   289  					q = append(q, ltoken{s, line}, ltoken{e, line})
   290  				}
   291  			case "application":
   292  				if !skipSynthesize {
   293  					for _, attr := range tkn.Attr {
   294  						if attr.Name.Space == androidSchema && attr.Name.Local == "icon" {
   295  							return nil, fmt.Errorf("manual declaration of android:icon in AndroidManifest.xml not supported")
   296  						}
   297  					}
   298  					if withIcon {
   299  						tkn.Attr = append(tkn.Attr,
   300  							xml.Attr{
   301  								Name: xml.Name{
   302  									Space: androidSchema,
   303  									Local: "icon",
   304  								},
   305  								Value: "@mipmap/icon",
   306  							})
   307  					}
   308  				}
   309  				q = append(q, ltoken{tkn, line})
   310  			case "activity", "intent-filter", "action", "category", "service", "meta-data":
   311  				// need android:exported="true" for activities in android sdk version 31 and above (still not working so testing with other things also set to exported)
   312  				if !skipSynthesize && targetSdkVersion >= 31 {
   313  					tkn.Attr = append(tkn.Attr,
   314  						xml.Attr{
   315  							Name: xml.Name{
   316  								Space: androidSchema,
   317  								Local: "exported",
   318  							},
   319  							Value: "true",
   320  						},
   321  					)
   322  				}
   323  				q = append(q, ltoken{tkn, line})
   324  			}
   325  		default:
   326  			q = append(q, ltoken{tkn, line})
   327  		}
   328  	}
   329  
   330  	for _, ltkn := range q {
   331  		tkn, line := ltkn.Token, ltkn.line
   332  		switch tkn := tkn.(type) {
   333  		case xml.StartElement:
   334  			el := &Element{
   335  				NodeHeader: NodeHeader{
   336  					LineNumber: uint32(line),
   337  					Comment:    0xFFFFFFFF,
   338  				},
   339  				NS:   NoEntry,
   340  				Name: pool.ref(tkn.Name.Local),
   341  			}
   342  			if len(bx.stack) == 0 {
   343  				bx.Children = append(bx.Children, el)
   344  			} else {
   345  				n := len(bx.stack)
   346  				var p *Element
   347  				p, bx.stack = bx.stack[n-1], bx.stack[:n-1]
   348  				p.Children = append(p.Children, el)
   349  				bx.stack = append(bx.stack, p)
   350  			}
   351  			bx.stack = append(bx.stack, el)
   352  
   353  			for _, attr := range tkn.Attr {
   354  				if (attr.Name.Space == "xmlns" && attr.Name.Local == "tools") || attr.Name.Space == toolsSchema {
   355  					continue // TODO can tbl be queried for schemas to determine validity instead?
   356  				}
   357  
   358  				if attr.Name.Space == "xmlns" && attr.Name.Local == "android" {
   359  					if bx.Namespace != nil {
   360  						return nil, fmt.Errorf("multiple declarations of xmlns:android encountered")
   361  					}
   362  					bx.Namespace = &Namespace{
   363  						NodeHeader: NodeHeader{
   364  							LineNumber: uint32(line),
   365  							Comment:    NoEntry,
   366  						},
   367  						prefix: 0,
   368  						uri:    0,
   369  					}
   370  					continue
   371  				}
   372  
   373  				nattr := &Attribute{
   374  					NS:       pool.ref(attr.Name.Space),
   375  					Name:     pool.ref(attr.Name.Local),
   376  					RawValue: NoEntry,
   377  				}
   378  				el.attrs = append(el.attrs, nattr)
   379  
   380  				if attr.Name.Space == "" {
   381  					nattr.NS = NoEntry
   382  					// TODO it's unclear how to query these
   383  					switch attr.Name.Local {
   384  					case "platformBuildVersionCode":
   385  						nattr.TypedValue.Type = DataIntDec
   386  						i, err := strconv.Atoi(attr.Value)
   387  						if err != nil {
   388  							return nil, err
   389  						}
   390  						nattr.TypedValue.Value = uint32(i)
   391  					default: // "package", "platformBuildVersionName", and any invalid
   392  						nattr.RawValue = pool.ref(attr.Value)
   393  						nattr.TypedValue.Type = DataString
   394  					}
   395  				} else {
   396  					// get type spec and value data type
   397  					ref, err := tbl.RefByName("attr/" + attr.Name.Local)
   398  					if err != nil {
   399  						return nil, err
   400  					}
   401  					nt, err := ref.Resolve(tbl)
   402  					if err != nil {
   403  						return nil, err
   404  					}
   405  					if len(nt.values) == 0 {
   406  						panic("encountered empty values slice")
   407  					}
   408  
   409  					if len(nt.values) == 1 {
   410  						val := nt.values[0]
   411  						if val.data.Type != DataIntDec {
   412  							panic("TODO only know how to handle DataIntDec type here")
   413  						}
   414  
   415  						t := DataType(val.data.Value)
   416  						switch t {
   417  						case DataString, DataAttribute, DataType(0x3e):
   418  							// TODO identify 0x3e, in bootstrap.xml this is the native lib name
   419  							nattr.RawValue = pool.ref(attr.Value)
   420  							nattr.TypedValue.Type = DataString
   421  							nattr.TypedValue.Value = uint32(nattr.RawValue)
   422  						case DataIntBool, DataType(0x08):
   423  							nattr.TypedValue.Type = DataIntBool
   424  							switch attr.Value {
   425  							case "true":
   426  								nattr.TypedValue.Value = 0xFFFFFFFF
   427  							case "false":
   428  								nattr.TypedValue.Value = 0
   429  							default:
   430  								return nil, fmt.Errorf("invalid bool value %q", attr.Value)
   431  							}
   432  						case DataIntDec, DataFloat, DataFraction:
   433  							// TODO DataFraction needs it's own case statement. minSdkVersion identifies as DataFraction
   434  							// but has accepted input in the past such as android:minSdkVersion="L"
   435  							// Other use-cases for DataFraction are currently unknown as applicable to manifest generation
   436  							// but this provides minimum support for writing out minSdkVersion="15" correctly.
   437  							nattr.TypedValue.Type = DataIntDec
   438  							i, err := strconv.Atoi(attr.Value)
   439  							if err != nil {
   440  								return nil, err
   441  							}
   442  							nattr.TypedValue.Value = uint32(i)
   443  						case DataReference:
   444  							nattr.TypedValue.Type = DataReference
   445  							dref, err := tbl.RefByName(attr.Value)
   446  							if err != nil {
   447  								if strings.HasPrefix(attr.Value, "@mipmap") {
   448  									// firstDrawableId is a TableRef matching first entry of mipmap spec initialized by NewMipmapTable.
   449  									// 7f is default package, 02 is mipmap spec, 0000 is first entry; e.g. R.drawable.icon
   450  									// TODO resource table should generate ids as required.
   451  									const firstDrawableId = 0x7f020000
   452  									nattr.TypedValue.Value = firstDrawableId
   453  									continue
   454  								}
   455  								return nil, err
   456  							}
   457  							nattr.TypedValue.Value = uint32(dref)
   458  						default:
   459  							return nil, fmt.Errorf("unhandled data type %0#2x: %s", uint8(t), t)
   460  						}
   461  					} else {
   462  						// 0x01000000 is an unknown ref that doesn't point to anything, typically
   463  						// located at the start of entry value lists, peek at last value to determine type.
   464  						t := nt.values[len(nt.values)-1].data.Type
   465  						switch t {
   466  						case DataIntDec:
   467  							for _, val := range nt.values {
   468  								if val.name == 0x01000000 {
   469  									continue
   470  								}
   471  								nr, err := val.name.Resolve(tbl)
   472  								if err != nil {
   473  									return nil, err
   474  								}
   475  								if attr.Value == nr.key.Resolve(tbl.pkgs[0].keyPool) { // TODO hard-coded pkg ref
   476  									nattr.TypedValue = *val.data
   477  									break
   478  								}
   479  							}
   480  						case DataIntHex:
   481  							nattr.TypedValue.Type = t
   482  							for _, x := range strings.Split(attr.Value, "|") {
   483  								for _, val := range nt.values {
   484  									if val.name == 0x01000000 {
   485  										continue
   486  									}
   487  									nr, err := val.name.Resolve(tbl)
   488  									if err != nil {
   489  										return nil, err
   490  									}
   491  									if x == nr.key.Resolve(tbl.pkgs[0].keyPool) { // TODO hard-coded pkg ref
   492  										nattr.TypedValue.Value |= val.data.Value
   493  										break
   494  									}
   495  								}
   496  							}
   497  						default:
   498  							return nil, fmt.Errorf("unhandled data type for configuration %0#2x: %s", uint8(t), t)
   499  						}
   500  					}
   501  				}
   502  			}
   503  		case xml.CharData:
   504  			if s := poolTrim(string(tkn)); s != "" {
   505  				cdt := &CharData{
   506  					NodeHeader: NodeHeader{
   507  						LineNumber: uint32(line),
   508  						Comment:    NoEntry,
   509  					},
   510  					RawData: pool.ref(s),
   511  				}
   512  				el := bx.stack[len(bx.stack)-1]
   513  				if el.head == nil {
   514  					el.head = cdt
   515  				} else if el.tail == nil {
   516  					el.tail = cdt
   517  				} else {
   518  					return nil, fmt.Errorf("element head and tail already contain chardata")
   519  				}
   520  			}
   521  		case xml.EndElement:
   522  			if tkn.Name.Local == "manifest" {
   523  				bx.Namespace.end = &Namespace{
   524  					NodeHeader: NodeHeader{
   525  						LineNumber: uint32(line),
   526  						Comment:    NoEntry,
   527  					},
   528  					prefix: 0,
   529  					uri:    0,
   530  				}
   531  			}
   532  			n := len(bx.stack)
   533  			var el *Element
   534  			el, bx.stack = bx.stack[n-1], bx.stack[:n-1]
   535  			if el.end != nil {
   536  				return nil, fmt.Errorf("element end already exists")
   537  			}
   538  			el.end = &ElementEnd{
   539  				NodeHeader: NodeHeader{
   540  					LineNumber: uint32(line),
   541  					Comment:    NoEntry,
   542  				},
   543  				NS:   el.NS,
   544  				Name: el.Name,
   545  			}
   546  		case xml.Comment, xml.ProcInst:
   547  			// discard
   548  		default:
   549  			panic(fmt.Errorf("unhandled token type: %T %+v", tkn, tkn))
   550  		}
   551  	}
   552  
   553  	// pools appear to be sorted as follows:
   554  	// * attribute names prefixed with android:
   555  	// * "android", [schema-url], [empty-string]
   556  	// * for each node:
   557  	//   * attribute names with no prefix
   558  	//   * node name
   559  	//   * attribute value if data type of name is DataString, DataAttribute, or 0x3e (an unknown)
   560  	bx.Pool = new(Pool)
   561  
   562  	var arecurse func(*Element)
   563  	arecurse = func(el *Element) {
   564  		for _, attr := range el.attrs {
   565  			if attr.NS == NoEntry {
   566  				continue
   567  			}
   568  			if attr.NS.Resolve(pool) == androidSchema {
   569  				bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool))
   570  			}
   571  		}
   572  		for _, child := range el.Children {
   573  			arecurse(child)
   574  		}
   575  	}
   576  	for _, el := range bx.Children {
   577  		arecurse(el)
   578  	}
   579  
   580  	// TODO encoding/xml does not enforce namespace prefix and manifest encoding in aapt
   581  	// appears to ignore all other prefixes. Inserting this manually is not strictly correct
   582  	// for the general case, but the effort to do otherwise currently offers nothing.
   583  	bx.Pool.strings = append(bx.Pool.strings, "android", androidSchema)
   584  
   585  	// there always appears to be an empty string located after schema, even if one is
   586  	// not present in manifest.
   587  	bx.Pool.strings = append(bx.Pool.strings, "")
   588  
   589  	var brecurse func(*Element)
   590  	brecurse = func(el *Element) {
   591  		for _, attr := range el.attrs {
   592  			if attr.NS == NoEntry {
   593  				bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool))
   594  			}
   595  		}
   596  
   597  		bx.Pool.strings = append(bx.Pool.strings, el.Name.Resolve(pool))
   598  
   599  		for _, attr := range el.attrs {
   600  			if attr.RawValue != NoEntry {
   601  				bx.Pool.strings = append(bx.Pool.strings, attr.RawValue.Resolve(pool))
   602  			} else if attr.NS == NoEntry {
   603  				bx.Pool.strings = append(bx.Pool.strings, fmt.Sprintf("%+v", attr.TypedValue.Value))
   604  			}
   605  		}
   606  
   607  		if el.head != nil {
   608  			bx.Pool.strings = append(bx.Pool.strings, el.head.RawData.Resolve(pool))
   609  		}
   610  		if el.tail != nil {
   611  			bx.Pool.strings = append(bx.Pool.strings, el.tail.RawData.Resolve(pool))
   612  		}
   613  
   614  		for _, child := range el.Children {
   615  			brecurse(child)
   616  		}
   617  	}
   618  	for _, el := range bx.Children {
   619  		brecurse(el)
   620  	}
   621  
   622  	// do not eliminate duplicates until the entire slice has been composed.
   623  	// consider <activity android:label="label" .../>
   624  	// all attribute names come first followed by values; in such a case, the value "label"
   625  	// would be a reference to the same "android:label" in the string pool which will occur
   626  	// within the beginning of the pool where other attr names are located.
   627  	bx.Pool.strings = asSet(bx.Pool.strings)
   628  
   629  	// TODO consider cases of multiple declarations of the same attr name that should return error
   630  	// before ever reaching this point.
   631  	bx.Map = new(Map)
   632  	for _, s := range bx.Pool.strings {
   633  		ref, err := tbl.RefByName("attr/" + s)
   634  		if err != nil {
   635  			break // break after first non-ref as all strings after are also non-refs.
   636  		}
   637  		bx.Map.rs = append(bx.Map.rs, ref)
   638  	}
   639  
   640  	// resolve tmp pool refs to final pool refs
   641  	// TODO drop this in favor of sort directly on Table
   642  	var resolve func(el *Element)
   643  	resolve = func(el *Element) {
   644  		if el.NS != NoEntry {
   645  			el.NS = bx.Pool.ref(el.NS.Resolve(pool))
   646  			el.end.NS = el.NS
   647  		}
   648  		el.Name = bx.Pool.ref(el.Name.Resolve(pool))
   649  		el.end.Name = el.Name
   650  		for _, attr := range el.attrs {
   651  			if attr.NS != NoEntry {
   652  				attr.NS = bx.Pool.ref(attr.NS.Resolve(pool))
   653  			}
   654  			attr.Name = bx.Pool.ref(attr.Name.Resolve(pool))
   655  			if attr.RawValue != NoEntry {
   656  				attr.RawValue = bx.Pool.ref(attr.RawValue.Resolve(pool))
   657  				if attr.TypedValue.Type == DataString {
   658  					attr.TypedValue.Value = uint32(attr.RawValue)
   659  				}
   660  			}
   661  		}
   662  		for _, child := range el.Children {
   663  			resolve(child)
   664  		}
   665  	}
   666  	for _, el := range bx.Children {
   667  		resolve(el)
   668  	}
   669  
   670  	var asort func(*Element)
   671  	asort = func(el *Element) {
   672  		sort.Sort(byType(el.attrs))
   673  		sort.Sort(byNamespace(el.attrs))
   674  		sort.Sort(byName(el.attrs))
   675  		for _, child := range el.Children {
   676  			asort(child)
   677  		}
   678  	}
   679  	for _, el := range bx.Children {
   680  		asort(el)
   681  	}
   682  
   683  	for i, s := range bx.Pool.strings {
   684  		switch s {
   685  		case androidSchema:
   686  			bx.Namespace.uri = PoolRef(i)
   687  			bx.Namespace.end.uri = PoolRef(i)
   688  		case "android":
   689  			bx.Namespace.prefix = PoolRef(i)
   690  			bx.Namespace.end.prefix = PoolRef(i)
   691  		}
   692  	}
   693  
   694  	return bx, nil
   695  }
   696  
   697  // UnmarshalBinary decodes all resource chunks in buf returning any error encountered.
   698  func (bx *XML) UnmarshalBinary(buf []byte) error {
   699  	if err := (&bx.chunkHeader).UnmarshalBinary(buf); err != nil {
   700  		return err
   701  	}
   702  	buf = buf[8:]
   703  	for len(buf) > 0 {
   704  		k, err := bx.unmarshalBinaryKind(buf)
   705  		if err != nil {
   706  			return err
   707  		}
   708  		buf = buf[k.size():]
   709  	}
   710  	return nil
   711  }
   712  
   713  // unmarshalBinaryKind decodes and stores the first resource chunk of bin.
   714  // It returns the unmarshaler interface and any error encountered.
   715  // If k.size() < len(bin), subsequent chunks can be decoded at bin[k.size():].
   716  func (bx *XML) unmarshalBinaryKind(bin []byte) (k unmarshaler, err error) {
   717  	k, err = bx.kind(ResType(btou16(bin)))
   718  	if err != nil {
   719  		return nil, err
   720  	}
   721  	if err = k.UnmarshalBinary(bin); err != nil {
   722  		return nil, err
   723  	}
   724  	return k, nil
   725  }
   726  
   727  func (bx *XML) kind(t ResType) (unmarshaler, error) {
   728  	switch t {
   729  	case ResStringPool:
   730  		if bx.Pool != nil {
   731  			return nil, fmt.Errorf("pool already exists")
   732  		}
   733  		bx.Pool = new(Pool)
   734  		return bx.Pool, nil
   735  	case ResXMLResourceMap:
   736  		if bx.Map != nil {
   737  			return nil, fmt.Errorf("resource map already exists")
   738  		}
   739  		bx.Map = new(Map)
   740  		return bx.Map, nil
   741  	case ResXMLStartNamespace:
   742  		if bx.Namespace != nil {
   743  			return nil, fmt.Errorf("namespace start already exists")
   744  		}
   745  		bx.Namespace = new(Namespace)
   746  		return bx.Namespace, nil
   747  	case ResXMLEndNamespace:
   748  		if bx.Namespace.end != nil {
   749  			return nil, fmt.Errorf("namespace end already exists")
   750  		}
   751  		bx.Namespace.end = new(Namespace)
   752  		return bx.Namespace.end, nil
   753  	case ResXMLStartElement:
   754  		el := new(Element)
   755  		if len(bx.stack) == 0 {
   756  			bx.Children = append(bx.Children, el)
   757  		} else {
   758  			n := len(bx.stack)
   759  			var p *Element
   760  			p, bx.stack = bx.stack[n-1], bx.stack[:n-1]
   761  			p.Children = append(p.Children, el)
   762  			bx.stack = append(bx.stack, p)
   763  		}
   764  		bx.stack = append(bx.stack, el)
   765  		return el, nil
   766  	case ResXMLEndElement:
   767  		n := len(bx.stack)
   768  		var el *Element
   769  		el, bx.stack = bx.stack[n-1], bx.stack[:n-1]
   770  		if el.end != nil {
   771  			return nil, fmt.Errorf("element end already exists")
   772  		}
   773  		el.end = new(ElementEnd)
   774  		return el.end, nil
   775  	case ResXMLCharData: // TODO assure correctness
   776  		cdt := new(CharData)
   777  		el := bx.stack[len(bx.stack)-1]
   778  		if el.head == nil {
   779  			el.head = cdt
   780  		} else if el.tail == nil {
   781  			el.tail = cdt
   782  		} else {
   783  			return nil, fmt.Errorf("element head and tail already contain chardata")
   784  		}
   785  		return cdt, nil
   786  	default:
   787  		return nil, fmt.Errorf("unexpected type %s", t)
   788  	}
   789  }
   790  
   791  func (bx *XML) MarshalBinary() ([]byte, error) {
   792  	bx.typ = ResXML
   793  	bx.headerByteSize = 8
   794  
   795  	var (
   796  		bin, b []byte
   797  		err    error
   798  	)
   799  	b, err = bx.chunkHeader.MarshalBinary()
   800  	if err != nil {
   801  		return nil, err
   802  	}
   803  	bin = append(bin, b...)
   804  
   805  	b, err = bx.Pool.MarshalBinary()
   806  	if err != nil {
   807  		return nil, err
   808  	}
   809  	bin = append(bin, b...)
   810  
   811  	b, err = bx.Map.MarshalBinary()
   812  	if err != nil {
   813  		return nil, err
   814  	}
   815  	bin = append(bin, b...)
   816  
   817  	b, err = bx.Namespace.MarshalBinary()
   818  	if err != nil {
   819  		return nil, err
   820  	}
   821  	bin = append(bin, b...)
   822  
   823  	for _, child := range bx.Children {
   824  		if err := marshalRecurse(child, &bin); err != nil {
   825  			return nil, err
   826  		}
   827  	}
   828  
   829  	b, err = bx.Namespace.end.MarshalBinary()
   830  	if err != nil {
   831  		return nil, err
   832  	}
   833  	bin = append(bin, b...)
   834  
   835  	putu32(bin[4:], uint32(len(bin)))
   836  	return bin, nil
   837  }
   838  
   839  func marshalRecurse(el *Element, bin *[]byte) error {
   840  	b, err := el.MarshalBinary()
   841  	if err != nil {
   842  		return err
   843  	}
   844  	*bin = append(*bin, b...)
   845  
   846  	if el.head != nil {
   847  		b, err := el.head.MarshalBinary()
   848  		if err != nil {
   849  			return err
   850  		}
   851  		*bin = append(*bin, b...)
   852  	}
   853  
   854  	for _, child := range el.Children {
   855  		if err := marshalRecurse(child, bin); err != nil {
   856  			return err
   857  		}
   858  	}
   859  
   860  	b, err = el.end.MarshalBinary()
   861  	if err != nil {
   862  		return err
   863  	}
   864  	*bin = append(*bin, b...)
   865  
   866  	return nil
   867  }
   868  
   869  func (bx *XML) iterElements() <-chan *Element {
   870  	ch := make(chan *Element, 1)
   871  	go func() {
   872  		for _, el := range bx.Children {
   873  			iterElementsRecurse(el, ch)
   874  		}
   875  		close(ch)
   876  	}()
   877  	return ch
   878  }
   879  
   880  func iterElementsRecurse(el *Element, ch chan *Element) {
   881  	ch <- el
   882  	for _, e := range el.Children {
   883  		iterElementsRecurse(e, ch)
   884  	}
   885  }
   886  
   887  // asSet returns a set from a slice of strings.
   888  func asSet(xs []string) []string {
   889  	m := make(map[string]bool)
   890  	fo := xs[:0]
   891  	for _, x := range xs {
   892  		if !m[x] {
   893  			m[x] = true
   894  			fo = append(fo, x)
   895  		}
   896  	}
   897  	return fo
   898  }
   899  
   900  // poolTrim trims all but immediately surrounding space.
   901  // \n\t\tfoobar\n\t\t becomes \tfoobar\n
   902  func poolTrim(s string) string {
   903  	var start, end int
   904  	for i, r := range s {
   905  		if !unicode.IsSpace(r) {
   906  			if i != 0 {
   907  				start = i - 1 // preserve preceding space
   908  			}
   909  			break
   910  		}
   911  	}
   912  
   913  	for i := len(s) - 1; i >= 0; i-- {
   914  		r := rune(s[i])
   915  		if !unicode.IsSpace(r) {
   916  			if i != len(s)-1 {
   917  				end = i + 2
   918  			}
   919  			break
   920  		}
   921  	}
   922  
   923  	if start == 0 && end == 0 {
   924  		return "" // every char was a space
   925  	}
   926  
   927  	return s[start:end]
   928  }
   929  
   930  // byNamespace sorts attributes based on string pool position of namespace.
   931  // Given that "android" always proceeds "" in the pool, this results in the
   932  // correct ordering of attributes.
   933  type byNamespace []*Attribute
   934  
   935  func (a byNamespace) Len() int { return len(a) }
   936  func (a byNamespace) Less(i, j int) bool {
   937  	return a[i].NS < a[j].NS
   938  }
   939  func (a byNamespace) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   940  
   941  // byType sorts attributes by the uint8 value of the type.
   942  type byType []*Attribute
   943  
   944  func (a byType) Len() int { return len(a) }
   945  func (a byType) Less(i, j int) bool {
   946  	return a[i].TypedValue.Type < a[j].TypedValue.Type
   947  }
   948  func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   949  
   950  // byName sorts attributes that have matching types based on string pool position of name.
   951  type byName []*Attribute
   952  
   953  func (a byName) Len() int { return len(a) }
   954  func (a byName) Less(i, j int) bool {
   955  	return (a[i].TypedValue.Type == DataString || a[i].TypedValue.Type == DataIntDec) &&
   956  		(a[j].TypedValue.Type == DataString || a[j].TypedValue.Type == DataIntDec) &&
   957  		a[i].Name < a[j].Name
   958  }
   959  func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   960  
   961  type lineReader struct {
   962  	off   int64
   963  	lines []int64
   964  	r     io.Reader
   965  }
   966  
   967  func (r *lineReader) Read(p []byte) (n int, err error) {
   968  	n, err = r.r.Read(p)
   969  	for i := 0; i < n; i++ {
   970  		if p[i] == '\n' {
   971  			r.lines = append(r.lines, r.off+int64(i))
   972  		}
   973  	}
   974  	r.off += int64(n)
   975  	return n, err
   976  }
   977  
   978  func (r *lineReader) line(pos int64) int {
   979  	return sort.Search(len(r.lines), func(i int) bool {
   980  		return pos < r.lines[i]
   981  	}) + 1
   982  }