Merge xmlns decls in old manifest merger to help transition

The old manifest merger doesn't merge xmlns decls.
That could be a problem if a library has xmlns:tools
but the binary doesn't (and we don't strip tools:).
Library manifests may end up with more xmlns:tools
annotations while transitioning from the old manifest
merger to the new. It would be a problem if the same
manifest is used by both the old and new merger.
An alternative may be to strip the tools annotations
after merging w/ this old merger... other options?

--
MOS_MIGRATED_REVID=131332171
diff --git a/tools/android/merge_manifests.py b/tools/android/merge_manifests.py
index 44881e2..e5b093a 100644
--- a/tools/android/merge_manifests.py
+++ b/tools/android/merge_manifests.py
@@ -332,6 +332,37 @@
     if applications:
       manifest.appendChild(applications[0])
 
+  def _MergeTopLevelNamespaces(self, mergee_dom):
+    """Merge the xmlns declarations in the top-level manifest nodes.
+
+    This does not handle and ignores xmlns declarations in child nodes.
+    Overall, this manifest merger does not try to interpret any attributes that
+    use the android "tools" namespace either. E.g., tools:node="remove".
+
+    This functionality is just to help migrate from this manifest merger,
+    to a new manifest merger that does handle tools annotations (a manifest
+    may be sent to both mergers during migration).
+
+    Args:
+      mergee_dom: The dom of the mergee manifest.
+    Raises:
+      MalformedManifestException: if the mergee and merger manifests contain
+      xmlns declarations that don't agree.
+    """
+    manifest = self._merger_dom.getElementsByTagName('manifest')[0]
+    mergee_manifest = mergee_dom.getElementsByTagName('manifest')[0]
+    for i in range(mergee_manifest.attributes.length):
+      attr = mergee_manifest.attributes.item(i)
+      if attr.prefix and attr.prefix == 'xmlns':
+        if manifest.hasAttribute(attr.name):
+          main_attr_value = manifest.getAttribute(attr.name)
+          if main_attr_value != attr.value:
+            raise MalformedManifestException(
+                'different values for namespace %s ("%s" vs "%s")' % (
+                    attr.name, main_attr_value, attr.value))
+        else:
+          manifest.setAttribute(attr.name, attr.value)
+
   def Merge(self):
     """Takes two manifests, and merges them together to produce a third."""
     self._RemoveFromMerger()
@@ -342,6 +373,7 @@
       self._ReplaceArgumentPlaceholders(mergee_dom)
       self._ExpandPackageName(mergee_dom)
       self._ApplyExcludePermissions(mergee_dom)
+      self._MergeTopLevelNamespaces(mergee_dom)
 
       for destination, values in sorted(
           self._NODES_TO_COPY_FROM_MERGEE.iteritems()):
diff --git a/tools/android/merge_manifests_test.py b/tools/android/merge_manifests_test.py
index 55fef1c..62d0474 100644
--- a/tools/android/merge_manifests_test.py
+++ b/tools/android/merge_manifests_test.py
@@ -435,6 +435,84 @@
 </manifest>
 """
 
+MANIFEST_WITHOUT_EXTRA_NAMESPACE = """
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.test"
+    android:versionCode="1"
+    android:versionName="1.0.0.0">
+  <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
+  <application>
+    <activity android:name=".ui.home.HomeActivity"
+        android:label="@string/app_name" >
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN"/>
+        <category android:name="android.intent.category.LAUNCHER"/>
+      </intent-filter>
+    </activity>
+  </application>
+</manifest>
+"""
+
+MANIFEST_WITH_EXTRA_NAMESPACE = """
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.google.android.library">
+  <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
+  <application>
+    <service android:name=".nfcevent.NfcEventService"
+             android:exported="true"
+             tools:replace="exported" />
+  </application>
+</manifest>
+"""
+
+MERGED_MANIFEST_WITH_EXTRA_NAMESPACE = """
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.google.android.test"
+    android:versionCode="1"
+    android:versionName="1.0.0.0">
+  <!-- *** WARNING *** DO NOT EDIT! THIS IS GENERATED MANIFEST BY MERGE_MANIFEST TOOL.
+  Merger manifest:
+    MANIFEST_WITHOUT_EXTRA_NAMESPACE
+  Mergee manifests:
+    MANIFEST_WITH_EXTRA_NAMESPACE
+   -->
+  <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
+  <application>
+    <activity android:name="com.google.android.test.ui.home.HomeActivity"
+        android:label="@string/app_name" >
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN"/>
+        <category android:name="android.intent.category.LAUNCHER"/>
+      </intent-filter>
+    </activity>
+    <!-- Merged from file: MANIFEST_WITH_EXTRA_NAMESPACE -->
+    <service android:exported="true" android:name="com.google.android.library.nfcevent.NfcEventService" tools:replace="exported"/>
+  </application>
+</manifest>
+"""
+
+MANIFEST_WITH_CONFLICTING_NAMESPACE = """
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="not_tools"
+    package="com.google.android.test"
+    android:versionCode="1"
+    android:versionName="1.0.0.0">
+  <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
+  <application>
+    <activity android:name=".ui.home.HomeActivity"
+        android:label="@string/app_name" >
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN"/>
+        <category android:name="android.intent.category.LAUNCHER"/>
+      </intent-filter>
+    </activity>
+  </application>
+</manifest>
+"""
+
 
 def Reformat(string):
   """Reformat for comparison."""
@@ -520,6 +598,31 @@
         ['all'])
     merger.Merge()
 
+  def testMergeWithNamespaces(self):
+    self.maxDiff = None
+    merger = merge_manifests.MergeManifests(
+        (MANIFEST_WITHOUT_EXTRA_NAMESPACE, 'MANIFEST_WITHOUT_EXTRA_NAMESPACE'),
+        [(MANIFEST_WITH_EXTRA_NAMESPACE, 'MANIFEST_WITH_EXTRA_NAMESPACE')],
+        ['all'])
+    result = merger.Merge()
+    expected = xml.dom.minidom.parseString(
+        MERGED_MANIFEST_WITH_EXTRA_NAMESPACE).toprettyxml()
+    # Make sure the result is valid xml (not missing xmlns declarations)
+    result_reparsed = xml.dom.minidom.parseString(result).toprettyxml()
+    self.assertEquals(Reformat(expected), Reformat(result_reparsed))
+
+  def testMergeConflictingNamespaces(self):
+    self.maxDiff = None
+    merger = merge_manifests.MergeManifests(
+        (MANIFEST_WITH_CONFLICTING_NAMESPACE,
+         'MANIFEST_WITH_CONFLICTING_NAMESPACE'),
+        [(MANIFEST_WITH_EXTRA_NAMESPACE, 'MANIFEST_WITH_EXTRA_NAMESPACE')],
+        ['all'])
+    with self.assertRaisesRegexp(merge_manifests.MalformedManifestException,
+                                 'different values for namespace xmlns:tools'):
+      merger.Merge()
+
+
 if __name__ == '__main__':
   unittest.main()