xref: /aosp_15_r20/external/bazelbuild-rules_android/src/tools/ak/manifestutils.go (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1*9e965d6fSRomain Jobredeaux// Copyright 2018 The Bazel Authors. All rights reserved.
2*9e965d6fSRomain Jobredeaux//
3*9e965d6fSRomain Jobredeaux// Licensed under the Apache License, Version 2.0 (the "License");
4*9e965d6fSRomain Jobredeaux// you may not use this file except in compliance with the License.
5*9e965d6fSRomain Jobredeaux// You may obtain a copy of the License at
6*9e965d6fSRomain Jobredeaux//
7*9e965d6fSRomain Jobredeaux//    http://www.apache.org/licenses/LICENSE-2.0
8*9e965d6fSRomain Jobredeaux//
9*9e965d6fSRomain Jobredeaux// Unless required by applicable law or agreed to in writing, software
10*9e965d6fSRomain Jobredeaux// distributed under the License is distributed on an "AS IS" BASIS,
11*9e965d6fSRomain Jobredeaux// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9e965d6fSRomain Jobredeaux// See the License for the specific language governing permissions and
13*9e965d6fSRomain Jobredeaux// limitations under the License.
14*9e965d6fSRomain Jobredeaux
15*9e965d6fSRomain Jobredeaux// Package manifestutils provides common methods to interact with and modify AndroidManifest.xml files.
16*9e965d6fSRomain Jobredeauxpackage manifestutils
17*9e965d6fSRomain Jobredeaux
18*9e965d6fSRomain Jobredeauximport (
19*9e965d6fSRomain Jobredeaux	"encoding/xml"
20*9e965d6fSRomain Jobredeaux	"io"
21*9e965d6fSRomain Jobredeaux	"log"
22*9e965d6fSRomain Jobredeaux	"strings"
23*9e965d6fSRomain Jobredeaux
24*9e965d6fSRomain Jobredeaux	"src/common/golang/xml2"
25*9e965d6fSRomain Jobredeaux)
26*9e965d6fSRomain Jobredeaux
27*9e965d6fSRomain Jobredeaux// Constant attribute names used in an AndroidManifest.
28*9e965d6fSRomain Jobredeauxconst (
29*9e965d6fSRomain Jobredeaux	NameSpace           = "http://schemas.android.com/apk/res/android"
30*9e965d6fSRomain Jobredeaux	ElemManifest        = "manifest"
31*9e965d6fSRomain Jobredeaux	AttrPackage         = "package"
32*9e965d6fSRomain Jobredeaux	AttrSplit           = "split"
33*9e965d6fSRomain Jobredeaux	AttrFeatureName     = "featureName"
34*9e965d6fSRomain Jobredeaux	AttrSharedUserID    = "sharedUserId"
35*9e965d6fSRomain Jobredeaux	AttrSharedUserLabel = "sharedUserLabel"
36*9e965d6fSRomain Jobredeaux	AttrVersionCode     = "versionCode"
37*9e965d6fSRomain Jobredeaux	AttrVersionName     = "versionName"
38*9e965d6fSRomain Jobredeaux)
39*9e965d6fSRomain Jobredeaux
40*9e965d6fSRomain Jobredeauxvar (
41*9e965d6fSRomain Jobredeaux	// NoNSAttrs contains attributes that are not namespaced.
42*9e965d6fSRomain Jobredeaux	NoNSAttrs = map[string]bool{
43*9e965d6fSRomain Jobredeaux		AttrPackage:     true,
44*9e965d6fSRomain Jobredeaux		AttrSplit:       true,
45*9e965d6fSRomain Jobredeaux		AttrFeatureName: true}
46*9e965d6fSRomain Jobredeaux)
47*9e965d6fSRomain Jobredeaux
48*9e965d6fSRomain Jobredeaux// Manifest is the XML root that we want to parse.
49*9e965d6fSRomain Jobredeauxtype Manifest struct {
50*9e965d6fSRomain Jobredeaux	XMLName         xml.Name    `xml:"manifest"`
51*9e965d6fSRomain Jobredeaux	Package         string      `xml:"package,attr"`
52*9e965d6fSRomain Jobredeaux	SharedUserID    string      `xml:"sharedUserId,attr"`
53*9e965d6fSRomain Jobredeaux	SharedUserLabel string      `xml:"sharedUserLabel,attr"`
54*9e965d6fSRomain Jobredeaux	VersionCode     string      `xml:"versionCode,attr"`
55*9e965d6fSRomain Jobredeaux	VersionName     string      `xml:"versionName,attr"`
56*9e965d6fSRomain Jobredeaux	Application     Application `xml:"application"`
57*9e965d6fSRomain Jobredeaux}
58*9e965d6fSRomain Jobredeaux
59*9e965d6fSRomain Jobredeaux// Application is the XML tag that we want to parse.
60*9e965d6fSRomain Jobredeauxtype Application struct {
61*9e965d6fSRomain Jobredeaux	XMLName xml.Name `xml:"application"`
62*9e965d6fSRomain Jobredeaux	Name    string   `xml:"http://schemas.android.com/apk/res/android name,attr"`
63*9e965d6fSRomain Jobredeaux}
64*9e965d6fSRomain Jobredeaux
65*9e965d6fSRomain Jobredeaux// Encoder takes the xml.Token and encodes it, interface allows us to use xml2.Encoder.
66*9e965d6fSRomain Jobredeauxtype Encoder interface {
67*9e965d6fSRomain Jobredeaux	EncodeToken(xml.Token) error
68*9e965d6fSRomain Jobredeaux}
69*9e965d6fSRomain Jobredeaux
70*9e965d6fSRomain Jobredeaux// Patch updates an AndroidManifest by patching the attributes of existing elements.
71*9e965d6fSRomain Jobredeaux//
72*9e965d6fSRomain Jobredeaux// Attributes that are already defined on the element are updated, while missing
73*9e965d6fSRomain Jobredeaux// attributes are added to the element's attributes. Elements in patchElems that are
74*9e965d6fSRomain Jobredeaux// missing from the manifest are ignored.
75*9e965d6fSRomain Jobredeauxfunc Patch(dec *xml.Decoder, enc Encoder, patchElems map[string]map[string]xml.Attr) error {
76*9e965d6fSRomain Jobredeaux	for {
77*9e965d6fSRomain Jobredeaux		t, err := dec.Token()
78*9e965d6fSRomain Jobredeaux		if err != nil {
79*9e965d6fSRomain Jobredeaux			if err == io.EOF {
80*9e965d6fSRomain Jobredeaux				break
81*9e965d6fSRomain Jobredeaux			}
82*9e965d6fSRomain Jobredeaux			return err
83*9e965d6fSRomain Jobredeaux		}
84*9e965d6fSRomain Jobredeaux		switch tt := t.(type) {
85*9e965d6fSRomain Jobredeaux		case xml.StartElement:
86*9e965d6fSRomain Jobredeaux			elem := tt.Name.Local
87*9e965d6fSRomain Jobredeaux			if attrs, ok := patchElems[elem]; ok {
88*9e965d6fSRomain Jobredeaux				found := make(map[string]bool)
89*9e965d6fSRomain Jobredeaux				for i, a := range tt.Attr {
90*9e965d6fSRomain Jobredeaux					if attr, ok := attrs[a.Name.Local]; a.Name.Space == attr.Name.Space && ok {
91*9e965d6fSRomain Jobredeaux						found[a.Name.Local] = true
92*9e965d6fSRomain Jobredeaux						tt.Attr[i] = attr
93*9e965d6fSRomain Jobredeaux					}
94*9e965d6fSRomain Jobredeaux				}
95*9e965d6fSRomain Jobredeaux				for _, attr := range attrs {
96*9e965d6fSRomain Jobredeaux					if found[attr.Name.Local] {
97*9e965d6fSRomain Jobredeaux						continue
98*9e965d6fSRomain Jobredeaux					}
99*9e965d6fSRomain Jobredeaux
100*9e965d6fSRomain Jobredeaux					tt.Attr = append(tt.Attr, attr)
101*9e965d6fSRomain Jobredeaux				}
102*9e965d6fSRomain Jobredeaux			}
103*9e965d6fSRomain Jobredeaux			enc.EncodeToken(tt)
104*9e965d6fSRomain Jobredeaux		default:
105*9e965d6fSRomain Jobredeaux			enc.EncodeToken(tt)
106*9e965d6fSRomain Jobredeaux		}
107*9e965d6fSRomain Jobredeaux	}
108*9e965d6fSRomain Jobredeaux	return nil
109*9e965d6fSRomain Jobredeaux}
110*9e965d6fSRomain Jobredeaux
111*9e965d6fSRomain Jobredeaux// WriteManifest writes an AndroidManifest with updates to patched elements.
112*9e965d6fSRomain Jobredeauxfunc WriteManifest(dst io.Writer, src io.Reader, patchElems map[string]map[string]xml.Attr) error {
113*9e965d6fSRomain Jobredeaux	e := xml2.NewEncoder(dst)
114*9e965d6fSRomain Jobredeaux	if err := Patch(xml.NewDecoder(src), e, patchElems); err != nil {
115*9e965d6fSRomain Jobredeaux		return err
116*9e965d6fSRomain Jobredeaux	}
117*9e965d6fSRomain Jobredeaux	return e.Flush()
118*9e965d6fSRomain Jobredeaux}
119*9e965d6fSRomain Jobredeaux
120*9e965d6fSRomain Jobredeaux// CreatePatchElements creates an element map from a string array of "element:attr:attr_value" entries.
121*9e965d6fSRomain Jobredeauxfunc CreatePatchElements(attr []string) map[string]map[string]xml.Attr {
122*9e965d6fSRomain Jobredeaux	patchElems := make(map[string]map[string]xml.Attr)
123*9e965d6fSRomain Jobredeaux	for _, a := range attr {
124*9e965d6fSRomain Jobredeaux		pts := strings.Split(a, ":")
125*9e965d6fSRomain Jobredeaux		if len(pts) < 3 {
126*9e965d6fSRomain Jobredeaux			log.Fatalf("Failed to parse attr to replace %s", a)
127*9e965d6fSRomain Jobredeaux		}
128*9e965d6fSRomain Jobredeaux
129*9e965d6fSRomain Jobredeaux		elem := pts[0]
130*9e965d6fSRomain Jobredeaux		attr := pts[1]
131*9e965d6fSRomain Jobredeaux		ns := NameSpace
132*9e965d6fSRomain Jobredeaux
133*9e965d6fSRomain Jobredeaux		// https://developer.android.com/guide/topics/manifest/manifest-element
134*9e965d6fSRomain Jobredeaux		if elem == ElemManifest && NoNSAttrs[attr] {
135*9e965d6fSRomain Jobredeaux			ns = ""
136*9e965d6fSRomain Jobredeaux		}
137*9e965d6fSRomain Jobredeaux
138*9e965d6fSRomain Jobredeaux		if ais, ok := patchElems[elem]; ok {
139*9e965d6fSRomain Jobredeaux			ais[attr] = xml.Attr{
140*9e965d6fSRomain Jobredeaux				Name: xml.Name{Space: ns, Local: attr}, Value: pts[2]}
141*9e965d6fSRomain Jobredeaux		} else {
142*9e965d6fSRomain Jobredeaux			patchElems[elem] = map[string]xml.Attr{
143*9e965d6fSRomain Jobredeaux				attr: xml.Attr{
144*9e965d6fSRomain Jobredeaux					Name: xml.Name{Space: ns, Local: attr}, Value: pts[2]}}
145*9e965d6fSRomain Jobredeaux		}
146*9e965d6fSRomain Jobredeaux	}
147*9e965d6fSRomain Jobredeaux	return patchElems
148*9e965d6fSRomain Jobredeaux}
149