xref: /aosp_15_r20/external/bazelbuild-rules_android/src/tools/ak/patch/patch.go (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1// Copyright 2018 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package patch sets/replaces the application class/package in a given AndroidManifest.xml.
16package patch
17
18import (
19	"bytes"
20	"encoding/xml"
21	"flag"
22	"io/ioutil"
23	"log"
24	"os"
25	"strings"
26	"sync"
27
28	"src/common/golang/flags"
29	"src/tools/ak/manifestutils"
30	"src/tools/ak/types"
31)
32
33var stubManifest = `<?xml version="1.0" encoding="utf-8"?>
34<manifest
35    xmlns:android="http://schemas.android.com/apk/res/android">
36  <application/>
37</manifest>`
38
39var (
40	// Cmd defines the command to run patch
41	Cmd = types.Command{
42		Init:  Init,
43		Run:   Run,
44		Desc:  desc,
45		Flags: []string{"in", "out", "attr", "app", "oldapp", "pkg"},
46	}
47
48	// Variables that hold flag values
49	split  flags.StringList
50	attr   flags.StringList
51	in     string
52	out    string
53	app    string
54	oldApp string
55	pkg    string
56
57	initOnce sync.Once
58)
59
60// Init initializes patch.
61func Init() {
62	initOnce.Do(func() {
63		flag.StringVar(&in, "in", "", "Path to the input xml file.")
64		flag.StringVar(&out, "out", "", "Path to the output xml file.")
65		flag.Var(&attr, "attr", "(optional) attr(s) to set. {element}:{attr}:{value}.")
66		flag.Var(&split, "split", "(optional) splits(s) to write. {name}:{file}.")
67		flag.StringVar(&oldApp, "oldapp", "", "(optional) Path to output the old application class name.")
68		flag.StringVar(&pkg, "pkg", "", "(optional) Path to output the package name.")
69	})
70}
71
72func desc() string {
73	return "Setapp sets/replaces the application class in a given AndroidManifest.xml."
74}
75
76// Run is the entry point for patch.
77func Run() {
78	if in == "" || (out == "" && split == nil) {
79		log.Fatal("fields and -in and -out|-splits and must be defined.")
80	}
81
82	elems := manifestutils.CreatePatchElements(attr)
83	elems[manifestutils.ElemManifest] = make(map[string]xml.Attr)
84
85	b, err := ioutil.ReadFile(in)
86	if err != nil {
87		log.Fatalf("ioutil.ReadFile(%q) failed: %v", in, err)
88	}
89	var manifest manifestutils.Manifest
90	xml.Unmarshal(b, &manifest)
91
92	// Optional parse package name and/or application class name before replacing
93	if pkg != "" || oldApp != "" {
94
95		if pkg != "" {
96			err = ioutil.WriteFile(pkg, []byte(manifest.Package), 0644)
97			if err != nil {
98				log.Fatalf("ioutil.WriteFile(%q) failed: %v", pkg, err)
99			}
100		}
101		if oldApp != "" {
102			appName := manifest.Application.Name
103			if appName == "" {
104				appName = "android.app.Application"
105			}
106			err := ioutil.WriteFile(oldApp, []byte(appName), 0644)
107			if err != nil {
108				log.Fatalf("ioutil.WriteFile(%q) failed: %v", oldApp, err)
109			}
110		}
111	}
112
113	// parse manifest attrs if any for stub manifest
114	if manifest.Package != "" {
115		elems[manifestutils.ElemManifest][manifestutils.AttrPackage] = xml.Attr{
116			Name: xml.Name{Local: manifestutils.AttrPackage}, Value: manifest.Package}
117	}
118	if manifest.SharedUserID != "" {
119		elems[manifestutils.ElemManifest][manifestutils.AttrSharedUserID] = xml.Attr{
120			Name:  xml.Name{Space: manifestutils.NameSpace, Local: manifestutils.AttrSharedUserID},
121			Value: manifest.SharedUserID}
122	}
123	if manifest.SharedUserLabel != "" {
124		elems[manifestutils.ElemManifest][manifestutils.AttrSharedUserLabel] = xml.Attr{
125			Name:  xml.Name{Space: manifestutils.NameSpace, Local: manifestutils.AttrSharedUserLabel},
126			Value: manifest.SharedUserLabel}
127	}
128	if manifest.VersionCode != "" {
129		elems[manifestutils.ElemManifest][manifestutils.AttrVersionCode] = xml.Attr{
130			Name:  xml.Name{Space: manifestutils.NameSpace, Local: manifestutils.AttrVersionCode},
131			Value: manifest.VersionCode}
132	}
133	if manifest.VersionName != "" {
134		elems[manifestutils.ElemManifest][manifestutils.AttrVersionName] = xml.Attr{
135			Name:  xml.Name{Space: manifestutils.NameSpace, Local: manifestutils.AttrVersionName},
136			Value: manifest.VersionName}
137	}
138
139	if out != "" {
140		o, err := os.Create(out)
141		if err != nil {
142			log.Fatalf("Error creating output file %q:  %v", out, err)
143		}
144		defer o.Close()
145
146		if err := manifestutils.WriteManifest(o, bytes.NewReader(b), elems); err != nil {
147			log.Fatalf("Error setting fields: %v", err)
148		}
149
150	}
151
152	// Patch the splits
153	b = []byte(stubManifest)
154	for _, s := range split {
155		pts := strings.Split(s, ":")
156		if len(pts) != 2 {
157			log.Fatalf("Failed to parse split %s", s)
158		}
159		elems[manifestutils.ElemManifest][manifestutils.AttrSplit] = xml.Attr{
160			Name: xml.Name{Local: manifestutils.AttrSplit}, Value: pts[0]}
161
162		o, err := os.Create(pts[1])
163		if err != nil {
164			log.Fatalf("Error creating output file %q:  %v", pts[1], err)
165		}
166
167		if err := manifestutils.WriteManifest(o, bytes.NewReader(b), elems); err != nil {
168			log.Fatalf("Error setting fields: %v", err)
169		}
170	}
171}
172