generate_android_manifest.py (4563B)
1 #!/usr/bin/env python3 2 # 3 # Copyright 2015 The Chromium Authors 4 # Use of this source code is governed by a BSD-style license that can be 5 # found in the LICENSE file. 6 """Creates an AndroidManifest.xml for an incremental APK. 7 8 Given the manifest file for the real APK, generates an AndroidManifest.xml with 9 the application class changed to IncrementalApplication. 10 """ 11 12 import argparse 13 import os 14 import sys 15 from xml.etree import ElementTree 16 17 sys.path.append(os.path.join(os.path.dirname(__file__), os.path.pardir, 'gyp')) 18 from util import build_utils 19 from util import manifest_utils 20 import action_helpers # build_utils adds //build to sys.path. 21 22 _DEFAULT_APPLICATION_CLASS = 'android.app.Application' 23 _INCREMENTAL_APP_NAME = 'org.chromium.incrementalinstall.BootstrapApplication' 24 _INCREMENTAL_APP_COMPONENT_FACTORY = ( 25 'org.chromium.incrementalinstall.BootstrapAppComponentFactory') 26 _META_DATA_APP_NAME = 'incremental-install-application' 27 _META_DATA_APP_COMPONENT_FACTORY = 'incremental-install-app-component-factory' 28 _META_DATA_INSTRUMENTATION_NAMES = [ 29 'incremental-install-instrumentation-0', 30 'incremental-install-instrumentation-1', 31 ] 32 _INCREMENTAL_INSTRUMENTATION_CLASSES = [ 33 'android.app.Instrumentation', 34 'org.chromium.incrementalinstall.SecondInstrumentation', 35 ] 36 37 38 def _AddNamespace(name): 39 """Adds the android namespace prefix to the given identifier.""" 40 return '{%s}%s' % (manifest_utils.ANDROID_NAMESPACE, name) 41 42 43 def _ParseArgs(args): 44 parser = argparse.ArgumentParser() 45 parser.add_argument('--src-manifest', 46 required=True, 47 help='The main manifest of the app.') 48 parser.add_argument('--dst-manifest', 49 required=True, 50 help='The output modified manifest.') 51 parser.add_argument('--disable-isolated-processes', 52 help='Changes all android:isolatedProcess to false. ' 53 'This is required on Android M+', 54 action='store_true') 55 56 ret = parser.parse_args(build_utils.ExpandFileArgs(args)) 57 return ret 58 59 60 def _CreateMetaData(parent, name, value): 61 meta_data_node = ElementTree.SubElement(parent, 'meta-data') 62 meta_data_node.set(_AddNamespace('name'), name) 63 meta_data_node.set(_AddNamespace('value'), value) 64 65 66 def _ProcessManifest(path, disable_isolated_processes): 67 doc, _, app_node = manifest_utils.ParseManifest(path) 68 69 # Pylint for some reason things app_node is an int. 70 # pylint: disable=no-member 71 real_app_class = app_node.get(_AddNamespace('name'), 72 _DEFAULT_APPLICATION_CLASS) 73 app_node.set(_AddNamespace('name'), _INCREMENTAL_APP_NAME) 74 # pylint: enable=no-member 75 _CreateMetaData(app_node, _META_DATA_APP_NAME, real_app_class) 76 77 real_acf = app_node.get(_AddNamespace('appComponentFactory')) 78 if real_acf: 79 app_node.set(_AddNamespace('appComponentFactory'), 80 _INCREMENTAL_APP_COMPONENT_FACTORY) 81 _CreateMetaData(app_node, _META_DATA_APP_COMPONENT_FACTORY, real_acf) 82 83 # Seems to be a bug in ElementTree, as doc.find() doesn't work here. 84 instrumentation_nodes = doc.findall('instrumentation') 85 assert len(instrumentation_nodes) <= 2, ( 86 'Need to update incremental install to support >2 <instrumentation> tags') 87 for i, instrumentation_node in enumerate(instrumentation_nodes): 88 real_instrumentation_class = instrumentation_node.get(_AddNamespace('name')) 89 instrumentation_node.set(_AddNamespace('name'), 90 _INCREMENTAL_INSTRUMENTATION_CLASSES[i]) 91 _CreateMetaData(app_node, _META_DATA_INSTRUMENTATION_NAMES[i], 92 real_instrumentation_class) 93 94 ret = ElementTree.tostring(doc.getroot(), encoding='UTF-8') 95 # Disable check for page-aligned native libraries. 96 ret = ret.replace(b'extractNativeLibs="false"', b'extractNativeLibs="true"') 97 if disable_isolated_processes: 98 ret = ret.replace(b'isolatedProcess="true"', b'isolatedProcess="false"') 99 # externalService only matters for isolatedProcess="true". See: 100 # https://developer.android.com/reference/android/R.attr#externalService 101 ret = ret.replace(b'externalService="true"', b'externalService="false"') 102 return ret 103 104 105 def main(raw_args): 106 options = _ParseArgs(raw_args) 107 108 new_manifest_data = _ProcessManifest(options.src_manifest, 109 options.disable_isolated_processes) 110 with action_helpers.atomic_output(options.dst_manifest) as out_manifest: 111 out_manifest.write(new_manifest_data) 112 113 114 if __name__ == '__main__': 115 main(sys.argv[1:])