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