From 938ad5ef73f4c8991d268892a07783a67a254e9b Mon Sep 17 00:00:00 2001
From: Tor Norbye <tnorbye@google.com>
Date: Fri, 15 Feb 2019 00:50:27 +0000
Subject: [PATCH] 72967236: @RequiresPermission support for carrier-privileged
 APIs

Allow @RequiresPermission to use the special marker string
"carrier privileges" to indicate that the app having carrier
services will also satisfy the requirement.

Bug: 72967236
Test: Unit tests included
Change-Id: I1ae8ef8d70b20b24a2f907cd80a50011fa66625c
---
 .../com/android/tools/metalava/Constants.kt   |  1 +
 .../com/android/tools/metalava/DocAnalyzer.kt | 22 +++++++++++++++----
 .../android/tools/metalava/DocAnalyzerTest.kt | 21 ++++++++++++++++++
 3 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/src/main/java/com/android/tools/metalava/Constants.kt b/src/main/java/com/android/tools/metalava/Constants.kt
index 59ffda5..dd071a2 100644
--- a/src/main/java/com/android/tools/metalava/Constants.kt
+++ b/src/main/java/com/android/tools/metalava/Constants.kt
@@ -37,6 +37,7 @@ const val ANDROID_NONNULL = "android.annotation.NonNull"
 const val ANDROIDX_VISIBLE_FOR_TESTING = "androidx.annotation.VisibleForTesting"
 const val ANDROID_SUPPORT_VISIBLE_FOR_TESTING = "android.support.annotation.VisibleForTesting"
 const val ATTR_OTHERWISE = "otherwise"
+const val CARRIER_PRIVILEGES_MARKER = "carrier privileges"
 
 const val ENV_VAR_METALAVA_TESTS_RUNNING = "METALAVA_TESTS_RUNNING"
 const val ENV_VAR_METALAVA_DUMP_ARGV = "METALAVA_DUMP_ARGV"
diff --git a/src/main/java/com/android/tools/metalava/DocAnalyzer.kt b/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
index cd3f4d9..c255092 100644
--- a/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
+++ b/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
@@ -5,6 +5,7 @@ import com.android.sdklib.SdkVersionInfo
 import com.android.sdklib.repository.AndroidSdkHandler
 import com.android.tools.lint.LintCliClient
 import com.android.tools.lint.checks.ApiLookup
+import com.android.tools.lint.detector.api.editDistance
 import com.android.tools.lint.helpers.DefaultJavaEvaluator
 import com.android.tools.metalava.doclava1.Errors
 import com.android.tools.metalava.model.AnnotationAttributeValue
@@ -311,13 +312,26 @@ class DocAnalyzer(
                             resolved
                         else {
                             val v: Any = value.value() ?: value.toSource()
+                            if (v == CARRIER_PRIVILEGES_MARKER) {
+                                // TODO: Warn if using allOf with carrier
+                                sb.append("{@link android.telephony.TelephonyManager#hasCarrierPrivileges carrier privileges}")
+                                continue
+                            }
                             findPermissionField(codebase, v)
                         }
                         if (field == null) {
-                            reporter.report(
-                                Errors.MISSING_PERMISSION, item,
-                                "Cannot find permission field for $value required by $item (may be hidden or removed)"
-                            )
+                            val v = value.value()?.toString() ?: value.toSource()
+                            if (editDistance(CARRIER_PRIVILEGES_MARKER, v, 3) < 3) {
+                                reporter.report(
+                                    Errors.MISSING_PERMISSION, item,
+                                    "Unrecognized permission `$v`; did you mean `$CARRIER_PRIVILEGES_MARKER`?"
+                                )
+                            } else {
+                                reporter.report(
+                                    Errors.MISSING_PERMISSION, item,
+                                    "Cannot find permission field for $value required by $item (may be hidden or removed)"
+                                )
+                            }
                             sb.append(value.toSource())
                         } else {
                             if (filterReference.test(field)) {
diff --git a/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt b/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
index 13a86e4..5811331 100644
--- a/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
+++ b/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
@@ -152,6 +152,7 @@ class DocAnalyzerTest : DriverTest() {
     @Test
     fun `Document Permissions`() {
         check(
+            docStubs = true,
             sourceFiles = *arrayOf(
                 java(
                     """
@@ -180,6 +181,15 @@ class DocAnalyzerTest : DriverTest() {
                         @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) // b/73559440
                         public void test5() {
                         }
+
+                        @RequiresPermission(anyOf = {Manifest.permission.ACCESS_COARSE_LOCATION, "carrier privileges"})
+                        public void test6() {
+                        }
+
+                        // Typo in marker
+                        @RequiresPermission(anyOf = {Manifest.permission.ACCESS_COARSE_LOCATION, "carier priviliges"})
+                        public void test6() {
+                        }
                     }
                     """
                 ),
@@ -201,6 +211,7 @@ class DocAnalyzerTest : DriverTest() {
             ),
             checkCompilation = false, // needs androidx.annotations in classpath
             checkDoclava1 = false,
+            warnings = "src/test/pkg/PermissionTest.java:31: lint: Unrecognized permission `carier priviliges`; did you mean `carrier privileges`? [MissingPermission]",
             stubs = arrayOf(
                 """
                 package test.pkg;
@@ -230,6 +241,16 @@ class DocAnalyzerTest : DriverTest() {
                 public void test4() { throw new RuntimeException("Stub!"); }
                 @androidx.annotation.RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true)
                 public void test5() { throw new RuntimeException("Stub!"); }
+                /**
+                 * Requires {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or {@link android.telephony.TelephonyManager#hasCarrierPrivileges carrier privileges}
+                 */
+                @androidx.annotation.RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, "carrier privileges"})
+                public void test6() { throw new RuntimeException("Stub!"); }
+                /**
+                 * Requires {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or "carier priviliges"
+                 */
+                @androidx.annotation.RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, "carier priviliges"})
+                public void test6() { throw new RuntimeException("Stub!"); }
                 }
                 """
             )
-- 
GitLab