Propagate max_sdk_version to manifest_fixer

If max_sdk_version is included in Android.bp that value will now be
propagated to manifest_fixer.py. This value will then be used to
override any maxSdkVersion attribute set on permission or
uses-permission tags in the android manifest if maxSdkVersion="-1".

Bug: 223902327
Test: add max_sdk_version to Android.bp for test app
Test: create permission in test app manifest with maxSdkVersion="-1"
Test: run test to check maxSdkVersion=max_sdk_version
Change-Id: Ic533ef2a41b9ecc9ee68c69399026df47ee945b7
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 2004c92..c188c48 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -28,6 +28,9 @@
 	// MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
 	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+	// ReplaceMaxSdkVersionPlaceholder returns SdkSpec to replace the maxSdkVersion property of permission and
+	// uses-permission tags if it is set.
+	ReplaceMaxSdkVersionPlaceholder(ctx EarlyModuleContext) SdkSpec
 	// TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
 	TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
diff --git a/java/aar.go b/java/aar.go
index 00ff7e7..cf84309 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -678,6 +678,10 @@
 	return a.SdkVersion(ctx)
 }
 
+func (a *AARImport) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, "")
+}
+
 func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return a.SdkVersion(ctx)
 }
diff --git a/java/android_manifest.go b/java/android_manifest.go
index a297b2c..c61823d 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -136,6 +136,11 @@
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
 
+		replaceMaxSdkVersionPlaceholder, err := params.SdkContext.ReplaceMaxSdkVersionPlaceholder(ctx).EffectiveVersion(ctx)
+		if err != nil {
+			ctx.ModuleErrorf("invalid ReplaceMaxSdkVersionPlaceholder: %s", err)
+		}
+
 		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
 			minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
 			deps = append(deps, ApiFingerprintPath(ctx))
@@ -145,6 +150,7 @@
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
 		args = append(args, "--minSdkVersion ", minSdkVersion)
+		args = append(args, "--replaceMaxSdkVersionPlaceholder ", strconv.Itoa(replaceMaxSdkVersionPlaceholder.FinalOrFutureInt()))
 		args = append(args, "--raise-min-sdk-version")
 	}
 
diff --git a/java/base.go b/java/base.go
index 0900daa..a249ed9 100644
--- a/java/base.go
+++ b/java/base.go
@@ -204,6 +204,10 @@
 	// Defaults to empty string "". See sdk_version for possible values.
 	Max_sdk_version *string
 
+	// if not blank, set the maxSdkVersion properties of permission and uses-permission tags.
+	// Defaults to empty string "". See sdk_version for possible values.
+	Replace_max_sdk_version_placeholder *string
+
 	// if not blank, set the targetSdkVersion in the AndroidManifest.xml.
 	// Defaults to sdk_version if not set. See sdk_version for possible values.
 	Target_sdk_version *string
@@ -649,6 +653,11 @@
 	return android.SdkSpecFrom(ctx, maxSdkVersion)
 }
 
+func (j *Module) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	replaceMaxSdkVersionPlaceholder := proptools.StringDefault(j.deviceProperties.Replace_max_sdk_version_placeholder, "")
+	return android.SdkSpecFrom(ctx, replaceMaxSdkVersionPlaceholder)
+}
+
 func (j *Module) MinSdkVersionString() string {
 	return j.minSdkVersion.Raw
 }
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 023d619..9663922 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -256,6 +256,10 @@
 	return j.SdkVersion(ctx)
 }
 
+func (j *Javadoc) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	return j.SdkVersion(ctx)
+}
+
 func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return j.SdkVersion(ctx)
 }
diff --git a/java/java.go b/java/java.go
index 0dfb968..7d40a7e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1327,6 +1327,10 @@
 	// specified.
 	Min_sdk_version *string
 
+	// The max sdk version placeholder used to replace maxSdkVersion attributes on permission
+	// and uses-permission tags in manifest_fixer.
+	Replace_max_sdk_version_placeholder *string
+
 	Installable *bool
 
 	// If not empty, classes are restricted to the specified packages and their sub-packages.
@@ -1406,6 +1410,13 @@
 	return j.SdkVersion(ctx)
 }
 
+func (j *Import) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	if j.properties.Replace_max_sdk_version_placeholder != nil {
+		return android.SdkSpecFrom(ctx, *j.properties.Replace_max_sdk_version_placeholder)
+	}
+	return android.SdkSpecFrom(ctx, "")
+}
+
 func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return j.SdkVersion(ctx)
 }
diff --git a/java/rro.go b/java/rro.go
index be84aff..7952c2c 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -173,6 +173,10 @@
 	return r.SdkVersion(ctx)
 }
 
