Merge "Add team members to SystemUI OWNERS." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 9f48bf4..3fea8fc 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -18,8 +18,10 @@
 
     // Add java_aconfig_libraries to here to add them to the core framework
     srcs: [
+        ":android.security.flags-aconfig-java{.generated_srcjars}",
         ":com.android.hardware.camera2-aconfig-java{.generated_srcjars}",
         ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
+        ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
         ":com.android.text.flags-aconfig-java{.generated_srcjars}",
     ],
     // Add aconfig-annotations-lib as a dependency for the optimization
@@ -60,6 +62,19 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// Input
+aconfig_declarations {
+    name: "com.android.hardware.input.input-aconfig",
+    package: "com.android.hardware.input",
+    srcs: ["core/java/android/hardware/input/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "com.android.hardware.input-aconfig-java",
+    aconfig_declarations: "com.android.hardware.input.input-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Text
 aconfig_declarations {
     name: "com.android.text.flags-aconfig",
@@ -72,3 +87,24 @@
     aconfig_declarations: "com.android.text.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// Security
+aconfig_declarations {
+    name: "android.security.flags-aconfig",
+    package: "android.security",
+    srcs: ["core/java/android/security/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.security.flags-aconfig-java",
+    aconfig_declarations: "android.security.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+java_aconfig_library {
+    name: "android.security.flags-aconfig-java-host",
+    aconfig_declarations: "android.security.flags-aconfig",
+    host_supported: true,
+    test: true,
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/cmds/idmap2/libidmap2/CommandLineOptions.cpp b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
index 8129d99..b1edc18 100644
--- a/cmds/idmap2/libidmap2/CommandLineOptions.cpp
+++ b/cmds/idmap2/libidmap2/CommandLineOptions.cpp
@@ -20,6 +20,7 @@
 #include <cassert>
 #include <iomanip>
 #include <iostream>
+#include <iterator>
 #include <memory>
 #include <set>
 #include <sstream>
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
new file mode 100644
index 0000000..ebfe66f5
--- /dev/null
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -0,0 +1,19 @@
+package: "com.android.hardware.input"
+
+# Project link: https://gantry.corp.google.com/projects/android_platform_input_native/changes
+
+flag {
+    namespace: "input_native"
+    name: "keyboard_layout_preview_flag"
+    description: "Controls whether a preview will be shown in Settings when selecting a physical keyboard layout"
+    bug: "293579375"
+}
+
+
+flag {
+    namespace: "input_native"
+    name: "keyboard_a11y_sticky_keys_flag"
+    description: "Controls if the sticky keys accessibility feature for physical keyboard is available to the user"
+    bug: "294546335"
+}
+
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
new file mode 100644
index 0000000..b27dac2
--- /dev/null
+++ b/core/java/android/security/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.security"
+
+flag {
+    name: "fsverity_api"
+    namespace: "hardware_backed_security"
+    description: "Feature flag for fs-verity API"
+    bug: "285185747"
+}
diff --git a/core/tests/coretests/src/android/hardware/input/InputFlagsTest.java b/core/tests/coretests/src/android/hardware/input/InputFlagsTest.java
new file mode 100644
index 0000000..5aeab42
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/InputFlagsTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
+import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link com.android.hardware.input.Flags}
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:InputFlagsTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class InputFlagsTest {
+
+    /**
+     * Test that the flags work
+     */
+    @Test
+    public void testFlags() {
+        // No crash when accessing the flag.
+        keyboardLayoutPreviewFlag();
+        keyboardA11yStickyKeysFlag();
+    }
+}
+
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 86eb36c93..23cb91d 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -49,6 +49,7 @@
 import android.util.Slog;
 
 import com.android.internal.infra.AndroidFuture;
+import com.android.server.utils.Slogf;
 
 import java.util.Collections;
 import java.util.List;
@@ -318,8 +319,8 @@
                 int flags,
                 AndroidFuture resultCallback) {
             if (DEBUG) {
-                Slog.d(TAG, "enableTrust(" + message + ", durationMs = " + durationMs
-                        + ", flags = " + flags + ")");
+                Slogf.d(TAG, "grantTrust(message=\"%s\", durationMs=%d, flags=0x%x)",
+                        message, durationMs, flags);
             }
 
             Message msg = mHandler.obtainMessage(
@@ -336,30 +337,32 @@
 
         @Override
         public void lockUser() {
+            if (DEBUG) Slog.d(TAG, "lockUser()");
             mHandler.sendEmptyMessage(MSG_LOCK_USER);
         }
 
         @Override
         public void setManagingTrust(boolean managingTrust) {
-            if (DEBUG) Slog.d(TAG, "managingTrust()");
+            if (DEBUG) Slogf.d(TAG, "setManagingTrust(%s)", managingTrust);
             mHandler.obtainMessage(MSG_MANAGING_TRUST, managingTrust ? 1 : 0, 0).sendToTarget();
         }
 
         @Override
         public void onConfigureCompleted(boolean result, IBinder token) {
-            if (DEBUG) Slog.d(TAG, "onSetTrustAgentFeaturesEnabledCompleted(result=" + result);
+            if (DEBUG) Slogf.d(TAG, "onConfigureCompleted(result=%s)", result);
             mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_COMPLETED,
                     result ? 1 : 0, 0, token).sendToTarget();
         }
 
         @Override
         public void addEscrowToken(byte[] token, int userId) {
+            // 'token' is secret; never log it.
+            if (DEBUG) Slogf.d(TAG, "addEscrowToken(userId=%d)", userId);
+
             if (mContext.getResources()
                     .getBoolean(com.android.internal.R.bool.config_allowEscrowTokenForTrustAgent)) {
-                throw  new SecurityException("Escrow token API is not allowed.");
+                throw new SecurityException("Escrow token API is not allowed.");
             }
-
-            if (DEBUG) Slog.d(TAG, "adding escrow token for user " + userId);
             Message msg = mHandler.obtainMessage(MSG_ADD_ESCROW_TOKEN);
             msg.getData().putByteArray(DATA_ESCROW_TOKEN, token);
             msg.getData().putInt(DATA_USER_ID, userId);
@@ -368,12 +371,12 @@
 
         @Override
         public void isEscrowTokenActive(long handle, int userId) {
+            if (DEBUG) Slogf.d(TAG, "isEscrowTokenActive(handle=%016x, userId=%d)", handle, userId);
+
             if (mContext.getResources()
                     .getBoolean(com.android.internal.R.bool.config_allowEscrowTokenForTrustAgent)) {
                 throw new SecurityException("Escrow token API is not allowed.");
             }
-
-            if (DEBUG) Slog.d(TAG, "checking the state of escrow token on user " + userId);
             Message msg = mHandler.obtainMessage(MSG_ESCROW_TOKEN_STATE);
             msg.getData().putLong(DATA_HANDLE, handle);
             msg.getData().putInt(DATA_USER_ID, userId);
@@ -382,12 +385,12 @@
 
         @Override
         public void removeEscrowToken(long handle, int userId) {
+            if (DEBUG) Slogf.d(TAG, "removeEscrowToken(handle=%016x, userId=%d)", handle, userId);
+
             if (mContext.getResources()
                     .getBoolean(com.android.internal.R.bool.config_allowEscrowTokenForTrustAgent)) {
                 throw new SecurityException("Escrow token API is not allowed.");
             }
-
-            if (DEBUG) Slog.d(TAG, "removing escrow token on user " + userId);
             Message msg = mHandler.obtainMessage(MSG_REMOVE_ESCROW_TOKEN);
             msg.getData().putLong(DATA_HANDLE, handle);
             msg.getData().putInt(DATA_USER_ID, userId);
@@ -396,12 +399,13 @@
 
         @Override
         public void unlockUserWithToken(long handle, byte[] token, int userId) {
+            // 'token' is secret; never log it.
+            if (DEBUG) Slogf.d(TAG, "unlockUserWithToken(handle=%016x, userId=%d)", handle, userId);
+
             if (mContext.getResources()
                     .getBoolean(com.android.internal.R.bool.config_allowEscrowTokenForTrustAgent)) {
                 throw new SecurityException("Escrow token API is not allowed.");
             }
-
-            if (DEBUG) Slog.d(TAG, "unlocking user " + userId);
             Message msg = mHandler.obtainMessage(MSG_UNLOCK_USER);
             msg.getData().putInt(DATA_USER_ID, userId);
             msg.getData().putLong(DATA_HANDLE, handle);
@@ -411,7 +415,7 @@
 
         @Override
         public void showKeyguardErrorMessage(CharSequence message) {
-            if (DEBUG) Slog.d(TAG, "Showing keyguard error message: " + message);
+            if (DEBUG) Slogf.d(TAG, "showKeyguardErrorMessage(\"%s\")", message);
             Message msg = mHandler.obtainMessage(MSG_SHOW_KEYGUARD_ERROR_MESSAGE);
             msg.getData().putCharSequence(DATA_MESSAGE, message);
             msg.sendToTarget();
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 9828b97..f93949b 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -116,9 +116,7 @@
 
 class ManifestExtractor {
  public:
-
-  explicit ManifestExtractor(LoadedApk* apk, DumpManifestOptions& options)
-      : apk_(apk), options_(options) { }
+  explicit ManifestExtractor(LoadedApk* apk, DumpManifestOptions& options);
 
   class Element {
    public:
@@ -387,7 +385,7 @@
   DumpManifestOptions& options_;
 
  private:
-  std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_ = util::make_unique<CommonFeatureGroup>();
+  std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_;
   std::map<std::string, ConfigDescription> locales_;
   std::map<uint16_t, ConfigDescription> densities_;
   std::vector<Element*> parent_stack_;
@@ -1970,6 +1968,12 @@
   }
 }
 
+// Define this constructor after the CommonFeatureGroup class definition to avoid errors with using
+// std::unique_ptr on an incomplete type.
+ManifestExtractor::ManifestExtractor(LoadedApk* apk, DumpManifestOptions& options)
+    : apk_(apk), options_(options), commonFeatureGroup_(util::make_unique<CommonFeatureGroup>()) {
+}
+
 bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
   // Load the manifest
   std::unique_ptr<xml::XmlResource> doc = apk_->LoadXml("AndroidManifest.xml", diag);
diff --git a/tools/lint/common/Android.bp b/tools/lint/common/Android.bp
index 898f88b..8bfbfe5 100644
--- a/tools/lint/common/Android.bp
+++ b/tools/lint/common/Android.bp
@@ -27,3 +27,30 @@
     libs: ["lint_api"],
     kotlincflags: ["-Xjvm-default=all"],
 }
+
+java_defaults {
+    name: "AndroidLintCheckerTestDefaults",
+    srcs: ["checks/src/test/java/**/*.kt"],
+    static_libs: [
+        "junit",
+        "lint",
+        "lint_tests",
+    ],
+    test_options: {
+        unit_test: true,
+        tradefed_options: [
+            {
+                // lint bundles in some classes that were built with older versions
+                // of libraries, and no longer load. Since tradefed tries to load
+                // all classes in the jar to look for tests, it crashes loading them.
+                // Exclude these classes from tradefed's search.
+                name: "exclude-paths",
+                value: "org/apache",
+            },
+            {
+                name: "exclude-paths",
+                value: "META-INF",
+            },
+        ],
+    },
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt
similarity index 100%
rename from tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt
rename to tools/lint/common/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
similarity index 100%
rename from tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
rename to tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
similarity index 100%
rename from tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
rename to tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
index cd4d778d..acc0ad0 100644
--- a/tools/lint/fix/soong_lint_fix.py
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -29,6 +29,39 @@
 PATH_SUFFIX = "android_common/lint"
 FIX_ZIP = "suggested-fixes.zip"
 
+
+class SoongModule:
+    """A Soong module to lint.
+
+    The constructor takes the name of the module (for example,
+    "framework-minus-apex"). find() must be called to extract the intermediate
+    module path from Soong's module-info.json
+    """
+    def __init__(self, name):
+        self._name = name
+
+    def find(self, module_info):
+        """Finds the module in the loaded module_info.json."""
+        if self._name not in module_info:
+            raise Exception(f"Module {self._name} not found!")
+
+        partial_path = module_info[self._name]["path"][0]
+        print(f"Found module {partial_path}/{self._name}.")
+        self._path = f"{PATH_PREFIX}/{partial_path}/{self._name}/{PATH_SUFFIX}"
+
+    @property
+    def name(self):
+        return self._name
+
+    @property
+    def lint_report(self):
+        return f"{self._path}/lint-report.txt"
+
+    @property
+    def suggested_fixes(self):
+        return f"{self._path}/{FIX_ZIP}"
+
+
 class SoongLintFix:
     """
     This class creates a command line tool that will
@@ -53,16 +86,14 @@
         self._parser = _setup_parser()
         self._args = None
         self._kwargs = None
-        self._path = None
-        self._target = None
+        self._modules = []
 
-
-    def run(self, additional_setup=None, custom_fix=None):
+    def run(self):
         """
         Run the script
         """
         self._setup()
-        self._find_module()
+        self._find_modules()
         self._lint()
 
         if not self._args.no_fix:
@@ -87,8 +118,6 @@
 
         os.chdir(ANDROID_BUILD_TOP)
 
-
-    def _find_module(self):
         print("Refreshing soong modules...")
         try:
             os.mkdir(ANDROID_PRODUCT_OUT)
@@ -97,48 +126,47 @@
         subprocess.call(f"{SOONG_UI} --make-mode {PRODUCT_OUT}/module-info.json", **self._kwargs)
         print("done.")
 
+
+    def _find_modules(self):
         with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f:
             module_info = json.load(f)
 
-        if self._args.module not in module_info:
-            sys.exit(f"Module {self._args.module} not found!")
-
-        module_path = module_info[self._args.module]["path"][0]
-        print(f"Found module {module_path}/{self._args.module}.")
-
-        self._path = f"{PATH_PREFIX}/{module_path}/{self._args.module}/{PATH_SUFFIX}"
-        self._target = f"{self._path}/lint-report.txt"
-
+        for module_name in self._args.modules:
+            module = SoongModule(module_name)
+            module.find(module_info)
+            self._modules.append(module)
 
     def _lint(self):
         print("Cleaning up any old lint results...")
-        try:
-            os.remove(f"{self._target}")
-            os.remove(f"{self._path}/{FIX_ZIP}")
-        except FileNotFoundError:
-            pass
+        for module in self._modules:
+            try:
+                os.remove(f"{module.lint_report}")
+                os.remove(f"{module.suggested_fixes}")
+            except FileNotFoundError:
+                pass
         print("done.")
 
-        print(f"Generating {self._target}")
-        subprocess.call(f"{SOONG_UI} --make-mode {self._target}", **self._kwargs)
+        target = " ".join([ module.lint_report for module in self._modules ])
+        print(f"Generating {target}")
+        subprocess.call(f"{SOONG_UI} --make-mode {target}", **self._kwargs)
         print("done.")
 
-
     def _fix(self):
-        print("Copying suggested fixes to the tree...")
-        with zipfile.ZipFile(f"{self._path}/{FIX_ZIP}") as zip:
-            for name in zip.namelist():
-                if name.startswith("out") or not name.endswith(".java"):
-                    continue
-                with zip.open(name) as src, open(f"{ANDROID_BUILD_TOP}/{name}", "wb") as dst:
-                    shutil.copyfileobj(src, dst)
+        for module in self._modules:
+            print(f"Copying suggested fixes for {module.name} to the tree...")
+            with zipfile.ZipFile(f"{module.suggested_fixes}") as zip:
+                for name in zip.namelist():
+                    if name.startswith("out") or not name.endswith(".java"):
+                        continue
+                    with zip.open(name) as src, open(f"{ANDROID_BUILD_TOP}/{name}", "wb") as dst:
+                        shutil.copyfileobj(src, dst)
             print("done.")
 
-
     def _print(self):
-        print("### lint-report.txt ###", end="\n\n")
-        with open(self._target, "r") as f:
-            print(f.read())
+        for module in self._modules:
+            print(f"### lint-report.txt {module.name} ###", end="\n\n")
+            with open(module.lint_report, "r") as f:
+                print(f.read())
 
 
 def _setup_parser():
@@ -151,7 +179,8 @@
         **Gotcha**: You must have run `source build/envsetup.sh` and `lunch` first.
         """, formatter_class=argparse.RawTextHelpFormatter)
 
-    parser.add_argument('module',
+    parser.add_argument('modules',
+                        nargs='+',
                         help='The soong build module to run '
                              '(e.g. framework-minus-apex or services.core.unboosted)')
 
@@ -170,4 +199,4 @@
     return parser
 
 if __name__ == "__main__":
-    SoongLintFix().run()
\ No newline at end of file
+    SoongLintFix().run()
diff --git a/tools/lint/framework/Android.bp b/tools/lint/framework/Android.bp
index 30a6daa..5acdf43 100644
--- a/tools/lint/framework/Android.bp
+++ b/tools/lint/framework/Android.bp
@@ -37,28 +37,9 @@
 
 java_test_host {
     name: "AndroidFrameworkLintCheckerTest",
+    defaults: ["AndroidLintCheckerTestDefaults"],
     srcs: ["checks/src/test/java/**/*.kt"],
     static_libs: [
         "AndroidFrameworkLintChecker",
-        "junit",
-        "lint",
-        "lint_tests",
     ],
-    test_options: {
-        unit_test: true,
-        tradefed_options: [
-            {
-                // lint bundles in some classes that were built with older versions
-                // of libraries, and no longer load. Since tradefed tries to load
-                // all classes in the jar to look for tests, it crashes loading them.
-                // Exclude these classes from tradefed's search.
-                name: "exclude-paths",
-                value: "org/apache",
-            },
-            {
-                name: "exclude-paths",
-                value: "META-INF",
-            },
-        ],
-    },
 }
diff --git a/tools/lint/global/Android.bp b/tools/lint/global/Android.bp
index bedb7bd..3e74171 100644
--- a/tools/lint/global/Android.bp
+++ b/tools/lint/global/Android.bp
@@ -38,28 +38,9 @@
 
 java_test_host {
     name: "AndroidGlobalLintCheckerTest",
+    defaults: ["AndroidLintCheckerTestDefaults"],
     srcs: ["checks/src/test/java/**/*.kt"],
     static_libs: [
         "AndroidGlobalLintChecker",
-        "junit",
-        "lint",
-        "lint_tests",
     ],
-    test_options: {
-        unit_test: true,
-        tradefed_options: [
-            {
-                // lint bundles in some classes that were built with older versions
-                // of libraries, and no longer load. Since tradefed tries to load
-                // all classes in the jar to look for tests, it crashes loading them.
-                // Exclude these classes from tradefed's search.
-                name: "exclude-paths",
-                value: "org/apache",
-            },
-            {
-                name: "exclude-paths",
-                value: "META-INF",
-            },
-        ],
-    },
 }
diff --git a/tools/lint/utils/Android.bp b/tools/lint/utils/Android.bp
new file mode 100644
index 0000000..75e8d68
--- /dev/null
+++ b/tools/lint/utils/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library_host {
+    name: "AndroidUtilsLintChecker",
+    srcs: ["checks/src/main/java/**/*.kt"],
+    plugins: ["auto_service_plugin"],
+    libs: [
+        "auto_service_annotations",
+        "lint_api",
+    ],
+    static_libs: [
+        "AndroidCommonLint",
+    ],
+    kotlincflags: ["-Xjvm-default=all"],
+}
+
+java_test_host {
+    name: "AndroidUtilsLintCheckerTest",
+    defaults: ["AndroidLintCheckerTestDefaults"],
+    srcs: ["checks/src/test/java/**/*.kt"],
+    static_libs: [
+        "AndroidUtilsLintChecker",
+    ],
+}
diff --git a/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt b/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt
new file mode 100644
index 0000000..fa61c42
--- /dev/null
+++ b/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.android.lint.aidl.AnnotatedAidlCounter
+import com.google.auto.service.AutoService
+
+@AutoService(IssueRegistry::class)
+@Suppress("UnstableApiUsage")
+class AndroidUtilsIssueRegistry : IssueRegistry() {
+    override val issues = listOf(
+        AnnotatedAidlCounter.ISSUE_ANNOTATED_AIDL_COUNTER,
+    )
+
+    override val api: Int
+        get() = CURRENT_API
+
+    override val minApi: Int
+        get() = 8
+
+    override val vendor: Vendor = Vendor(
+        vendorName = "Android",
+        feedbackUrl = "http://b/issues/new?component=315013",
+        contact = "tweek@google.com"
+    )
+}
diff --git a/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/AnnotatedAidlCounter.kt b/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/AnnotatedAidlCounter.kt
new file mode 100644
index 0000000..f0ec3f4
--- /dev/null
+++ b/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/AnnotatedAidlCounter.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.aidl
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Context
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UMethod
+
+import java.util.TreeMap
+
+/**
+ *  Count the number of AIDL interfaces. Reports the number of annotated and
+ *  non-annotated methods.
+ */
+@Suppress("UnstableApiUsage")
+class AnnotatedAidlCounter : AidlImplementationDetector() {
+
+    private data class Stat(
+        var unannotated: Int = 0,
+        var enforced: Int = 0,
+        var notRequired: Int = 0,
+    )
+
+    private var packagesStats: TreeMap<String, Stat> = TreeMap<String, Stat>()
+
+    override fun visitAidlMethod(
+            context: JavaContext,
+            node: UMethod,
+            interfaceName: String,
+            body: UBlockExpression
+    ) {
+        val packageName = context.uastFile?.packageName ?: "<unknown>"
+        var packageStat = packagesStats.getOrDefault(packageName, Stat())
+        when {
+            node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION) -> packageStat.enforced += 1
+            node.hasAnnotation(ANNOTATION_REQUIRES_NO_PERMISSION) -> packageStat.notRequired += 1
+            else -> packageStat.unannotated += 1
+        }
+        packagesStats.put(packageName, packageStat)
+        // context.driver.client.log(null, "%s.%s#%s".format(packageName, interfaceName, node.name))
+    }
+
+    override fun afterCheckRootProject(context: Context) {
+        var total = Stat()
+        for ((packageName, stat) in packagesStats) {
+            context.client.log(null, "package $packageName => $stat")
+            total.unannotated += stat.unannotated
+            total.enforced += stat.enforced
+            total.notRequired += stat.notRequired
+        }
+        val location = Location.create(context.project.dir)
+        context.report(
+            ISSUE_ANNOTATED_AIDL_COUNTER,
+            location,
+            "module ${context.project.name} => $total"
+        )
+    }
+
+    companion object {
+
+        @JvmField
+        val ISSUE_ANNOTATED_AIDL_COUNTER = Issue.create(
+                id = "AnnotatedAidlCounter",
+                briefDescription = "Statistics on the number of annotated AIDL methods.",
+                explanation = "",
+                category = Category.SECURITY,
+                priority = 5,
+                severity = Severity.INFORMATIONAL,
+                implementation = Implementation(
+                        AnnotatedAidlCounter::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                ),
+        )
+    }
+}
diff --git a/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/AnnotatedAidlCounterTest.kt b/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/AnnotatedAidlCounterTest.kt
new file mode 100644
index 0000000..692b7da
--- /dev/null
+++ b/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/AnnotatedAidlCounterTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.aidl
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class AnnotatedAidlCounterTest : LintDetectorTest() {
+    override fun getDetector(): Detector = AnnotatedAidlCounter()
+
+    override fun getIssues(): List<Issue> = listOf(
+        AnnotatedAidlCounter.ISSUE_ANNOTATED_AIDL_COUNTER,
+    )
+
+    override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+    /** No issue scenario */
+
+    fun testDoesNotDetectIssuesCorrectAnnotationOnMethod() {
+        lint().files(java(
+            """
+            package test.pkg;
+            import android.annotation.EnforcePermission;
+            public class TestClass2 extends IFooMethod.Stub {
+                @Override
+                @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+                public void testMethod() {}
+            }
+            """).indented(),
+                *stubs
+        )
+        .run()
+        .expect("""
+        app: Information: module app => Stat(unannotated=0, enforced=1, notRequired=0) [AnnotatedAidlCounter]
+        0 errors, 0 warnings
+        """)
+    }
+
+    // A service with permission annotation on the method.
+    private val interfaceIFooMethodStub: TestFile = java(
+        """
+        public interface IFooMethod extends android.os.IInterface {
+         public static abstract class Stub extends android.os.Binder implements IFooMethod {}
+          @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+          public void testMethod();
+        }
+        """
+    ).indented()
+
+    // A service without any permission annotation.
+    private val interfaceIBarStub: TestFile = java(
+        """
+        public interface IBar extends android.os.IInterface {
+         public static abstract class Stub extends android.os.Binder implements IBar {
+            @Override
+            public void testMethod() {}
+          }
+          public void testMethod();
+        }
+        """
+    ).indented()
+
+    private val manifestPermissionStub: TestFile = java(
+        """
+        package android.Manifest;
+        class permission {
+          public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+        }
+        """
+    ).indented()
+
+    private val enforcePermissionAnnotationStub: TestFile = java(
+        """
+        package android.annotation;
+        public @interface EnforcePermission {}
+        """
+    ).indented()
+
+    private val stubs = arrayOf(interfaceIFooMethodStub, interfaceIBarStub,
+            manifestPermissionStub, enforcePermissionAnnotationStub)
+}