Allow for setting a logging_parent for an Android App.

Unit test: go test ./... -test.v -run TestOverrideAndroidApp
Unit test: python manifest_fixer_test.py

BUG: 148198056
Change-Id: Ib5ff235d2a93e88b86aec1c0b16327ea938a094d
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 945bc18..c59732b 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -51,6 +51,9 @@
                       help='specify additional <uses-library> tag to add. android:requred is set to false')
   parser.add_argument('--uses-non-sdk-api', dest='uses_non_sdk_api', action='store_true',
                       help='manifest is for a package built against the platform')
+  parser.add_argument('--logging-parent', dest='logging_parent', default='',
+                      help=('specify logging parent as an additional <meta-data> tag. '
+                            'This value is ignored if the logging_parent meta-data tag is present.'))
   parser.add_argument('--use-embedded-dex', dest='use_embedded_dex', action='store_true',
                       help=('specify if the app wants to use embedded dex and avoid extracted,'
                             'locally compiled code. Must not conflict if already declared '
@@ -124,6 +127,52 @@
     element.setAttributeNode(target_attr)
 
 
+def add_logging_parent(doc, logging_parent_value):
+  """Add logging parent as an additional <meta-data> tag.
+
+  Args:
+    doc: The XML document. May be modified by this function.
+    logging_parent_value: A string representing the logging
+      parent value.
+  Raises:
+    RuntimeError: Invalid manifest
+  """
+  manifest = parse_manifest(doc)
+
+  logging_parent_key = 'android.content.pm.LOGGING_PARENT'
+  elems = get_children_with_tag(manifest, 'application')
+  application = elems[0] if len(elems) == 1 else None
+  if len(elems) > 1:
+    raise RuntimeError('found multiple <application> tags')
+  elif not elems:
+    application = doc.createElement('application')
+    indent = get_indent(manifest.firstChild, 1)
+    first = manifest.firstChild
+    manifest.insertBefore(doc.createTextNode(indent), first)
+    manifest.insertBefore(application, first)
+
+  indent = get_indent(application.firstChild, 2)
+
+  last = application.lastChild
+  if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
+    last = None
+
+  if not find_child_with_attribute(application, 'meta-data', android_ns,
+                                   'name', logging_parent_key):
+    ul = doc.createElement('meta-data')
+    ul.setAttributeNS(android_ns, 'android:name', logging_parent_key)
+    ul.setAttributeNS(android_ns, 'android:value', logging_parent_value)
+    application.insertBefore(doc.createTextNode(indent), last)
+    application.insertBefore(ul, last)
+    last = application.lastChild
+
+  # align the closing tag with the opening tag if it's not
+  # indented
+  if last and last.nodeType != minidom.Node.TEXT_NODE:
+    indent = get_indent(application.previousSibling, 1)
+    application.appendChild(doc.createTextNode(indent))
+
+
 def add_uses_libraries(doc, new_uses_libraries, required):
   """Add additional <uses-library> tags
 
@@ -291,6 +340,9 @@
     if args.uses_non_sdk_api:
       add_uses_non_sdk_api(doc)
 
+    if args.logging_parent:
+      add_logging_parent(doc, args.logging_parent)
+
     if args.use_embedded_dex:
       add_use_embedded_dex(doc)
 
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index ea8095e..d6e7f26 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -226,6 +226,47 @@
     self.assertEqual(output, expected)
 
 
+class AddLoggingParentTest(unittest.TestCase):
+  """Unit tests for add_logging_parent function."""
+
+  def add_logging_parent_test(self, input_manifest, logging_parent=None):
+    doc = minidom.parseString(input_manifest)
+    if logging_parent:
+      manifest_fixer.add_logging_parent(doc, logging_parent)
+    output = StringIO.StringIO()
+    manifest_fixer.write_xml(output, doc)
+    return output.getvalue()
+
+  manifest_tmpl = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+      '%s'
+      '</manifest>\n')
+
+  def uses_logging_parent(self, logging_parent=None):
+    attrs = ''
+    if logging_parent:
+      meta_text = ('<meta-data android:name="android.content.pm.LOGGING_PARENT" '
+                   'android:value="%s"/>\n') % (logging_parent)
+      attrs += '    <application>\n        %s    </application>\n' % (meta_text)
+
+    return attrs
+
+  def test_no_logging_parent(self):
+    """Tests manifest_fixer with no logging_parent."""
+    manifest_input = self.manifest_tmpl % ''
+    expected = self.manifest_tmpl % self.uses_logging_parent()
+    output = self.add_logging_parent_test(manifest_input)
+    self.assertEqual(output, expected)
+
+  def test_logging_parent(self):
+    """Tests manifest_fixer with no logging_parent."""
+    manifest_input = self.manifest_tmpl % ''
+    expected = self.manifest_tmpl % self.uses_logging_parent('FOO')
+    output = self.add_logging_parent_test(manifest_input, 'FOO')
+    self.assertEqual(output, expected)
+
+
 class AddUsesLibrariesTest(unittest.TestCase):
   """Unit tests for add_uses_libraries function."""