+func (r *RuntimeResourceOverlay) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, "")
+}
+
 func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return r.SdkVersion(ctx)
 }
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 2d3103b..2da29ee 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -39,6 +39,8 @@
   parser = argparse.ArgumentParser()
   parser.add_argument('--minSdkVersion', default='', dest='min_sdk_version',
                       help='specify minSdkVersion used by the build system')
+  parser.add_argument('--replaceMaxSdkVersionPlaceholder', default='', dest='max_sdk_version',
+                      help='specify maxSdkVersion used by the build system')
   parser.add_argument('--targetSdkVersion', default='', dest='target_sdk_version',
                       help='specify targetSdkVersion used by the build system')
   parser.add_argument('--raise-min-sdk-version', dest='raise_min_sdk_version', action='store_true',
@@ -342,6 +344,24 @@
   attr.value = 'true'
   application.setAttributeNode(attr)
 
+def set_max_sdk_version(doc, max_sdk_version):
+  """Replace the maxSdkVersion attribute value for permission and
+  uses-permission tags if the value was originally set to 'current'.
+  Used for cts test cases where the maxSdkVersion should equal to
+  Build.SDK_INT.
+
+  Args:
+    doc: The XML document.  May be modified by this function.
+    max_sdk_version: The requested maxSdkVersion attribute.
+  """
+  manifest = parse_manifest(doc)
+  for tag in ['permission', 'uses-permission']:
+    children = get_children_with_tag(manifest, tag)
+    for child in children:
+      max_attr = child.getAttributeNodeNS(android_ns, 'maxSdkVersion')
+      if max_attr and max_attr.value == 'current':
+        max_attr.value = max_sdk_version
+
 def main():
   """Program entry point."""
   try:
@@ -354,6 +374,9 @@
     if args.raise_min_sdk_version:
       raise_min_sdk_version(doc, args.min_sdk_version, args.target_sdk_version, args.library)
 
+    if args.max_sdk_version:
+      set_max_sdk_version(doc, args.max_sdk_version)
+
     if args.uses_libraries:
       add_uses_libraries(doc, args.uses_libraries, True)
 
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index 199b279..dad104a 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -571,5 +571,77 @@
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
 
+
+class SetMaxSdkVersionTest(unittest.TestCase):
+  """Unit tests for set_max_sdk_version function."""
+
+  def assert_xml_equal(self, output, expected):
+    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
+  def run_test(self, input_manifest, max_sdk_version):
+    doc = minidom.parseString(input_manifest)
+    manifest_fixer.set_max_sdk_version(doc, max_sdk_version)
+    output = io.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 permission(self, max=None):
+    if max is None:
+      return '   <permission/>'
+    return '    <permission android:maxSdkVersion="%s"/>\n' % max
+
+  def uses_permission(self, max=None):
+    if max is None:
+      return '   <uses-permission/>'
+    return '    <uses-permission android:maxSdkVersion="%s"/>\n' % max
+
+  def test_permission_no_max_sdk_version(self):
+    """Tests if permission has no maxSdkVersion attribute"""
+    manifest_input = self.manifest_tmpl % self.permission()
+    expected = self.manifest_tmpl % self.permission()
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_permission_max_sdk_version_changed(self):
+    """Tests if permission maxSdkVersion attribute is set to current"""
+    manifest_input = self.manifest_tmpl % self.permission('current')
+    expected = self.manifest_tmpl % self.permission(9000)
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_permission_max_sdk_version_not_changed(self):
+    """Tests if permission maxSdkVersion attribute is not set to current"""
+    manifest_input = self.manifest_tmpl % self.permission(30)
+    expected = self.manifest_tmpl % self.permission(30)
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_uses_permission_no_max_sdk_version(self):
+    """Tests if uses-permission has no maxSdkVersion attribute"""
+    manifest_input = self.manifest_tmpl % self.uses_permission()
+    expected = self.manifest_tmpl % self.uses_permission()
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_uses_permission_max_sdk_version_changed(self):
+    """Tests if uses-permission maxSdkVersion attribute is set to current"""
+    manifest_input = self.manifest_tmpl % self.uses_permission('current')
+    expected = self.manifest_tmpl % self.uses_permission(9000)
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
+  def test_uses_permission_max_sdk_version_not_changed(self):
+    """Tests if uses-permission maxSdkVersion attribute is not set to current"""
+    manifest_input = self.manifest_tmpl % self.uses_permission(30)
+    expected = self.manifest_tmpl % self.uses_permission(30)
+    output = self.run_test(manifest_input, '9000')
+    self.assert_xml_equal(output, expected)
+
 if __name__ == '__main__':
   unittest.main(verbosity=2)