Merge "Make java_sdk_library dependencies explicit" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index d4af940..7706de3 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -74,6 +74,7 @@
         "android.view.inputmethod.flags-aconfig-java",
         "android.webkit.flags-aconfig-java",
         "android.widget.flags-aconfig-java",
+        "art_exported_aconfig_flags_lib",
         "backstage_power_flags_lib",
         "backup_flags_lib",
         "camera_platform_flags_core_java_lib",
@@ -133,6 +134,14 @@
     libs: ["fake_device_config"],
 }
 
+// ART
+java_aconfig_library {
+    name: "art_exported_aconfig_flags_lib",
+    aconfig_declarations: "art-aconfig-flags",
+    mode: "exported",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Camera
 java_aconfig_library {
     name: "camera_platform_flags_core_java_lib",
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 65ab379..bca30b4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8842,6 +8842,7 @@
 
         try {
             ParsedPackage pp = parser2.parsePackage(apkFile, parserFlags, false);
+            pp.hideAsFinal();
 
             return PackageInfoCommonUtils.generate(pp, flagsBits, UserHandle.myUserId());
         } catch (PackageParserException e) {
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
index 516253b..44beb55 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
@@ -19,18 +19,18 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
-import android.app.compat.CompatChanges;
 import android.os.Binder;
 import android.os.UserHandle;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 
@@ -41,36 +41,40 @@
 
     private static final int USER_ID_1 = 11;
     private static final int USER_ID_2 = 12;
+    private RadioServiceUserController mUserController;
 
     @Mock
     private UserHandle mUserHandleMock;
 
+    @Rule
+    public final Expect expect = Expect.create();
+
     @Override
     protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder.spyStatic(ActivityManager.class).spyStatic(Binder.class)
-                .spyStatic(CompatChanges.class);
+        builder.spyStatic(ActivityManager.class).spyStatic(Binder.class);
     }
 
     @Before
     public void setUp() {
         doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
         doReturn(USER_ID_1).when(() -> ActivityManager.getCurrentUser());
+        mUserController = new RadioServiceUserControllerImpl();
     }
 
     @Test
     public void isCurrentOrSystemUser_forCurrentUser_returnsFalse() {
         when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
 
-        assertWithMessage("Current user")
-                .that(RadioServiceUserController.isCurrentOrSystemUser()).isTrue();
+        expect.withMessage("Current user")
+                .that(mUserController.isCurrentOrSystemUser()).isTrue();
     }
 
     @Test
     public void isCurrentOrSystemUser_forNonCurrentUser_returnsFalse() {
         when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_2);
 
-        assertWithMessage("Non-current user")
-                .that(RadioServiceUserController.isCurrentOrSystemUser()).isFalse();
+        expect.withMessage("Non-current user")
+                .that(mUserController.isCurrentOrSystemUser()).isFalse();
     }
 
     @Test
@@ -78,8 +82,8 @@
         when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
         when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
 
-        assertWithMessage("System user")
-                .that(RadioServiceUserController.isCurrentOrSystemUser()).isTrue();
+        expect.withMessage("System user")
+                .that(mUserController.isCurrentOrSystemUser()).isTrue();
     }
 
     @Test
@@ -87,14 +91,14 @@
         when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
         doThrow(new RuntimeException()).when(ActivityManager::getCurrentUser);
 
-        assertWithMessage("User when activity manager fails")
-                .that(RadioServiceUserController.isCurrentOrSystemUser()).isFalse();
+        expect.withMessage("User when activity manager fails")
+                .that(mUserController.isCurrentOrSystemUser()).isFalse();
     }
 
     @Test
     public void getCurrentUser() {
-        assertWithMessage("Current user")
-                .that(RadioServiceUserController.getCurrentUser()).isEqualTo(USER_ID_1);
+        expect.withMessage("Current user")
+                .that(mUserController.getCurrentUser()).isEqualTo(USER_ID_1);
     }
 
     @Test
@@ -102,7 +106,15 @@
         when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
         doThrow(new RuntimeException()).when(ActivityManager::getCurrentUser);
 
-        assertWithMessage("Current user when activity manager fails")
-                .that(RadioServiceUserController.getCurrentUser()).isEqualTo(UserHandle.USER_NULL);
+        expect.withMessage("Current user when activity manager fails")
+                .that(mUserController.getCurrentUser()).isEqualTo(UserHandle.USER_NULL);
+    }
+
+    @Test
+    public void getCallingUserId() {
+        when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
+
+        expect.withMessage("Calling user id")
+                .that(mUserController.getCallingUserId()).isEqualTo(USER_ID_1);
     }
 }
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
index 22f3bd4..63f12d8 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
@@ -91,12 +91,13 @@
     private IAnnouncementListener mAnnouncementListenerMock;
     @Mock
     private IBinder mListenerBinderMock;
+    @Mock
+    private RadioServiceUserController mUserControllerMock;
 
     @Override
     protected void initializeSession(StaticMockitoSessionBuilder builder) {
         builder.spyStatic(ServiceManager.class)
-                .spyStatic(RadioModule.class)
-                .spyStatic(RadioServiceUserController.class);
+                .spyStatic(RadioModule.class);
     }
 
     @Test
@@ -156,7 +157,7 @@
     @Test
     public void openSession_forNonCurrentUser_throwsException() throws Exception {
         createBroadcastRadioService();
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         IllegalStateException thrown = assertThrows(IllegalStateException.class,
                 () -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
@@ -206,9 +207,9 @@
     }
 
     private void createBroadcastRadioService() throws RemoteException {
-        doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(true).when(mUserControllerMock).isCurrentOrSystemUser();
         mockServiceManager();
-        mBroadcastRadioService = new BroadcastRadioServiceImpl(SERVICE_LIST);
+        mBroadcastRadioService = new BroadcastRadioServiceImpl(SERVICE_LIST, mUserControllerMock);
     }
 
     private void mockServiceManager() throws RemoteException {
@@ -222,9 +223,9 @@
                 any(IServiceCallback.class)));
 
         doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
-                eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class)));
+                eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class), any()));
         doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
-                eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class)));
+                eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class), any()));
 
         when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
         when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
index a952bde..368df09 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
@@ -34,6 +34,8 @@
 import android.os.ParcelableException;
 import android.os.RemoteException;
 
+import com.android.server.broadcastradio.RadioServiceUserController;
+
 import com.google.common.truth.Expect;
 
 import org.junit.Before;
@@ -63,6 +65,8 @@
     private IAnnouncementListener mListenerMock;
     @Mock
     private android.hardware.broadcastradio.ICloseHandle mHalCloseHandleMock;
+    @Mock
+    private RadioServiceUserController mUserControllerMock;
 
     // RadioModule under test
     private RadioModule mRadioModule;
@@ -70,7 +74,8 @@
 
     @Before
     public void setup() throws RemoteException {
-        mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES);
+        mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES,
+                mUserControllerMock);
 
         // TODO(b/241118988): test non-null image for getImage method
         when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index e963caff..016a900 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -49,12 +49,10 @@
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioTuner;
 import android.hardware.radio.UniqueProgramIdentifier;
-import android.os.Binder;
 import android.os.DeadObjectException;
 import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
-import android.os.UserHandle;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -147,10 +145,10 @@
 
     // Mocks
     @Mock
-    private UserHandle mUserHandleMock;
-    @Mock
     private IBroadcastRadio mBroadcastRadioMock;
     private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
+    @Mock
+    private RadioServiceUserController mUserControllerMock;
 
     // RadioModule under test
     private RadioModule mRadioModule;
@@ -169,8 +167,7 @@
 
     @Override
     protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder.spyStatic(RadioServiceUserController.class).spyStatic(CompatChanges.class)
-                .spyStatic(Binder.class);
+        builder.spyStatic(CompatChanges.class);
     }
 
     @Before
@@ -181,13 +178,12 @@
 
         doReturn(true).when(() -> CompatChanges.isChangeEnabled(
                 eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
-        doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier();
-        doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
-        doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
-        doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
+        doReturn(USER_ID_1).when(mUserControllerMock).getCallingUserId();
+        doReturn(true).when(mUserControllerMock).isCurrentOrSystemUser();
+        doReturn(USER_ID_1).when(mUserControllerMock).getCurrentUser();
 
         mRadioModule = new RadioModule(mBroadcastRadioMock,
-                AidlTestUtils.makeDefaultModuleProperties());
+                AidlTestUtils.makeDefaultModuleProperties(), mUserControllerMock);
 
         doAnswer(invocation -> {
             mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
@@ -224,7 +220,7 @@
     @Test
     public void setConfiguration_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
 
@@ -421,7 +417,7 @@
     @Test
     public void tune_forNonCurrentUser_doesNotTune() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
         ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
         RadioManager.ProgramInfo tuneInfo =
                 AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
@@ -501,7 +497,7 @@
         openAidlClients(/* numClients= */ 1);
         mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo(
                 ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
 
@@ -580,7 +576,7 @@
         openAidlClients(/* numClients= */ 1);
         mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo(
                 ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
 
@@ -614,7 +610,7 @@
     @Test
     public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].cancel();
 
@@ -685,7 +681,7 @@
     @Test
     public void startBackgroundScan_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].startBackgroundScan();
 
@@ -955,7 +951,7 @@
         openAidlClients(/* numClients= */ 1);
         ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
                 /* includeCategories= */ true, /* excludeModifications= */ false);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].startProgramListUpdates(filter);
 
@@ -994,7 +990,7 @@
         ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
                 /* includeCategories= */ true, /* excludeModifications= */ false);
         mTunerSessions[0].startProgramListUpdates(filter);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].stopProgramListUpdates();
 
@@ -1060,7 +1056,7 @@
     public void setConfigFlag_forNonCurrentUser_doesNotSetConfigFlag() throws Exception {
         openAidlClients(/* numClients= */ 1);
         int flag = UNSUPPORTED_CONFIG_FLAG + 1;
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
 
@@ -1125,7 +1121,7 @@
         openAidlClients(/* numClients= */ 1);
         Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
                 "mockParam2", "mockValue2");
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].setParameters(parametersSet);
 
@@ -1179,7 +1175,7 @@
     public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
             throws Exception {
         openAidlClients(1);
-        doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
+        doReturn(USER_ID_2).when(mUserControllerMock).getCurrentUser();
 
         mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo(
                 AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY));
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
index acf698b..7c3f221 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
@@ -88,11 +88,12 @@
     private IAnnouncementListener mAnnouncementListenerMock;
     @Mock
     private IBinder mBinderMock;
+    @Mock
+    private RadioServiceUserController mUserControllerMock;
 
     @Override
     protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder.spyStatic(RadioModule.class)
-                .spyStatic(RadioServiceUserController.class);
+        builder.spyStatic(RadioModule.class);
     }
 
     @Test
@@ -156,7 +157,7 @@
     @Test
     public void openSession_forNonCurrentUser_throwsException() throws Exception {
         createBroadcastRadioService();
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        when(mUserControllerMock.isCurrentOrSystemUser()).thenReturn(false);
 
         IllegalStateException thrown = assertThrows(IllegalStateException.class,
                 () -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
@@ -206,11 +207,11 @@
     }
 
     private void createBroadcastRadioService() throws RemoteException {
-        doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        when(mUserControllerMock.isCurrentOrSystemUser()).thenReturn(true);
 
         mockServiceManager();
         mBroadcastRadioService = new BroadcastRadioService(/* nextModuleId= */ FM_RADIO_MODULE_ID,
-                mServiceManagerMock);
+                mServiceManagerMock, mUserControllerMock);
     }
 
     private void mockServiceManager() throws RemoteException {
@@ -231,9 +232,9 @@
                 }).thenReturn(true);
 
         doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
-                eq(FM_RADIO_MODULE_ID), anyString()));
+                eq(FM_RADIO_MODULE_ID), anyString(), any()));
         doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
-                eq(DAB_RADIO_MODULE_ID), anyString()));
+                eq(DAB_RADIO_MODULE_ID), anyString(), any()));
 
         when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
         when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
index 1f5e770..b53f7ca 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
@@ -36,6 +36,8 @@
 import android.hardware.radio.RadioManager;
 import android.os.RemoteException;
 
+import com.android.server.broadcastradio.RadioServiceUserController;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -61,13 +63,16 @@
     private IAnnouncementListener mListenerMock;
     @Mock
     private android.hardware.broadcastradio.V2_0.ICloseHandle mHalCloseHandleMock;
+    @Mock
+    private RadioServiceUserController mUserControllerMock;
 
     private RadioModule mRadioModule;
     private android.hardware.broadcastradio.V2_0.IAnnouncementListener mHalListener;
 
     @Before
     public void setup() throws RemoteException {
-        mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES);
+        mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES,
+                mUserControllerMock);
 
         when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 8c16d79..fa07447 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -38,13 +38,13 @@
 import android.hardware.radio.UniqueProgramIdentifier;
 import android.os.RemoteException;
 
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
 import com.android.server.broadcastradio.RadioServiceUserController;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.mockito.stubbing.Answer;
 import org.mockito.verification.VerificationWithTimeout;
 
@@ -55,7 +55,8 @@
 /**
  * Tests for v2 HAL RadioModule.
  */
-public class StartProgramListUpdatesFanoutTest extends ExtendedRadioMockitoTestCase {
+@RunWith(MockitoJUnitRunner.class)
+public class StartProgramListUpdatesFanoutTest {
     private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
 
     private static final VerificationWithTimeout CB_TIMEOUT = timeout(500);
@@ -64,6 +65,8 @@
     @Mock IBroadcastRadio mBroadcastRadioMock;
     @Mock ITunerSession mHalTunerSessionMock;
     private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
+    @Mock
+    private RadioServiceUserController mUserControllerMock;
 
     // RadioModule under test
     private RadioModule mRadioModule;
@@ -110,17 +113,12 @@
     private static final RadioManager.ProgramInfo TEST_DAB_INFO = TestUtils.makeProgramInfo(
             TEST_DAB_SELECTOR, TEST_QUALITY);
 
-    @Override
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder.spyStatic(RadioServiceUserController.class);
-    }
-
     @Before
     public void setup() throws RemoteException {
-        doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(true).when(mUserControllerMock).isCurrentOrSystemUser();
 
-        mRadioModule = new RadioModule(mBroadcastRadioMock,
-                TestUtils.makeDefaultModuleProperties());
+        mRadioModule = new RadioModule(mBroadcastRadioMock, TestUtils.makeDefaultModuleProperties(),
+                mUserControllerMock);
 
         doAnswer((Answer) invocation -> {
             mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 55aae9d..62445cf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -44,16 +44,12 @@
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioTuner;
-import android.os.Binder;
 import android.os.DeadObjectException;
 import android.os.ParcelableException;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
 import com.android.server.broadcastradio.RadioServiceUserController;
 
 import com.google.common.truth.Expect;
@@ -76,7 +72,7 @@
  * Tests for HIDL HAL TunerSession.
  */
 @RunWith(MockitoJUnitRunner.class)
-public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase {
+public final class TunerSessionHidlTest {
 
     private static final int USER_ID_1 = 11;
     private static final int USER_ID_2 = 12;
@@ -104,27 +100,21 @@
     public final Expect mExpect = Expect.create();
 
     @Mock
-    private UserHandle mUserHandleMock;
-    @Mock
     private IBroadcastRadio mBroadcastRadioMock;
     @Mock
     ITunerSession mHalTunerSessionMock;
     private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
-
-    @Override
-    protected void initializeSession(StaticMockitoSessionBuilder builder) {
-        builder.spyStatic(RadioServiceUserController.class).spyStatic(Binder.class);
-    }
+    @Mock
+    private RadioServiceUserController mUserControllerMock;
 
     @Before
     public void setup() throws Exception {
-        doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier();
-        doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
-        doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
-        doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
+        doReturn(USER_ID_1).when(mUserControllerMock).getCallingUserId();
+        doReturn(true).when(mUserControllerMock).isCurrentOrSystemUser();
+        doReturn(USER_ID_1).when(mUserControllerMock).getCurrentUser();
 
         mRadioModule = new RadioModule(mBroadcastRadioMock,
-                TestUtils.makeDefaultModuleProperties());
+                TestUtils.makeDefaultModuleProperties(), mUserControllerMock);
 
         doAnswer(invocation -> {
             mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
@@ -228,7 +218,7 @@
     @Test
     public void setConfiguration_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
 
@@ -403,7 +393,7 @@
     @Test
     public void tune_forNonCurrentUser_doesNotTune() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
         ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
         RadioManager.ProgramInfo tuneInfo =
                 TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
@@ -481,7 +471,7 @@
         openAidlClients(/* numClients= */ 1);
         mHalCurrentInfo = TestUtils.makeHalProgramInfo(
                 Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
 
@@ -559,7 +549,7 @@
         openAidlClients(/* numClients= */ 1);
         mHalCurrentInfo = TestUtils.makeHalProgramInfo(
                 Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
 
@@ -593,7 +583,7 @@
     @Test
     public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].cancel();
 
@@ -663,7 +653,7 @@
     @Test
     public void startBackgroundScan_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].startBackgroundScan();
 
@@ -676,7 +666,7 @@
         openAidlClients(/* numClients= */ 1);
         ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
                 /* includeCategories= */ true, /* excludeModifications= */ false);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].startProgramListUpdates(filter);
 
@@ -715,7 +705,7 @@
         ProgramList.Filter aidlFilter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
                 /* includeCategories= */ true, /* excludeModifications= */ false);
         mTunerSessions[0].startProgramListUpdates(aidlFilter);
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].stopProgramListUpdates();
 
@@ -781,7 +771,7 @@
     public void setConfigFlag_forNonCurrentUser_doesNotSetConfigFlag() throws Exception {
         openAidlClients(/* numClients= */ 1);
         int flag = UNSUPPORTED_CONFIG_FLAG + 1;
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
 
@@ -846,7 +836,7 @@
         openAidlClients(/* numClients= */ 1);
         Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
                 "mockParam2", "mockValue2");
-        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        doReturn(false).when(mUserControllerMock).isCurrentOrSystemUser();
 
         mTunerSessions[0].setParameters(parametersSet);
 
@@ -900,7 +890,7 @@
     public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
             throws Exception {
         openAidlClients(1);
-        doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
+        doReturn(USER_ID_2).when(mUserControllerMock).getCurrentUser();
 
         mHalTunerCallback.onCurrentProgramInfoChanged(TestUtils.makeHalProgramInfo(
                 TestUtils.makeHalFmSelector(/* freq= */ 97300), SIGNAL_QUALITY));
diff --git a/libs/appfunctions/OWNERS b/libs/appfunctions/OWNERS
new file mode 100644
index 0000000..c093675
--- /dev/null
+++ b/libs/appfunctions/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /core/java/android/app/appfunctions/OWNERS
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 028b9b0..fcbcb02 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -32,8 +32,9 @@
     public BroadcastRadioService(Context context) {
         super(context);
         ArrayList<String> serviceNameList = IRadioServiceAidlImpl.getServicesNames();
-        mServiceImpl = serviceNameList.isEmpty() ? new IRadioServiceHidlImpl(this)
-                : new IRadioServiceAidlImpl(this, serviceNameList);
+        RadioServiceUserController userController = new RadioServiceUserControllerImpl();
+        mServiceImpl = serviceNameList.isEmpty() ? new IRadioServiceHidlImpl(this, userController)
+                : new IRadioServiceAidlImpl(this, serviceNameList, userController);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
index 16514fa..332958d 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
@@ -69,8 +69,9 @@
         return serviceList;
     }
 
-    IRadioServiceAidlImpl(BroadcastRadioService service, ArrayList<String> serviceList) {
-        this(service, new BroadcastRadioServiceImpl(serviceList));
+    IRadioServiceAidlImpl(BroadcastRadioService service, List<String> serviceList,
+            RadioServiceUserController userController) {
+        this(service, new BroadcastRadioServiceImpl(serviceList, userController));
         Slogf.i(TAG, "Initialize BroadcastRadioServiceAidl(%s)", service);
     }
 
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index ab08342..67d3c95 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -59,13 +59,16 @@
     @GuardedBy("mLock")
     private final List<RadioManager.ModuleProperties> mV1Modules;
 
-    IRadioServiceHidlImpl(BroadcastRadioService service) {
+    IRadioServiceHidlImpl(BroadcastRadioService service,
+            RadioServiceUserController userController) {
         mService = Objects.requireNonNull(service, "broadcast radio service cannot be null");
-        mHal1Client = new com.android.server.broadcastradio.hal1.BroadcastRadioService();
+        Objects.requireNonNull(userController, "user controller cannot be null");
+        mHal1Client = new com.android.server.broadcastradio.hal1.BroadcastRadioService(
+                userController);
         mV1Modules = mHal1Client.loadModules();
         OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
         mHal2Client = new com.android.server.broadcastradio.hal2.BroadcastRadioService(
-                max.isPresent() ? max.getAsInt() + 1 : 0);
+                max.isPresent() ? max.getAsInt() + 1 : 0, userController);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java b/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
index c705ebe..c15ccf1 100644
--- a/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
+++ b/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
@@ -16,19 +16,11 @@
 
 package com.android.server.broadcastradio;
 
-import android.app.ActivityManager;
-import android.os.Binder;
-import android.os.UserHandle;
-
 /**
- * Controller to handle users in {@link com.android.server.broadcastradio.BroadcastRadioService}
+ * Controller interface to handle users in
+ * {@link com.android.server.broadcastradio.BroadcastRadioService}
  */
-public final class RadioServiceUserController {
-
-    private RadioServiceUserController() {
-        throw new UnsupportedOperationException(
-                "RadioServiceUserController class is noninstantiable");
-    }
+public interface RadioServiceUserController {
 
     /**
      * Check if the user calling the method in Broadcast Radio Service is the current user or the
@@ -37,26 +29,20 @@
      * @return {@code true} if the user calling this method is the current user of system user,
      * {@code false} otherwise.
      */
-    public static boolean isCurrentOrSystemUser() {
-        int callingUser = Binder.getCallingUserHandle().getIdentifier();
-        return callingUser == getCurrentUser() || callingUser == UserHandle.USER_SYSTEM;
-    }
+    boolean isCurrentOrSystemUser();
 
     /**
      * Get current foreground user for Broadcast Radio Service
      *
      * @return foreground user id.
      */
-    public static int getCurrentUser() {
-        int userId = UserHandle.USER_NULL;
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userId = ActivityManager.getCurrentUser();
-        } catch (RuntimeException e) {
-            // Activity manager not running, nothing we can do assume user 0.
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        return userId;
-    }
-}
+    int getCurrentUser();
+
+    /**
+     * Get id of the user handle assigned to the process that sent the binder transaction that is
+     * being processed
+     *
+     * @return Id of the user handle
+     */
+    int getCallingUserId();
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/broadcastradio/RadioServiceUserControllerImpl.java b/services/core/java/com/android/server/broadcastradio/RadioServiceUserControllerImpl.java
new file mode 100644
index 0000000..e305d20
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/RadioServiceUserControllerImpl.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2024 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.android.server.broadcastradio;
+
+import android.app.ActivityManager;
+import android.os.Binder;
+import android.os.UserHandle;
+
+/**
+ * Implementation for the controller to handle users in
+ * {@link com.android.server.broadcastradio.BroadcastRadioService}
+ */
+public final class RadioServiceUserControllerImpl implements RadioServiceUserController {
+
+    /**
+     * @see RadioServiceUserController#isCurrentOrSystemUser()
+     */
+    @Override
+    public boolean isCurrentOrSystemUser() {
+        int callingUser = getCallingUserId();
+        return callingUser == getCurrentUser() || callingUser == UserHandle.USER_SYSTEM;
+    }
+
+    /**
+     * @see RadioServiceUserController#getCurrentUser()
+     */
+    @Override
+    public int getCurrentUser() {
+        int userId = UserHandle.USER_NULL;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            userId = ActivityManager.getCurrentUser();
+        } catch (RuntimeException e) {
+            // Activity manager not running, nothing we can do assume user 0.
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return userId;
+    }
+
+    /**
+     * @see RadioServiceUserController#getCallingUserId()
+     */
+    @Override
+    public int getCallingUserId() {
+        return Binder.getCallingUserHandle().getIdentifier();
+    }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index d9f8588..bf9d1a3 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -51,6 +51,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Object mLock = new Object();
+    private final RadioServiceUserController mUserController;
 
     @GuardedBy("mLock")
     private int mNextModuleId;
@@ -77,7 +78,7 @@
                 }
 
                 RadioModule radioModule =
-                        RadioModule.tryLoadingModule(moduleId, name, newBinder);
+                        RadioModule.tryLoadingModule(moduleId, name, newBinder, mUserController);
                 if (radioModule == null) {
                     Slogf.w(TAG, "No module %s with id %d (HAL AIDL)", name, moduleId);
                     return;
@@ -141,9 +142,12 @@
      * BroadcastRadio HAL services
      *
      * @param serviceNameList list of names of AIDL BroadcastRadio HAL services
+     * @param userController User controller implementation
      */
-    public BroadcastRadioServiceImpl(ArrayList<String> serviceNameList) {
+    public BroadcastRadioServiceImpl(List<String> serviceNameList,
+            RadioServiceUserController userController) {
         mNextModuleId = 0;
+        mUserController = Objects.requireNonNull(userController, "User controller can not be null");
         if (DEBUG) {
             Slogf.d(TAG, "Initializing BroadcastRadioServiceImpl %s", IBroadcastRadio.DESCRIPTOR);
         }
@@ -202,7 +206,7 @@
         if (DEBUG) {
             Slogf.d(TAG, "Open AIDL radio session");
         }
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.e(TAG, "Cannot open tuner on AIDL HAL client for non-current user");
             throw new IllegalStateException("Cannot open session for non-current user");
         }
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 03e347a..928fa29 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -62,6 +62,7 @@
     private final Handler mHandler;
     private final RadioEventLogger mLogger;
     private final RadioManager.ModuleProperties mProperties;
+    private final RadioServiceUserController mUserController;
 
     /**
      * Tracks antenna state reported by HAL (if any).
@@ -194,15 +195,18 @@
     };
 
     @VisibleForTesting
-    RadioModule(IBroadcastRadio service, RadioManager.ModuleProperties properties) {
+    RadioModule(IBroadcastRadio service, RadioManager.ModuleProperties properties,
+            RadioServiceUserController userController) {
         mProperties = Objects.requireNonNull(properties, "properties cannot be null");
         mService = Objects.requireNonNull(service, "service cannot be null");
         mHandler = new Handler(Looper.getMainLooper());
+        mUserController = Objects.requireNonNull(userController, "User controller can not be null");
         mLogger = new RadioEventLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
     }
 
     @Nullable
-    static RadioModule tryLoadingModule(int moduleId, String moduleName, IBinder serviceBinder) {
+    static RadioModule tryLoadingModule(int moduleId, String moduleName, IBinder serviceBinder,
+            RadioServiceUserController userController) {
         try {
             Slogf.i(TAG, "Try loading module for module id = %d, module name = %s",
                     moduleId, moduleName);
@@ -232,7 +236,7 @@
             RadioManager.ModuleProperties prop = ConversionUtils.propertiesFromHalProperties(
                     moduleId, moduleName, service.getProperties(), amfmConfig, dabConfig);
 
-            return new RadioModule(service, prop);
+            return new RadioModule(service, prop, userController);
         } catch (RemoteException ex) {
             Slogf.e(TAG, ex, "Failed to load module %s", moduleName);
             return null;
@@ -255,7 +259,7 @@
         RadioManager.ProgramInfo currentProgramInfo;
         synchronized (mLock) {
             boolean isFirstTunerSession = mAidlTunerSessions.isEmpty();
-            tunerSession = new TunerSession(this, mService, userCb);
+            tunerSession = new TunerSession(this, mService, userCb, mUserController);
             mAidlTunerSessions.add(tunerSession);
             antennaConnected = mAntennaConnected;
             currentProgramInfo = mCurrentProgramInfo;
@@ -433,7 +437,7 @@
 
     @GuardedBy("mLock")
     private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
-        int currentUserId = RadioServiceUserController.getCurrentUser();
+        int currentUserId = mUserController.getCurrentUser();
         List<TunerSession> deadSessions = null;
         for (int i = 0; i < mAidlTunerSessions.size(); i++) {
             if (mAidlTunerSessions.valueAt(i).mUserId != currentUserId
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index e90a1dd..f22661b 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -52,6 +52,7 @@
     final android.hardware.radio.ITunerCallback mCallback;
     private final int mUid;
     private final IBroadcastRadio mService;
+    private final RadioServiceUserController mUserController;
 
     @GuardedBy("mLock")
     private boolean mIsClosed;
@@ -65,11 +66,13 @@
     private RadioManager.BandConfig mPlaceHolderConfig;
 
     TunerSession(RadioModule radioModule, IBroadcastRadio service,
-            android.hardware.radio.ITunerCallback callback) {
+            android.hardware.radio.ITunerCallback callback,
+            RadioServiceUserController userController) {
         mModule = Objects.requireNonNull(radioModule, "radioModule cannot be null");
         mService = Objects.requireNonNull(service, "service cannot be null");
-        mUserId = Binder.getCallingUserHandle().getIdentifier();
         mCallback = Objects.requireNonNull(callback, "callback cannot be null");
+        mUserController = Objects.requireNonNull(userController, "User controller can not be null");
+        mUserId = mUserController.getCallingUserId();
         mUid = Binder.getCallingUid();
         mLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
     }
@@ -126,7 +129,7 @@
 
     @Override
     public void setConfiguration(RadioManager.BandConfig config) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot set configuration for AIDL HAL client from non-current user");
             return;
         }
@@ -169,7 +172,7 @@
     public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
         mLogger.logRadioEvent("Step with direction %s, skipSubChannel?  %s",
                 directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot step on AIDL HAL client from non-current user");
             return;
         }
@@ -187,7 +190,7 @@
     public void seek(boolean directionDown, boolean skipSubChannel) throws RemoteException {
         mLogger.logRadioEvent("Seek with direction %s, skipSubChannel? %s",
                 directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot scan on AIDL HAL client from non-current user");
             return;
         }
@@ -204,7 +207,7 @@
     @Override
     public void tune(ProgramSelector selector) throws RemoteException {
         mLogger.logRadioEvent("Tune with selector %s", selector);
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot tune on AIDL HAL client from non-current user");
             return;
         }
@@ -226,7 +229,7 @@
     @Override
     public void cancel() {
         Slogf.i(TAG, "Cancel");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot cancel on AIDL HAL client from non-current user");
             return;
         }
@@ -255,7 +258,7 @@
     @Override
     public boolean startBackgroundScan() {
         Slogf.w(TAG, "Explicit background scan trigger is not supported with HAL AIDL");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot start background scan on AIDL HAL client from non-current user");
             return false;
         }
@@ -268,7 +271,7 @@
     @Override
     public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
         mLogger.logRadioEvent("Start programList updates %s", filter);
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG,
                     "Cannot start program list updates on AIDL HAL client from non-current user");
             return;
@@ -344,7 +347,7 @@
     @Override
     public void stopProgramListUpdates() throws RemoteException {
         mLogger.logRadioEvent("Stop programList updates");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG,
                     "Cannot stop program list updates on AIDL HAL client from non-current user");
             return;
@@ -389,7 +392,7 @@
     public void setConfigFlag(int flag, boolean value) throws RemoteException {
         mLogger.logRadioEvent("set ConfigFlag %s to %b ",
                 ConfigFlag.$.toString(flag), value);
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot set config flag for AIDL HAL client from non-current user");
             return;
         }
@@ -406,7 +409,7 @@
     @Override
     public Map<String, String> setParameters(Map<String, String> parameters) {
         mLogger.logRadioEvent("Set parameters ");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot set parameters for AIDL HAL client from non-current user");
             return new ArrayMap<>();
         }
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
index fb42c94..6a6a3ae 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
@@ -35,6 +35,7 @@
      * This field is used by native code, do not access or modify.
      */
     private final long mNativeContext = nativeInit();
+    private final RadioServiceUserController mUserController;
 
     private final Object mLock = new Object();
 
@@ -50,6 +51,10 @@
     private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
             RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
 
+    public BroadcastRadioService(RadioServiceUserController userController) {
+        mUserController = Objects.requireNonNull(userController, "User controller can not be null");
+    }
+
     public @NonNull List<RadioManager.ModuleProperties> loadModules() {
         synchronized (mLock) {
             return Objects.requireNonNull(nativeLoadModules(mNativeContext));
@@ -58,7 +63,7 @@
 
     public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
             boolean withAudio, ITunerCallback callback) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.e(TAG, "Cannot open tuner on HAL 1.x client for non-current user");
             throw new IllegalStateException("Cannot open tuner for non-current user");
         }
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index 7cac409..8e64600d2 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 
 import com.android.server.broadcastradio.RadioServiceUserController;
+import com.android.server.broadcastradio.RadioServiceUserControllerImpl;
 import com.android.server.utils.Slogf;
 
 import java.util.List;
@@ -51,6 +52,7 @@
     private boolean mIsMuted = false;
     private int mRegion;
     private final boolean mWithAudio;
+    private final RadioServiceUserController mUserController = new RadioServiceUserControllerImpl();
 
     Tuner(@NonNull ITunerCallback clientCallback, int halRev,
             int region, boolean withAudio, int band) {
@@ -127,7 +129,7 @@
 
     @Override
     public void setConfiguration(RadioManager.BandConfig config) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot set configuration for HAL 1.x client from non-current user");
             return;
         }
@@ -176,7 +178,7 @@
 
     @Override
     public void step(boolean directionDown, boolean skipSubChannel) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot step on HAL 1.x client from non-current user");
             return;
         }
@@ -189,7 +191,7 @@
 
     @Override
     public void seek(boolean directionDown, boolean skipSubChannel) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot seek on HAL 1.x client from non-current user");
             return;
         }
@@ -202,7 +204,7 @@
 
     @Override
     public void tune(ProgramSelector selector) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot tune on HAL 1.x client from non-current user");
             return;
         }
@@ -219,7 +221,7 @@
 
     @Override
     public void cancel() {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot cancel on HAL 1.x client from non-current user");
             return;
         }
@@ -231,7 +233,7 @@
 
     @Override
     public void cancelAnnouncement() {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot cancel announcement on HAL 1.x client from non-current user");
             return;
         }
@@ -260,7 +262,7 @@
 
     @Override
     public boolean startBackgroundScan() {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG,
                     "Cannot start background scan on HAL 1.x client from non-current user");
             return false;
@@ -285,7 +287,7 @@
 
     @Override
     public void startProgramListUpdates(ProgramList.Filter filter) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG,
                     "Cannot start program list updates on HAL 1.x client from non-current user");
             return;
@@ -295,7 +297,7 @@
 
     @Override
     public void stopProgramListUpdates() {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG,
                     "Cannot stop program list updates on HAL 1.x client from non-current user");
             return;
@@ -321,7 +323,7 @@
 
     @Override
     public void setConfigFlag(int flag, boolean value) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot set config flag for HAL 1.x client from non-current user");
             return;
         }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index a4efa2e..3227afd 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -50,6 +50,8 @@
 
     private final Object mLock = new Object();
 
+    private final RadioServiceUserController mUserController;
+
     @GuardedBy("mLock")
     private int mNextModuleId;
 
@@ -75,7 +77,8 @@
                     moduleId = mNextModuleId;
                 }
 
-                RadioModule radioModule = RadioModule.tryLoadingModule(moduleId, serviceName);
+                RadioModule radioModule = RadioModule.tryLoadingModule(moduleId, serviceName,
+                        mUserController);
                 if (radioModule == null) {
                     return;
                 }
@@ -123,8 +126,9 @@
         }
     };
 
-    public BroadcastRadioService(int nextModuleId) {
+    public BroadcastRadioService(int nextModuleId, RadioServiceUserController userController) {
         mNextModuleId = nextModuleId;
+        mUserController = Objects.requireNonNull(userController, "User controller can not be null");
         try {
             IServiceManager manager = IServiceManager.getService();
             if (manager == null) {
@@ -138,8 +142,10 @@
     }
 
     @VisibleForTesting
-    BroadcastRadioService(int nextModuleId, IServiceManager manager) {
+    BroadcastRadioService(int nextModuleId, IServiceManager manager,
+            RadioServiceUserController userController) {
         mNextModuleId = nextModuleId;
+        mUserController = Objects.requireNonNull(userController, "User controller can not be null");
         Objects.requireNonNull(manager, "Service manager cannot be null");
         try {
             manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener);
@@ -171,7 +177,7 @@
     public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
             boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
         Slogf.v(TAG, "Open HIDL 2.0 session with module id " + moduleId);
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.e(TAG, "Cannot open tuner on HAL 2.0 client for non-current user");
             throw new IllegalStateException("Cannot open session for non-current user");
         }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index d3b2448..a0d6cc2 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -66,6 +66,7 @@
     private final Object mLock = new Object();
     private final Handler mHandler;
     private final RadioEventLogger mEventLogger;
+    private final RadioServiceUserController mUserController;
 
     @GuardedBy("mLock")
     private ITunerSession mHalTunerSession;
@@ -148,16 +149,18 @@
     private final Set<TunerSession> mAidlTunerSessions = new ArraySet<>();
 
     @VisibleForTesting
-    RadioModule(@NonNull IBroadcastRadio service,
-            @NonNull RadioManager.ModuleProperties properties) {
+    RadioModule(IBroadcastRadio service, RadioManager.ModuleProperties properties,
+            RadioServiceUserController userController) {
         mProperties = Objects.requireNonNull(properties);
         mService = Objects.requireNonNull(service);
         mHandler = new Handler(Looper.getMainLooper());
+        mUserController = Objects.requireNonNull(userController, "User controller can not be null");
         mEventLogger = new RadioEventLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
     }
 
     @Nullable
-    static RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
+    static RadioModule tryLoadingModule(int idx, String fqName,
+            RadioServiceUserController controller) {
         try {
             Slogf.i(TAG, "Try loading module for idx " + idx + ", fqName " + fqName);
             IBroadcastRadio service = IBroadcastRadio.getService(fqName);
@@ -179,7 +182,7 @@
             RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName,
                     service.getProperties(), amfmConfig.value, dabConfig.value);
 
-            return new RadioModule(service, prop);
+            return new RadioModule(service, prop, controller);
         } catch (RemoteException ex) {
             Slogf.e(TAG, "Failed to load module " + fqName, ex);
             return null;
@@ -208,7 +211,8 @@
                 });
                 mHalTunerSession = Objects.requireNonNull(hwSession.value);
             }
-            TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb);
+            TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb,
+                    mUserController);
             mAidlTunerSessions.add(tunerSession);
 
             // Propagate state to new client. Note: These callbacks are invoked while holding mLock
@@ -375,7 +379,7 @@
 
     @GuardedBy("mLock")
     private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
-        int currentUserId = RadioServiceUserController.getCurrentUser();
+        int currentUserId = mUserController.getCurrentUser();
         List<TunerSession> deadSessions = null;
         for (TunerSession tunerSession : mAidlTunerSessions) {
             if (tunerSession.mUserId != currentUserId && tunerSession.mUserId
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 80efacd..dc164b1 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -16,7 +16,6 @@
 
 package com.android.server.broadcastradio.hal2;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Bitmap;
 import android.hardware.broadcastradio.V2_0.ConfigFlag;
@@ -27,7 +26,6 @@
 import android.hardware.radio.ProgramList;
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
-import android.os.Binder;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -55,6 +53,7 @@
     private final ITunerSession mHwSession;
     final int mUserId;
     final android.hardware.radio.ITunerCallback mCallback;
+    private final RadioServiceUserController mUserController;
 
     @GuardedBy("mLock")
     private boolean mIsClosed = false;
@@ -66,12 +65,14 @@
     // necessary only for older APIs compatibility
     private RadioManager.BandConfig mDummyConfig = null;
 
-    TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession,
-            @NonNull android.hardware.radio.ITunerCallback callback) {
+    TunerSession(RadioModule module, ITunerSession hwSession,
+            android.hardware.radio.ITunerCallback callback,
+            RadioServiceUserController userController) {
         mModule = Objects.requireNonNull(module);
         mHwSession = Objects.requireNonNull(hwSession);
-        mUserId = Binder.getCallingUserHandle().getIdentifier();
         mCallback = Objects.requireNonNull(callback);
+        mUserController = Objects.requireNonNull(userController, "User controller can not be null");
+        mUserId = mUserController.getCallingUserId();
         mEventLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
     }
 
@@ -120,7 +121,7 @@
 
     @Override
     public void setConfiguration(RadioManager.BandConfig config) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot set configuration for HAL 2.0 client from non-current user");
             return;
         }
@@ -162,7 +163,7 @@
     public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
         mEventLogger.logRadioEvent("Step with direction %s, skipSubChannel?  %s",
                 directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot step on HAL 2.0 client from non-current user");
             return;
         }
@@ -177,7 +178,7 @@
     public void seek(boolean directionDown, boolean skipSubChannel) throws RemoteException {
         mEventLogger.logRadioEvent("Seek with direction %s, skipSubChannel? %s",
                 directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot scan on HAL 2.0 client from non-current user");
             return;
         }
@@ -191,7 +192,7 @@
     @Override
     public void tune(ProgramSelector selector) throws RemoteException {
         mEventLogger.logRadioEvent("Tune with selector %s", selector);
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot tune on HAL 2.0 client from non-current user");
             return;
         }
@@ -205,7 +206,7 @@
     @Override
     public void cancel() {
         Slogf.i(TAG, "Cancel");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot cancel on HAL 2.0 client from non-current user");
             return;
         }
@@ -230,7 +231,7 @@
     @Override
     public boolean startBackgroundScan() {
         Slogf.w(TAG, "Explicit background scan trigger is not supported with HAL 2.0");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG,
                     "Cannot start background scan on HAL 2.0 client from non-current user");
             return false;
@@ -242,7 +243,7 @@
     @Override
     public void startProgramListUpdates(ProgramList.Filter filter) {
         mEventLogger.logRadioEvent("start programList updates %s", filter);
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG,
                     "Cannot start program list updates on HAL 2.0 client from non-current user");
             return;
@@ -306,7 +307,7 @@
     @Override
     public void stopProgramListUpdates() throws RemoteException {
         mEventLogger.logRadioEvent("Stop programList updates");
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG,
                     "Cannot stop program list updates on HAL 2.0 client from non-current user");
             return;
@@ -355,7 +356,7 @@
     @Override
     public void setConfigFlag(int flag, boolean value) throws RemoteException {
         mEventLogger.logRadioEvent("Set ConfigFlag  %s = %b", ConfigFlag.toString(flag), value);
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot set config flag for HAL 2.0 client from non-current user");
             return;
         }
@@ -368,7 +369,7 @@
 
     @Override
     public Map<String, String> setParameters(Map<String, String> parameters) {
-        if (!RadioServiceUserController.isCurrentOrSystemUser()) {
+        if (!mUserController.isCurrentOrSystemUser()) {
             Slogf.w(TAG, "Cannot set parameters for HAL 2.0 client from non-current user");
             return new ArrayMap<>();
         }
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index ecd140e..96a25da 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -44,7 +44,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
@@ -324,11 +323,8 @@
         if (SubscriptionManager.isValidSubscriptionId(subId)) {
             // Get only configs as needed to save memory.
             final PersistableBundle carrierConfig =
-                    Flags.fixCrashOnGettingConfigWhenPhoneIsGone()
-                            ? CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
-                                    VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS)
-                            : mCarrierConfigManager.getConfigForSubId(subId,
-                                    VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+                    CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
+                            VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
             if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
                 mReadySubIdsBySlotId.put(slotId, subId);
 
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 887630b..b5cc553 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -59,7 +59,6 @@
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.test.TestLooper;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -73,10 +72,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.telephony.flags.Flags;
-
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -133,8 +129,6 @@
         TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
     }
 
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
 
     @NonNull private final Context mContext;
     @NonNull private final TestLooper mTestLooper;
@@ -193,7 +187,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CRASH_ON_GETTING_CONFIG_WHEN_PHONE_IS_GONE);
         doReturn(2).when(mTelephonyManager).getActiveModemCount();
 
         mCallback = mock(TelephonySubscriptionTrackerCallback.class);
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
index 24d203f..f5af99e 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
@@ -24,20 +24,31 @@
 import org.jetbrains.uast.UMethod
 
 /**
- * Given a UMethod, determine if this method is the entrypoint to an interface
- * generated by AIDL, returning the interface name if so, otherwise returning
- * null
+ * Given a UMethod, determine if this method is the entrypoint to an interface generated by AIDL,
+ * returning the interface name if so, otherwise returning null.
  */
 fun getContainingAidlInterface(context: JavaContext, node: UMethod): String? {
+    return containingAidlInterfacePsiClass(context, node)?.name
+}
+
+/**
+ * Given a UMethod, determine if this method is the entrypoint to an interface generated by AIDL,
+ * returning the fully qualified interface name if so, otherwise returning null.
+ */
+fun getContainingAidlInterfaceQualified(context: JavaContext, node: UMethod): String? {
+    return containingAidlInterfacePsiClass(context, node)?.qualifiedName
+}
+
+private fun containingAidlInterfacePsiClass(context: JavaContext, node: UMethod): PsiClass? {
     val containingStub = containingStub(context, node) ?: return null
     val superMethod = node.findSuperMethods(containingStub)
     if (superMethod.isEmpty()) return null
-    return containingStub.containingClass?.name
+    return containingStub.containingClass
 }
 
-/* Returns the containing Stub class if any. This is not sufficient to infer
- * that the method itself extends an AIDL generated method. See
- * getContainingAidlInterface for that purpose.
+/**
+ * Returns the containing Stub class if any. This is not sufficient to infer that the method itself
+ * extends an AIDL generated method. See getContainingAidlInterface for that purpose.
  */
 fun containingStub(context: JavaContext, node: UMethod?): PsiClass? {
     var superClass = node?.containingClass?.superClass
@@ -48,7 +59,7 @@
     return null
 }
 
-private fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
+fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
     if (psiClass == null) return false
     if (psiClass.name != "Stub") return false
     if (!context.evaluator.isStatic(psiClass)) return false
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 624a198..5c64697 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -41,6 +41,7 @@
         PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
         PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE,
         PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD,
+        FeatureAutomotiveDetector.ISSUE,
     )
 
     override val api: Int
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt
new file mode 100644
index 0000000..972b0c9
--- /dev/null
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 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.detector.api.Category
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.lint.detector.api.Detector
+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.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.ULiteralExpression
+import org.jetbrains.uast.UReferenceExpression
+
+/**
+ * A detector to check the usage of PackageManager.hasSystemFeature("
+ * android.hardware.type.automotive") in CTS tests.
+ */
+class FeatureAutomotiveDetector : Detector(), SourceCodeScanner {
+
+    companion object {
+
+        val EXPLANATION =
+            """
+            This class uses PackageManager.hasSystemFeature(\"android.hardware.type.automotive\") \
+            or other equivalent methods. \
+            If it is used to make a CTS test behave differently on AAOS, you should use \
+            @RequireAutomotive or @RequireNotAutomotive instead; otherwise, please ignore this \
+            warning. See https://g3doc.corp.google.com/wireless/android/partner/compatibility/\
+            g3doc/dev/write-a-test/index.md#write-a-test-that-behaves-differently-on-aaos
+            """
+
+        val ISSUE: Issue =
+            Issue.create(
+                id = "UsingFeatureAutomotiveInCTS",
+                briefDescription =
+                    "PackageManager.hasSystemFeature(\"" +
+                        " android.hardware.type.automotive\") is used in CTS tests",
+                explanation = EXPLANATION,
+                category = Category.TESTING,
+                priority = 8,
+                severity = Severity.WARNING,
+                implementation =
+                    Implementation(
+                        FeatureAutomotiveDetector::class.java,
+                        EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+                    )
+            )
+    }
+
+    override fun getApplicableMethodNames() =
+        listOf("hasSystemFeature", "hasFeature", "hasDeviceFeature", "bypassTestForFeatures")
+
+    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+        node.valueArguments.forEach {
+            val value =
+                when (it) {
+                    is ULiteralExpression -> it.value
+                    is UReferenceExpression -> ConstantEvaluator.evaluate(context, it)
+                    else -> null
+                }
+            if (value is String && value == "android.hardware.type.automotive") {
+                context.report(
+                    issue = ISSUE,
+                    location = context.getNameLocation(method),
+                    message = EXPLANATION
+                )
+            }
+        }
+    }
+}
diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt
new file mode 100644
index 0000000..b5e2c00
--- /dev/null
+++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2024 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.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
+import org.junit.Test
+
+@Suppress("UnstableApiUsage")
+class FeatureAutomotiveDetectorTest : LintDetectorTest() {
+    val explanation =
+        FeatureAutomotiveDetector.EXPLANATION.replace("\\", "").replace("\n            ", "") +
+            " [UsingFeatureAutomotiveInCTS]"
+
+    override fun getDetector(): Detector = FeatureAutomotiveDetector()
+    override fun getIssues(): List<Issue> = listOf(FeatureAutomotiveDetector.ISSUE)
+    override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+    @Test
+    fun testWarning1() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+
+                    private void fun() {
+                        PackageManager.getInstance().hasSystemFeature(
+                            "android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                            src/android/content/pm/PackageManager.java:13: Warning: $explanation
+                public boolean hasSystemFeature(String feature) {
+                               ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning2() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+
+                    private void fun() {
+                        String featureName = "android.hardware.type.automotive";
+                        PackageManager.getInstance().hasSystemFeature(featureName);
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                            src/android/content/pm/PackageManager.java:13: Warning: $explanation
+                public boolean hasSystemFeature(String feature) {
+                               ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning3() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+
+                    private void fun() {
+                        PackageManager.getInstance().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                            src/android/content/pm/PackageManager.java:13: Warning: $explanation
+                public boolean hasSystemFeature(String feature) {
+                               ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning4() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+
+                    private void fun() {
+                        String featureName = PackageManager.FEATURE_AUTOMOTIVE;
+                        PackageManager.getInstance().hasSystemFeature(featureName);
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                            src/android/content/pm/PackageManager.java:13: Warning: $explanation
+                public boolean hasSystemFeature(String feature) {
+                               ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning5() {
+        lint()
+            .files(
+                java(
+                        """
+                import com.android.example.Utils;
+
+                public class Foo {
+
+                    private void fun() {
+                        Utils.hasFeature("android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                    src/com/android/example/Utils.java:7: Warning: $explanation
+                public static boolean hasFeature(String feature) {
+                                      ~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning6() {
+        lint()
+            .files(
+                java(
+                        """
+                import com.android.example.Utils;
+
+                public class Foo {
+
+                    private void fun() {
+                        Utils.hasDeviceFeature("android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                    src/com/android/example/Utils.java:11: Warning: $explanation
+                public static boolean hasDeviceFeature(String feature) {
+                                      ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning7() {
+        lint()
+            .files(
+                java(
+                        """
+                import com.android.example.Utils;
+
+                public class Foo {
+
+                    private void fun() {
+                        Utils.hasFeature(new Object(), "android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                    src/com/android/example/Utils.java:15: Warning: $explanation
+                public static boolean hasFeature(Object object, String feature) {
+                                      ~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning8() {
+        lint()
+            .files(
+                java(
+                        """
+                import com.android.example.Utils;
+
+                public class Foo {
+
+                    private void fun() {
+                        Utils.bypassTestForFeatures("android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                    src/com/android/example/Utils.java:19: Warning: $explanation
+                public static boolean bypassTestForFeatures(String feature) {
+                                      ~~~~~~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testNoWarning() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+                    private void fun() {
+                        String featureName1 = "android.hardware.type.automotive";
+                        String featureName2 = PackageManager.FEATURE_AUTOMOTIVE;
+                        String notFeatureName = "FEATURE_AUTOMOTIVE";
+                        PackageManager.getInstance().hasSystemFeature(notFeatureName);
+                        /*
+                        PackageManager.getInstance().hasSystemFeature(
+                            "android.hardware.type.automotive");
+                         */
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    private val pmStub: TestFile =
+        java(
+            """
+        package android.content.pm;
+
+        import java.lang.String;
+
+        public class PackageManager {
+            public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+
+            public static PackageManager getInstance() {
+                return new PackageManager();
+            }
+
+            public boolean hasSystemFeature(String feature) {
+                return true;
+            }
+        }
+        """
+        )
+
+    private val exampleStub: TestFile =
+        java(
+            """
+        package com.android.example;
+
+        import java.lang.String;
+
+        public class Utils {
+            public static boolean hasFeature(String feature) {
+                return true;
+            }
+
+            public static boolean hasDeviceFeature(String feature) {
+                return true;
+            }
+
+            public static boolean hasFeature(Object object, String feature) {
+                return true;
+            }
+
+            public static boolean bypassTestForFeatures(String feature) {
+                return true;
+            }
+        }
+        """
+        )
+
+    private val stubs = arrayOf(pmStub, exampleStub)
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt
new file mode 100644
index 0000000..8777712
--- /dev/null
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt
@@ -0,0 +1,774 @@
+/*
+ * Copyright (C) 2024 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
+
+/**
+ * The exemptAidlInterfaces set was generated by running ExemptAidlInterfacesGenerator on the
+ * entire source tree. To reproduce the results, run generate-exempt-aidl-interfaces.sh
+ * located in tools/lint/utils.
+ *
+ * TODO: b/363248121 - Use the exemptAidlInterfaces set inside PermissionAnnotationDetector when it
+ * gets migrated to a global lint check.
+ */
+val exemptAidlInterfaces = setOf(
+    "android.accessibilityservice.IAccessibilityServiceConnection",
+    "android.accessibilityservice.IBrailleDisplayConnection",
+    "android.accounts.IAccountAuthenticatorResponse",
+    "android.accounts.IAccountManager",
+    "android.accounts.IAccountManagerResponse",
+    "android.adservices.adid.IAdIdProviderService",
+    "android.adservices.adid.IAdIdService",
+    "android.adservices.adid.IGetAdIdCallback",
+    "android.adservices.adid.IGetAdIdProviderCallback",
+    "android.adservices.adselection.AdSelectionCallback",
+    "android.adservices.adselection.AdSelectionOverrideCallback",
+    "android.adservices.adselection.AdSelectionService",
+    "android.adservices.adselection.GetAdSelectionDataCallback",
+    "android.adservices.adselection.PersistAdSelectionResultCallback",
+    "android.adservices.adselection.ReportImpressionCallback",
+    "android.adservices.adselection.ReportInteractionCallback",
+    "android.adservices.adselection.SetAppInstallAdvertisersCallback",
+    "android.adservices.adselection.UpdateAdCounterHistogramCallback",
+    "android.adservices.appsetid.IAppSetIdProviderService",
+    "android.adservices.appsetid.IAppSetIdService",
+    "android.adservices.appsetid.IGetAppSetIdCallback",
+    "android.adservices.appsetid.IGetAppSetIdProviderCallback",
+    "android.adservices.cobalt.IAdServicesCobaltUploadService",
+    "android.adservices.common.IAdServicesCommonCallback",
+    "android.adservices.common.IAdServicesCommonService",
+    "android.adservices.common.IAdServicesCommonStatesCallback",
+    "android.adservices.common.IEnableAdServicesCallback",
+    "android.adservices.common.IUpdateAdIdCallback",
+    "android.adservices.customaudience.CustomAudienceOverrideCallback",
+    "android.adservices.customaudience.FetchAndJoinCustomAudienceCallback",
+    "android.adservices.customaudience.ICustomAudienceCallback",
+    "android.adservices.customaudience.ICustomAudienceService",
+    "android.adservices.customaudience.ScheduleCustomAudienceUpdateCallback",
+    "android.adservices.extdata.IAdServicesExtDataStorageService",
+    "android.adservices.extdata.IGetAdServicesExtDataCallback",
+    "android.adservices.measurement.IMeasurementApiStatusCallback",
+    "android.adservices.measurement.IMeasurementCallback",
+    "android.adservices.measurement.IMeasurementService",
+    "android.adservices.ondevicepersonalization.aidl.IDataAccessService",
+    "android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback",
+    "android.adservices.ondevicepersonalization.aidl.IExecuteCallback",
+    "android.adservices.ondevicepersonalization.aidl.IFederatedComputeCallback",
+    "android.adservices.ondevicepersonalization.aidl.IFederatedComputeService",
+    "android.adservices.ondevicepersonalization.aidl.IIsolatedModelService",
+    "android.adservices.ondevicepersonalization.aidl.IIsolatedModelServiceCallback",
+    "android.adservices.ondevicepersonalization.aidl.IIsolatedService",
+    "android.adservices.ondevicepersonalization.aidl.IIsolatedServiceCallback",
+    "android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigService",
+    "android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigServiceCallback",
+    "android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationDebugService",
+    "android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService",
+    "android.adservices.ondevicepersonalization.aidl.IRegisterMeasurementEventCallback",
+    "android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback",
+    "android.adservices.shell.IShellCommand",
+    "android.adservices.shell.IShellCommandCallback",
+    "android.adservices.signals.IProtectedSignalsService",
+    "android.adservices.signals.UpdateSignalsCallback",
+    "android.adservices.topics.IGetTopicsCallback",
+    "android.adservices.topics.ITopicsService",
+    "android.app.admin.IDevicePolicyManager",
+    "android.app.adservices.IAdServicesManager",
+    "android.app.ambientcontext.IAmbientContextManager",
+    "android.app.ambientcontext.IAmbientContextObserver",
+    "android.app.appsearch.aidl.IAppFunctionService",
+    "android.app.appsearch.aidl.IAppSearchBatchResultCallback",
+    "android.app.appsearch.aidl.IAppSearchManager",
+    "android.app.appsearch.aidl.IAppSearchObserverProxy",
+    "android.app.appsearch.aidl.IAppSearchResultCallback",
+    "android.app.backup.IBackupCallback",
+    "android.app.backup.IBackupManager",
+    "android.app.backup.IRestoreSession",
+    "android.app.blob.IBlobCommitCallback",
+    "android.app.blob.IBlobStoreManager",
+    "android.app.blob.IBlobStoreSession",
+    "android.app.contentsuggestions.IContentSuggestionsManager",
+    "android.app.contextualsearch.IContextualSearchManager",
+    "android.app.ecm.IEnhancedConfirmationManager",
+    "android.apphibernation.IAppHibernationService",
+    "android.app.IActivityClientController",
+    "android.app.IActivityController",
+    "android.app.IActivityTaskManager",
+    "android.app.IAlarmCompleteListener",
+    "android.app.IAlarmListener",
+    "android.app.IAlarmManager",
+    "android.app.IApplicationThread",
+    "android.app.IAppTask",
+    "android.app.IAppTraceRetriever",
+    "android.app.IAssistDataReceiver",
+    "android.app.IForegroundServiceObserver",
+    "android.app.IGameManagerService",
+    "android.app.IGrammaticalInflectionManager",
+    "android.app.ILocaleManager",
+    "android.app.INotificationManager",
+    "android.app.IParcelFileDescriptorRetriever",
+    "android.app.IProcessObserver",
+    "android.app.ISearchManager",
+    "android.app.IStopUserCallback",
+    "android.app.ITaskStackListener",
+    "android.app.IUiModeManager",
+    "android.app.IUriGrantsManager",
+    "android.app.IUserSwitchObserver",
+    "android.app.IWallpaperManager",
+    "android.app.job.IJobCallback",
+    "android.app.job.IJobScheduler",
+    "android.app.job.IJobService",
+    "android.app.ondeviceintelligence.IDownloadCallback",
+    "android.app.ondeviceintelligence.IFeatureCallback",
+    "android.app.ondeviceintelligence.IFeatureDetailsCallback",
+    "android.app.ondeviceintelligence.IListFeaturesCallback",
+    "android.app.ondeviceintelligence.IOnDeviceIntelligenceManager",
+    "android.app.ondeviceintelligence.IProcessingSignal",
+    "android.app.ondeviceintelligence.IResponseCallback",
+    "android.app.ondeviceintelligence.IStreamingResponseCallback",
+    "android.app.ondeviceintelligence.ITokenInfoCallback",
+    "android.app.people.IPeopleManager",
+    "android.app.pinner.IPinnerService",
+    "android.app.prediction.IPredictionManager",
+    "android.app.role.IOnRoleHoldersChangedListener",
+    "android.app.role.IRoleController",
+    "android.app.role.IRoleManager",
+    "android.app.sdksandbox.ILoadSdkCallback",
+    "android.app.sdksandbox.IRequestSurfacePackageCallback",
+    "android.app.sdksandbox.ISdkSandboxManager",
+    "android.app.sdksandbox.ISdkSandboxProcessDeathCallback",
+    "android.app.sdksandbox.ISdkToServiceCallback",
+    "android.app.sdksandbox.ISharedPreferencesSyncCallback",
+    "android.app.sdksandbox.IUnloadSdkCallback",
+    "android.app.sdksandbox.testutils.testscenario.ISdkSandboxTestExecutor",
+    "android.app.search.ISearchUiManager",
+    "android.app.slice.ISliceManager",
+    "android.app.smartspace.ISmartspaceManager",
+    "android.app.timedetector.ITimeDetectorService",
+    "android.app.timezonedetector.ITimeZoneDetectorService",
+    "android.app.trust.ITrustManager",
+    "android.app.usage.IStorageStatsManager",
+    "android.app.usage.IUsageStatsManager",
+    "android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager",
+    "android.app.wearable.IWearableSensingCallback",
+    "android.app.wearable.IWearableSensingManager",
+    "android.bluetooth.IBluetooth",
+    "android.bluetooth.IBluetoothA2dp",
+    "android.bluetooth.IBluetoothA2dpSink",
+    "android.bluetooth.IBluetoothActivityEnergyInfoListener",
+    "android.bluetooth.IBluetoothAvrcpController",
+    "android.bluetooth.IBluetoothCallback",
+    "android.bluetooth.IBluetoothConnectionCallback",
+    "android.bluetooth.IBluetoothCsipSetCoordinator",
+    "android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback",
+    "android.bluetooth.IBluetoothGatt",
+    "android.bluetooth.IBluetoothGattCallback",
+    "android.bluetooth.IBluetoothGattServerCallback",
+    "android.bluetooth.IBluetoothHapClient",
+    "android.bluetooth.IBluetoothHapClientCallback",
+    "android.bluetooth.IBluetoothHeadset",
+    "android.bluetooth.IBluetoothHeadsetClient",
+    "android.bluetooth.IBluetoothHearingAid",
+    "android.bluetooth.IBluetoothHidDevice",
+    "android.bluetooth.IBluetoothHidDeviceCallback",
+    "android.bluetooth.IBluetoothHidHost",
+    "android.bluetooth.IBluetoothLeAudio",
+    "android.bluetooth.IBluetoothLeAudioCallback",
+    "android.bluetooth.IBluetoothLeBroadcastAssistant",
+    "android.bluetooth.IBluetoothLeBroadcastAssistantCallback",
+    "android.bluetooth.IBluetoothLeBroadcastCallback",
+    "android.bluetooth.IBluetoothLeCallControl",
+    "android.bluetooth.IBluetoothLeCallControlCallback",
+    "android.bluetooth.IBluetoothManager",
+    "android.bluetooth.IBluetoothManagerCallback",
+    "android.bluetooth.IBluetoothMap",
+    "android.bluetooth.IBluetoothMapClient",
+    "android.bluetooth.IBluetoothMcpServiceManager",
+    "android.bluetooth.IBluetoothMetadataListener",
+    "android.bluetooth.IBluetoothOobDataCallback",
+    "android.bluetooth.IBluetoothPan",
+    "android.bluetooth.IBluetoothPanCallback",
+    "android.bluetooth.IBluetoothPbap",
+    "android.bluetooth.IBluetoothPbapClient",
+    "android.bluetooth.IBluetoothPreferredAudioProfilesCallback",
+    "android.bluetooth.IBluetoothQualityReportReadyCallback",
+    "android.bluetooth.IBluetoothSap",
+    "android.bluetooth.IBluetoothScan",
+    "android.bluetooth.IBluetoothSocketManager",
+    "android.bluetooth.IBluetoothVolumeControl",
+    "android.bluetooth.IBluetoothVolumeControlCallback",
+    "android.bluetooth.le.IAdvertisingSetCallback",
+    "android.bluetooth.le.IDistanceMeasurementCallback",
+    "android.bluetooth.le.IPeriodicAdvertisingCallback",
+    "android.bluetooth.le.IScannerCallback",
+    "android.companion.ICompanionDeviceManager",
+    "android.companion.IOnMessageReceivedListener",
+    "android.companion.IOnTransportsChangedListener",
+    "android.companion.virtualcamera.IVirtualCameraCallback",
+    "android.companion.virtual.IVirtualDevice",
+    "android.companion.virtual.IVirtualDeviceManager",
+    "android.companion.virtualnative.IVirtualDeviceManagerNative",
+    "android.content.IClipboard",
+    "android.content.IContentService",
+    "android.content.IIntentReceiver",
+    "android.content.IIntentSender",
+    "android.content.integrity.IAppIntegrityManager",
+    "android.content.IRestrictionsManager",
+    "android.content.ISyncAdapterUnsyncableAccountCallback",
+    "android.content.ISyncContext",
+    "android.content.om.IOverlayManager",
+    "android.content.pm.dex.IArtManager",
+    "android.content.pm.dex.ISnapshotRuntimeProfileCallback",
+    "android.content.pm.IBackgroundInstallControlService",
+    "android.content.pm.ICrossProfileApps",
+    "android.content.pm.IDataLoaderManager",
+    "android.content.pm.IDataLoaderStatusListener",
+    "android.content.pm.ILauncherApps",
+    "android.content.pm.IOnChecksumsReadyListener",
+    "android.content.pm.IOtaDexopt",
+    "android.content.pm.IPackageDataObserver",
+    "android.content.pm.IPackageDeleteObserver",
+    "android.content.pm.IPackageInstaller",
+    "android.content.pm.IPackageInstallerSession",
+    "android.content.pm.IPackageInstallerSessionFileSystemConnector",
+    "android.content.pm.IPackageInstallObserver2",
+    "android.content.pm.IPackageLoadingProgressCallback",
+    "android.content.pm.IPackageManager",
+    "android.content.pm.IPackageManagerNative",
+    "android.content.pm.IPackageMoveObserver",
+    "android.content.pm.IPinItemRequest",
+    "android.content.pm.IShortcutService",
+    "android.content.pm.IStagedApexObserver",
+    "android.content.pm.verify.domain.IDomainVerificationManager",
+    "android.content.res.IResourcesManager",
+    "android.content.rollback.IRollbackManager",
+    "android.credentials.ICredentialManager",
+    "android.debug.IAdbTransport",
+    "android.devicelock.IDeviceLockService",
+    "android.devicelock.IGetDeviceIdCallback",
+    "android.devicelock.IGetKioskAppsCallback",
+    "android.devicelock.IIsDeviceLockedCallback",
+    "android.devicelock.IVoidResultCallback",
+    "android.federatedcompute.aidl.IExampleStoreCallback",
+    "android.federatedcompute.aidl.IExampleStoreIterator",
+    "android.federatedcompute.aidl.IExampleStoreIteratorCallback",
+    "android.federatedcompute.aidl.IExampleStoreService",
+    "android.federatedcompute.aidl.IFederatedComputeCallback",
+    "android.federatedcompute.aidl.IFederatedComputeService",
+    "android.federatedcompute.aidl.IResultHandlingService",
+    "android.flags.IFeatureFlags",
+    "android.frameworks.location.altitude.IAltitudeService",
+    "android.frameworks.vibrator.IVibratorController",
+    "android.frameworks.vibrator.IVibratorControlService",
+    "android.gsi.IGsiServiceCallback",
+    "android.hardware.biometrics.AuthenticationStateListener",
+    "android.hardware.biometrics.common.ICancellationSignal",
+    "android.hardware.biometrics.face.IFace",
+    "android.hardware.biometrics.face.ISession",
+    "android.hardware.biometrics.face.ISessionCallback",
+    "android.hardware.biometrics.fingerprint.IFingerprint",
+    "android.hardware.biometrics.fingerprint.ISession",
+    "android.hardware.biometrics.fingerprint.ISessionCallback",
+    "android.hardware.biometrics.IAuthService",
+    "android.hardware.biometrics.IBiometricAuthenticator",
+    "android.hardware.biometrics.IBiometricContextListener",
+    "android.hardware.biometrics.IBiometricSensorReceiver",
+    "android.hardware.biometrics.IBiometricService",
+    "android.hardware.biometrics.IBiometricStateListener",
+    "android.hardware.biometrics.IBiometricSysuiReceiver",
+    "android.hardware.biometrics.IInvalidationCallback",
+    "android.hardware.biometrics.ITestSession",
+    "android.hardware.broadcastradio.IAnnouncementListener",
+    "android.hardware.broadcastradio.ITunerCallback",
+    "android.hardware.contexthub.IContextHubCallback",
+    "android.hardware.devicestate.IDeviceStateManager",
+    "android.hardware.display.IColorDisplayManager",
+    "android.hardware.display.IDisplayManager",
+    "android.hardware.face.IFaceAuthenticatorsRegisteredCallback",
+    "android.hardware.face.IFaceService",
+    "android.hardware.face.IFaceServiceReceiver",
+    "android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback",
+    "android.hardware.fingerprint.IFingerprintClientActiveCallback",
+    "android.hardware.fingerprint.IFingerprintService",
+    "android.hardware.fingerprint.IFingerprintServiceReceiver",
+    "android.hardware.fingerprint.IUdfpsOverlayControllerCallback",
+    "android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback",
+    "android.hardware.hdmi.IHdmiControlCallback",
+    "android.hardware.hdmi.IHdmiControlService",
+    "android.hardware.hdmi.IHdmiDeviceEventListener",
+    "android.hardware.hdmi.IHdmiHotplugEventListener",
+    "android.hardware.hdmi.IHdmiSystemAudioModeChangeListener",
+    "android.hardware.health.IHealthInfoCallback",
+    "android.hardware.ICameraServiceProxy",
+    "android.hardware.IConsumerIrService",
+    "android.hardware.input.IInputManager",
+    "android.hardware.iris.IIrisService",
+    "android.hardware.ISensorPrivacyManager",
+    "android.hardware.ISerialManager",
+    "android.hardware.lights.ILightsManager",
+    "android.hardware.location.IContextHubClient",
+    "android.hardware.location.IContextHubClientCallback",
+    "android.hardware.location.IContextHubService",
+    "android.hardware.location.IContextHubTransactionCallback",
+    "android.hardware.location.ISignificantPlaceProviderManager",
+    "android.hardware.radio.IAnnouncementListener",
+    "android.hardware.radio.ICloseHandle",
+    "android.hardware.radio.ims.media.IImsMedia",
+    "android.hardware.radio.ims.media.IImsMediaListener",
+    "android.hardware.radio.ims.media.IImsMediaSession",
+    "android.hardware.radio.ims.media.IImsMediaSessionListener",
+    "android.hardware.radio.IRadioService",
+    "android.hardware.radio.ITuner",
+    "android.hardware.radio.sap.ISapCallback",
+    "android.hardware.soundtrigger3.ISoundTriggerHw",
+    "android.hardware.soundtrigger3.ISoundTriggerHwCallback",
+    "android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback",
+    "android.hardware.soundtrigger.IRecognitionStatusCallback",
+    "android.hardware.tetheroffload.ITetheringOffloadCallback",
+    "android.hardware.thermal.IThermalChangedCallback",
+    "android.hardware.tv.hdmi.cec.IHdmiCecCallback",
+    "android.hardware.tv.hdmi.connection.IHdmiConnectionCallback",
+    "android.hardware.tv.hdmi.earc.IEArcCallback",
+    "android.hardware.usb.gadget.IUsbGadgetCallback",
+    "android.hardware.usb.IUsbCallback",
+    "android.hardware.usb.IUsbManager",
+    "android.hardware.usb.IUsbSerialReader",
+    "android.hardware.wifi.hostapd.IHostapdCallback",
+    "android.hardware.wifi.IWifiChipEventCallback",
+    "android.hardware.wifi.IWifiEventCallback",
+    "android.hardware.wifi.IWifiNanIfaceEventCallback",
+    "android.hardware.wifi.IWifiRttControllerEventCallback",
+    "android.hardware.wifi.IWifiStaIfaceEventCallback",
+    "android.hardware.wifi.supplicant.INonStandardCertCallback",
+    "android.hardware.wifi.supplicant.ISupplicantP2pIfaceCallback",
+    "android.hardware.wifi.supplicant.ISupplicantStaIfaceCallback",
+    "android.hardware.wifi.supplicant.ISupplicantStaNetworkCallback",
+    "android.health.connect.aidl.IAccessLogsResponseCallback",
+    "android.health.connect.aidl.IActivityDatesResponseCallback",
+    "android.health.connect.aidl.IAggregateRecordsResponseCallback",
+    "android.health.connect.aidl.IApplicationInfoResponseCallback",
+    "android.health.connect.aidl.IChangeLogsResponseCallback",
+    "android.health.connect.aidl.IDataStagingFinishedCallback",
+    "android.health.connect.aidl.IEmptyResponseCallback",
+    "android.health.connect.aidl.IGetChangeLogTokenCallback",
+    "android.health.connect.aidl.IGetHealthConnectDataStateCallback",
+    "android.health.connect.aidl.IGetHealthConnectMigrationUiStateCallback",
+    "android.health.connect.aidl.IGetPriorityResponseCallback",
+    "android.health.connect.aidl.IHealthConnectService",
+    "android.health.connect.aidl.IInsertRecordsResponseCallback",
+    "android.health.connect.aidl.IMedicalDataSourceResponseCallback",
+    "android.health.connect.aidl.IMedicalResourcesResponseCallback",
+    "android.health.connect.aidl.IMigrationCallback",
+    "android.health.connect.aidl.IReadMedicalResourcesResponseCallback",
+    "android.health.connect.aidl.IReadRecordsResponseCallback",
+    "android.health.connect.aidl.IRecordTypeInfoResponseCallback",
+    "android.health.connect.exportimport.IImportStatusCallback",
+    "android.health.connect.exportimport.IQueryDocumentProvidersCallback",
+    "android.health.connect.exportimport.IScheduledExportStatusCallback",
+    "android.location.ICountryDetector",
+    "android.location.IGpsGeofenceHardware",
+    "android.location.ILocationManager",
+    "android.location.provider.ILocationProviderManager",
+    "android.media.IAudioRoutesObserver",
+    "android.media.IMediaCommunicationService",
+    "android.media.IMediaCommunicationServiceCallback",
+    "android.media.IMediaController2",
+    "android.media.IMediaRoute2ProviderServiceCallback",
+    "android.media.IMediaRouterService",
+    "android.media.IMediaSession2",
+    "android.media.IMediaSession2Service",
+    "android.media.INativeSpatializerCallback",
+    "android.media.IPlaybackConfigDispatcher",
+    "android.media.IRecordingConfigDispatcher",
+    "android.media.IRemoteDisplayCallback",
+    "android.media.ISoundDoseCallback",
+    "android.media.ISpatializerHeadTrackingCallback",
+    "android.media.ITranscodingClientCallback",
+    "android.media.metrics.IMediaMetricsManager",
+    "android.media.midi.IMidiManager",
+    "android.media.musicrecognition.IMusicRecognitionAttributionTagCallback",
+    "android.media.musicrecognition.IMusicRecognitionManager",
+    "android.media.musicrecognition.IMusicRecognitionServiceCallback",
+    "android.media.projection.IMediaProjection",
+    "android.media.projection.IMediaProjectionCallback",
+    "android.media.projection.IMediaProjectionManager",
+    "android.media.projection.IMediaProjectionWatcherCallback",
+    "android.media.session.ISession",
+    "android.media.session.ISessionController",
+    "android.media.session.ISessionManager",
+    "android.media.soundtrigger.ISoundTriggerDetectionServiceClient",
+    "android.media.soundtrigger_middleware.IInjectGlobalEvent",
+    "android.media.soundtrigger_middleware.IInjectModelEvent",
+    "android.media.soundtrigger_middleware.IInjectRecognitionEvent",
+    "android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService",
+    "android.media.soundtrigger_middleware.ISoundTriggerModule",
+    "android.media.tv.ad.ITvAdManager",
+    "android.media.tv.ad.ITvAdSessionCallback",
+    "android.media.tv.interactive.ITvInteractiveAppManager",
+    "android.media.tv.interactive.ITvInteractiveAppServiceCallback",
+    "android.media.tv.interactive.ITvInteractiveAppSessionCallback",
+    "android.media.tv.ITvInputHardware",
+    "android.media.tv.ITvInputManager",
+    "android.media.tv.ITvInputServiceCallback",
+    "android.media.tv.ITvInputSessionCallback",
+    "android.media.tv.ITvRemoteServiceInput",
+    "android.nearby.aidl.IOffloadCallback",
+    "android.nearby.IBroadcastListener",
+    "android.nearby.INearbyManager",
+    "android.nearby.IScanListener",
+    "android.net.connectivity.aidl.ConnectivityNative",
+    "android.net.dhcp.IDhcpEventCallbacks",
+    "android.net.dhcp.IDhcpServer",
+    "android.net.dhcp.IDhcpServerCallbacks",
+    "android.net.ICaptivePortal",
+    "android.net.IConnectivityDiagnosticsCallback",
+    "android.net.IConnectivityManager",
+    "android.net.IEthernetManager",
+    "android.net.IEthernetServiceListener",
+    "android.net.IIntResultListener",
+    "android.net.IIpConnectivityMetrics",
+    "android.net.IIpMemoryStore",
+    "android.net.IIpMemoryStoreCallbacks",
+    "android.net.IIpSecService",
+    "android.net.INetdEventCallback",
+    "android.net.INetdUnsolicitedEventListener",
+    "android.net.INetworkActivityListener",
+    "android.net.INetworkAgent",
+    "android.net.INetworkAgentRegistry",
+    "android.net.INetworkInterfaceOutcomeReceiver",
+    "android.net.INetworkManagementEventObserver",
+    "android.net.INetworkMonitor",
+    "android.net.INetworkMonitorCallbacks",
+    "android.net.INetworkOfferCallback",
+    "android.net.INetworkPolicyListener",
+    "android.net.INetworkPolicyManager",
+    "android.net.INetworkScoreService",
+    "android.net.INetworkStackConnector",
+    "android.net.INetworkStackStatusCallback",
+    "android.net.INetworkStatsService",
+    "android.net.INetworkStatsSession",
+    "android.net.IOnCompleteListener",
+    "android.net.IPacProxyManager",
+    "android.net.ip.IIpClient",
+    "android.net.ip.IIpClientCallbacks",
+    "android.net.ipmemorystore.IOnBlobRetrievedListener",
+    "android.net.ipmemorystore.IOnL2KeyResponseListener",
+    "android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener",
+    "android.net.ipmemorystore.IOnSameL3NetworkResponseListener",
+    "android.net.ipmemorystore.IOnStatusAndCountListener",
+    "android.net.ipmemorystore.IOnStatusListener",
+    "android.net.IQosCallback",
+    "android.net.ISocketKeepaliveCallback",
+    "android.net.ITestNetworkManager",
+    "android.net.ITetheredInterfaceCallback",
+    "android.net.ITetheringConnector",
+    "android.net.ITetheringEventCallback",
+    "android.net.IVpnManager",
+    "android.net.mdns.aidl.IMDnsEventListener",
+    "android.net.metrics.INetdEventListener",
+    "android.net.netstats.IUsageCallback",
+    "android.net.netstats.provider.INetworkStatsProvider",
+    "android.net.netstats.provider.INetworkStatsProviderCallback",
+    "android.net.nsd.INsdManager",
+    "android.net.nsd.INsdManagerCallback",
+    "android.net.nsd.INsdServiceConnector",
+    "android.net.nsd.IOffloadEngine",
+    "android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener",
+    "android.net.thread.IActiveOperationalDatasetReceiver",
+    "android.net.thread.IConfigurationReceiver",
+    "android.net.thread.IOperationalDatasetCallback",
+    "android.net.thread.IOperationReceiver",
+    "android.net.thread.IStateCallback",
+    "android.net.thread.IThreadNetworkController",
+    "android.net.thread.IThreadNetworkManager",
+    "android.net.vcn.IVcnManagementService",
+    "android.net.wear.ICompanionDeviceManagerProxy",
+    "android.net.wifi.aware.IWifiAwareDiscoverySessionCallback",
+    "android.net.wifi.aware.IWifiAwareEventCallback",
+    "android.net.wifi.aware.IWifiAwareMacAddressProvider",
+    "android.net.wifi.aware.IWifiAwareManager",
+    "android.net.wifi.hotspot2.IProvisioningCallback",
+    "android.net.wifi.IActionListener",
+    "android.net.wifi.IBooleanListener",
+    "android.net.wifi.IByteArrayListener",
+    "android.net.wifi.ICoexCallback",
+    "android.net.wifi.IDppCallback",
+    "android.net.wifi.IIntegerListener",
+    "android.net.wifi.IInterfaceCreationInfoCallback",
+    "android.net.wifi.ILastCallerListener",
+    "android.net.wifi.IListListener",
+    "android.net.wifi.ILocalOnlyConnectionStatusListener",
+    "android.net.wifi.ILocalOnlyHotspotCallback",
+    "android.net.wifi.IMacAddressListListener",
+    "android.net.wifi.IMapListener",
+    "android.net.wifi.INetworkRequestMatchCallback",
+    "android.net.wifi.INetworkRequestUserSelectionCallback",
+    "android.net.wifi.IOnWifiActivityEnergyInfoListener",
+    "android.net.wifi.IOnWifiDriverCountryCodeChangedListener",
+    "android.net.wifi.IOnWifiUsabilityStatsListener",
+    "android.net.wifi.IPnoScanResultsCallback",
+    "android.net.wifi.IScanDataListener",
+    "android.net.wifi.IScanResultsCallback",
+    "android.net.wifi.IScoreUpdateObserver",
+    "android.net.wifi.ISoftApCallback",
+    "android.net.wifi.IStringListener",
+    "android.net.wifi.ISubsystemRestartCallback",
+    "android.net.wifi.ISuggestionConnectionStatusListener",
+    "android.net.wifi.ISuggestionUserApprovalStatusListener",
+    "android.net.wifi.ITrafficStateCallback",
+    "android.net.wifi.ITwtCallback",
+    "android.net.wifi.ITwtCapabilitiesListener",
+    "android.net.wifi.ITwtStatsListener",
+    "android.net.wifi.IWifiBandsListener",
+    "android.net.wifi.IWifiConnectedNetworkScorer",
+    "android.net.wifi.IWifiLowLatencyLockListener",
+    "android.net.wifi.IWifiManager",
+    "android.net.wifi.IWifiNetworkSelectionConfigListener",
+    "android.net.wifi.IWifiNetworkStateChangedListener",
+    "android.net.wifi.IWifiScanner",
+    "android.net.wifi.IWifiScannerListener",
+    "android.net.wifi.IWifiVerboseLoggingStatusChangedListener",
+    "android.net.wifi.p2p.IWifiP2pListener",
+    "android.net.wifi.p2p.IWifiP2pManager",
+    "android.net.wifi.rtt.IRttCallback",
+    "android.net.wifi.rtt.IWifiRttManager",
+    "android.ondevicepersonalization.IOnDevicePersonalizationSystemService",
+    "android.ondevicepersonalization.IOnDevicePersonalizationSystemServiceCallback",
+    "android.os.IBatteryPropertiesRegistrar",
+    "android.os.ICancellationSignal",
+    "android.os.IDeviceIdentifiersPolicyService",
+    "android.os.IDeviceIdleController",
+    "android.os.IDumpstate",
+    "android.os.IDumpstateListener",
+    "android.os.IExternalVibratorService",
+    "android.os.IHardwarePropertiesManager",
+    "android.os.IHintManager",
+    "android.os.IHintSession",
+    "android.os.IIncidentCompanion",
+    "android.os.image.IDynamicSystemService",
+    "android.os.incremental.IStorageHealthListener",
+    "android.os.INetworkManagementService",
+    "android.os.IPendingIntentRef",
+    "android.os.IPowerStatsService",
+    "android.os.IProfilingResultCallback",
+    "android.os.IProfilingService",
+    "android.os.IProgressListener",
+    "android.os.IPullAtomCallback",
+    "android.os.IRecoverySystem",
+    "android.os.IRemoteCallback",
+    "android.os.ISecurityStateManager",
+    "android.os.IServiceCallback",
+    "android.os.IStatsCompanionService",
+    "android.os.IStatsManagerService",
+    "android.os.IStatsQueryCallback",
+    "android.os.ISystemConfig",
+    "android.os.ISystemUpdateManager",
+    "android.os.IThermalEventListener",
+    "android.os.IUpdateLock",
+    "android.os.IUserManager",
+    "android.os.IUserRestrictionsListener",
+    "android.os.IVibratorManagerService",
+    "android.os.IVoldListener",
+    "android.os.IVoldMountCallback",
+    "android.os.IVoldTaskListener",
+    "android.os.logcat.ILogcatManagerService",
+    "android.permission.ILegacyPermissionManager",
+    "android.permission.IPermissionChecker",
+    "android.permission.IPermissionManager",
+    "android.print.IPrintManager",
+    "android.print.IPrintSpoolerCallbacks",
+    "android.print.IPrintSpoolerClient",
+    "android.printservice.IPrintServiceClient",
+    "android.printservice.recommendation.IRecommendationServiceCallbacks",
+    "android.provider.aidl.IDeviceConfigManager",
+    "android.remoteauth.IDeviceDiscoveryListener",
+    "android.safetycenter.IOnSafetyCenterDataChangedListener",
+    "android.safetycenter.ISafetyCenterManager",
+    "android.scheduling.IRebootReadinessManager",
+    "android.scheduling.IRequestRebootReadinessStatusListener",
+    "android.security.attestationverification.IAttestationVerificationManagerService",
+    "android.security.IFileIntegrityService",
+    "android.security.keystore.IKeyAttestationApplicationIdProvider",
+    "android.security.rkp.IRegistration",
+    "android.security.rkp.IRemoteProvisioning",
+    "android.service.appprediction.IPredictionService",
+    "android.service.assist.classification.IFieldClassificationCallback",
+    "android.service.attention.IAttentionCallback",
+    "android.service.attention.IProximityUpdateCallback",
+    "android.service.autofill.augmented.IFillCallback",
+    "android.service.autofill.IConvertCredentialCallback",
+    "android.service.autofill.IFillCallback",
+    "android.service.autofill.IInlineSuggestionUiCallback",
+    "android.service.autofill.ISaveCallback",
+    "android.service.autofill.ISurfacePackageResultCallback",
+    "android.service.contentcapture.IContentCaptureServiceCallback",
+    "android.service.contentcapture.IContentProtectionAllowlistCallback",
+    "android.service.contentcapture.IDataShareCallback",
+    "android.service.credentials.IBeginCreateCredentialCallback",
+    "android.service.credentials.IBeginGetCredentialCallback",
+    "android.service.credentials.IClearCredentialStateCallback",
+    "android.service.dreams.IDreamManager",
+    "android.service.games.IGameServiceController",
+    "android.service.games.IGameSessionController",
+    "android.service.notification.IStatusBarNotificationHolder",
+    "android.service.oemlock.IOemLockService",
+    "android.service.ondeviceintelligence.IProcessingUpdateStatusCallback",
+    "android.service.ondeviceintelligence.IRemoteProcessingService",
+    "android.service.ondeviceintelligence.IRemoteStorageService",
+    "android.service.persistentdata.IPersistentDataBlockService",
+    "android.service.resolver.IResolverRankerResult",
+    "android.service.rotationresolver.IRotationResolverCallback",
+    "android.service.textclassifier.ITextClassifierCallback",
+    "android.service.textclassifier.ITextClassifierService",
+    "android.service.timezone.ITimeZoneProviderManager",
+    "android.service.trust.ITrustAgentServiceCallback",
+    "android.service.voice.IDetectorSessionStorageService",
+    "android.service.voice.IDetectorSessionVisualQueryDetectionCallback",
+    "android.service.voice.IDspHotwordDetectionCallback",
+    "android.service.wallpaper.IWallpaperConnection",
+    "android.speech.IRecognitionListener",
+    "android.speech.IRecognitionService",
+    "android.speech.IRecognitionServiceManager",
+    "android.speech.tts.ITextToSpeechManager",
+    "android.speech.tts.ITextToSpeechSession",
+    "android.system.composd.ICompilationTaskCallback",
+    "android.system.virtualizationmaintenance.IVirtualizationReconciliationCallback",
+    "android.system.virtualizationservice.IVirtualMachineCallback",
+    "android.system.vmtethering.IVmTethering",
+    "android.telephony.imsmedia.IImsAudioSession",
+    "android.telephony.imsmedia.IImsAudioSessionCallback",
+    "android.telephony.imsmedia.IImsMedia",
+    "android.telephony.imsmedia.IImsMediaCallback",
+    "android.telephony.imsmedia.IImsTextSession",
+    "android.telephony.imsmedia.IImsTextSessionCallback",
+    "android.telephony.imsmedia.IImsVideoSession",
+    "android.telephony.imsmedia.IImsVideoSessionCallback",
+    "android.tracing.ITracingServiceProxy",
+    "android.uwb.IOnUwbActivityEnergyInfoListener",
+    "android.uwb.IUwbAdapter",
+    "android.uwb.IUwbAdapterStateCallbacks",
+    "android.uwb.IUwbAdfProvisionStateCallbacks",
+    "android.uwb.IUwbOemExtensionCallback",
+    "android.uwb.IUwbRangingCallbacks",
+    "android.uwb.IUwbVendorUciCallback",
+    "android.view.accessibility.IAccessibilityInteractionConnectionCallback",
+    "android.view.accessibility.IAccessibilityManager",
+    "android.view.accessibility.IMagnificationConnectionCallback",
+    "android.view.accessibility.IRemoteMagnificationAnimationCallback",
+    "android.view.autofill.IAutoFillManager",
+    "android.view.autofill.IAutofillWindowPresenter",
+    "android.view.contentcapture.IContentCaptureManager",
+    "android.view.IDisplayChangeWindowCallback",
+    "android.view.IDisplayWindowListener",
+    "android.view.IInputFilter",
+    "android.view.IInputFilterHost",
+    "android.view.IInputMonitorHost",
+    "android.view.IRecentsAnimationController",
+    "android.view.IRemoteAnimationFinishedCallback",
+    "android.view.ISensitiveContentProtectionManager",
+    "android.view.IWindowId",
+    "android.view.IWindowManager",
+    "android.view.IWindowSession",
+    "android.view.translation.ITranslationManager",
+    "android.view.translation.ITranslationServiceCallback",
+    "android.webkit.IWebViewUpdateService",
+    "android.window.IBackAnimationFinishedCallback",
+    "android.window.IDisplayAreaOrganizerController",
+    "android.window.ITaskFragmentOrganizerController",
+    "android.window.ITaskOrganizerController",
+    "android.window.ITransitionMetricsReporter",
+    "android.window.IUnhandledDragCallback",
+    "android.window.IWindowContainerToken",
+    "android.window.IWindowlessStartingSurfaceCallback",
+    "android.window.IWindowOrganizerController",
+    "androidx.core.uwb.backend.IUwb",
+    "androidx.core.uwb.backend.IUwbClient",
+    "com.android.clockwork.modes.IModeManager",
+    "com.android.clockwork.modes.IStateChangeListener",
+    "com.android.clockwork.power.IWearPowerService",
+    "com.android.devicelockcontroller.IDeviceLockControllerService",
+    "com.android.devicelockcontroller.storage.IGlobalParametersService",
+    "com.android.devicelockcontroller.storage.ISetupParametersService",
+    "com.android.federatedcompute.services.training.aidl.IIsolatedTrainingService",
+    "com.android.federatedcompute.services.training.aidl.ITrainingResultCallback",
+    "com.android.internal.app.IAppOpsActiveCallback",
+    "com.android.internal.app.ILogAccessDialogCallback",
+    "com.android.internal.app.ISoundTriggerService",
+    "com.android.internal.app.ISoundTriggerSession",
+    "com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener",
+    "com.android.internal.app.IVoiceInteractionManagerService",
+    "com.android.internal.app.IVoiceInteractionSessionListener",
+    "com.android.internal.app.IVoiceInteractionSessionShowCallback",
+    "com.android.internal.app.IVoiceInteractionSoundTriggerSession",
+    "com.android.internal.app.procstats.IProcessStats",
+    "com.android.internal.appwidget.IAppWidgetService",
+    "com.android.internal.backup.ITransportStatusCallback",
+    "com.android.internal.compat.IOverrideValidator",
+    "com.android.internal.compat.IPlatformCompat",
+    "com.android.internal.compat.IPlatformCompatNative",
+    "com.android.internal.graphics.fonts.IFontManager",
+    "com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback",
+    "com.android.internal.inputmethod.IConnectionlessHandwritingCallback",
+    "com.android.internal.inputmethod.IImeTracker",
+    "com.android.internal.inputmethod.IInlineSuggestionsRequestCallback",
+    "com.android.internal.inputmethod.IInputContentUriToken",
+    "com.android.internal.inputmethod.IInputMethodPrivilegedOperations",
+    "com.android.internal.inputmethod.IInputMethodSessionCallback",
+    "com.android.internal.net.INetworkWatchlistManager",
+    "com.android.internal.os.IBinaryTransparencyService",
+    "com.android.internal.os.IDropBoxManagerService",
+    "com.android.internal.policy.IKeyguardDismissCallback",
+    "com.android.internal.policy.IKeyguardDrawnCallback",
+    "com.android.internal.policy.IKeyguardExitCallback",
+    "com.android.internal.policy.IKeyguardStateCallback",
+    "com.android.internal.statusbar.IAddTileResultCallback",
+    "com.android.internal.statusbar.ISessionListener",
+    "com.android.internal.statusbar.IStatusBarService",
+    "com.android.internal.telecom.IDeviceIdleControllerAdapter",
+    "com.android.internal.telecom.IInternalServiceRetriever",
+    "com.android.internal.telephony.IMms",
+    "com.android.internal.telephony.ITelephonyRegistry",
+    "com.android.internal.textservice.ISpellCheckerServiceCallback",
+    "com.android.internal.textservice.ITextServicesManager",
+    "com.android.internal.view.IDragAndDropPermissions",
+    "com.android.internal.view.IInputMethodManager",
+    "com.android.internal.view.inline.IInlineContentProvider",
+    "com.android.internal.widget.ILockSettings",
+    "com.android.net.IProxyPortListener",
+    "com.android.net.module.util.IRoutingCoordinator",
+    "com.android.ondevicepersonalization.libraries.plugin.internal.IPluginCallback",
+    "com.android.ondevicepersonalization.libraries.plugin.internal.IPluginExecutorService",
+    "com.android.ondevicepersonalization.libraries.plugin.internal.IPluginStateCallback",
+    "com.android.rkpdapp.IGetKeyCallback",
+    "com.android.rkpdapp.IGetRegistrationCallback",
+    "com.android.rkpdapp.IRegistration",
+    "com.android.rkpdapp.IRemoteProvisioning",
+    "com.android.rkpdapp.IStoreUpgradedKeyCallback",
+    "com.android.sdksandbox.IComputeSdkStorageCallback",
+    "com.android.sdksandbox.ILoadSdkInSandboxCallback",
+    "com.android.sdksandbox.IRequestSurfacePackageFromSdkCallback",
+    "com.android.sdksandbox.ISdkSandboxManagerToSdkSandboxCallback",
+    "com.android.sdksandbox.ISdkSandboxService",
+    "com.android.sdksandbox.IUnloadSdkInSandboxCallback",
+    "com.android.server.profcollect.IProviderStatusCallback",
+    "com.android.server.thread.openthread.IChannelMasksReceiver",
+    "com.android.server.thread.openthread.INsdPublisher",
+    "com.android.server.thread.openthread.IOtDaemonCallback",
+    "com.android.server.thread.openthread.IOtStatusReceiver",
+    "com.google.android.clockwork.ambient.offload.IDisplayOffloadService",
+    "com.google.android.clockwork.ambient.offload.IDisplayOffloadTransitionFinishedCallbacks",
+    "com.google.android.clockwork.healthservices.IHealthService",
+    "vendor.google_clockwork.healthservices.IHealthServicesCallback",
+)
diff --git a/tools/lint/utils/README.md b/tools/lint/utils/README.md
new file mode 100644
index 0000000..b5583c5
--- /dev/null
+++ b/tools/lint/utils/README.md
@@ -0,0 +1,11 @@
+# Utility Android Lint Checks for AOSP
+
+This directory contains scripts that execute utility Android Lint Checks for AOSP, specifically:
+* `enforce_permission_counter.py`: Provides statistics regarding the percentage of annotated/not
+  annotated `AIDL` methods with `@EnforcePermission` annotations.
+* `generate-exempt-aidl-interfaces.sh`: Provides a list of all `AIDL` interfaces in the entire
+  source tree.
+
+When adding a new utility Android Lint check to this directory, consider adding any utility or
+data processing tool you might require. Make sure that your contribution is documented in this
+README file.
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
index fa61c42..9842881 100644
--- 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
@@ -19,6 +19,7 @@
 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.ExemptAidlInterfacesGenerator
 import com.google.android.lint.aidl.AnnotatedAidlCounter
 import com.google.auto.service.AutoService
 
@@ -27,6 +28,7 @@
 class AndroidUtilsIssueRegistry : IssueRegistry() {
     override val issues = listOf(
         AnnotatedAidlCounter.ISSUE_ANNOTATED_AIDL_COUNTER,
+        ExemptAidlInterfacesGenerator.ISSUE_PERMISSION_ANNOTATION_EXEMPT_AIDL_INTERFACES,
     )
 
     override val api: Int
@@ -38,6 +40,6 @@
     override val vendor: Vendor = Vendor(
         vendorName = "Android",
         feedbackUrl = "http://b/issues/new?component=315013",
-        contact = "[email protected]"
+        contact = "[email protected]"
     )
 }
diff --git a/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfacesGenerator.kt b/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfacesGenerator.kt
new file mode 100644
index 0000000..6ad223c
--- /dev/null
+++ b/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfacesGenerator.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 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.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UMethod
+
+/**
+ * Generates a set of fully qualified AIDL Interface names present in the entire source tree with
+ * the following requirement: their implementations have to be inside directories whose path
+ * prefixes match `systemServicePathPrefixes`.
+ */
+class ExemptAidlInterfacesGenerator : AidlImplementationDetector() {
+    private val targetExemptAidlInterfaceNames = mutableSetOf<String>()
+    private val systemServicePathPrefixes = setOf(
+        "frameworks/base/services",
+        "frameworks/base/apex",
+        "frameworks/opt/wear",
+        "packages/modules"
+    )
+
+    // We could've improved performance by visiting classes rather than methods, however, this lint
+    // check won't be run regularly, hence we've decided not to add extra overrides to
+    // AidlImplementationDetector.
+    override fun visitAidlMethod(
+        context: JavaContext,
+        node: UMethod,
+        interfaceName: String,
+        body: UBlockExpression
+    ) {
+        val filePath = context.file.path
+
+        // We perform `filePath.contains` instead of `filePath.startsWith` since getting the
+        // relative path of a source file is non-trivial. That is because `context.file.path`
+        // returns the path to where soong builds the file (i.e. /out/soong/...). Moreover, the
+        // logic to extract the relative path would need to consider several /out/soong/...
+        // locations patterns.
+        if (systemServicePathPrefixes.none { filePath.contains(it) }) return
+
+        val fullyQualifiedInterfaceName =
+            getContainingAidlInterfaceQualified(context, node) ?: return
+
+        targetExemptAidlInterfaceNames.add("\"$fullyQualifiedInterfaceName\",")
+    }
+
+    override fun afterCheckEachProject(context: Context) {
+        if (targetExemptAidlInterfaceNames.isEmpty()) return
+
+        val message = targetExemptAidlInterfaceNames.joinToString("\n")
+
+        context.report(
+            ISSUE_PERMISSION_ANNOTATION_EXEMPT_AIDL_INTERFACES,
+            context.getLocation(context.project.dir),
+            "\n" + message + "\n",
+        )
+    }
+
+    companion object {
+        @JvmField
+        val ISSUE_PERMISSION_ANNOTATION_EXEMPT_AIDL_INTERFACES = Issue.create(
+            id = "PermissionAnnotationExemptAidlInterfaces",
+            briefDescription = "Returns a set of all AIDL interfaces",
+            explanation = """
+                Produces the exemptAidlInterfaces set used by PermissionAnnotationDetector
+            """.trimIndent(),
+            category = Category.SECURITY,
+            priority = 5,
+            severity = Severity.INFORMATIONAL,
+            implementation = Implementation(
+                ExemptAidlInterfacesGenerator::class.java,
+                Scope.JAVA_FILE_SCOPE
+            )
+        )
+    }
+}
diff --git a/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/ExemptAidlInterfacesGeneratorTest.kt b/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/ExemptAidlInterfacesGeneratorTest.kt
new file mode 100644
index 0000000..9a17bb4
--- /dev/null
+++ b/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/ExemptAidlInterfacesGeneratorTest.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2024 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
+
+class ExemptAidlInterfacesGeneratorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = ExemptAidlInterfacesGenerator()
+
+    override fun getIssues(): List<Issue> = listOf(
+        ExemptAidlInterfacesGenerator.ISSUE_PERMISSION_ANNOTATION_EXEMPT_AIDL_INTERFACES,
+    )
+
+    override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+    fun testMultipleAidlInterfacesImplemented() {
+        lint()
+            .files(
+                java(
+                    createVisitedPath("TestClass1.java"),
+                    """
+                        package com.android.server;
+                        public class TestClass1 extends IFoo.Stub {
+                            public void testMethod() {}
+                        }
+                    """
+                )
+                    .indented(),
+                java(
+                    createVisitedPath("TestClass2.java"),
+                    """
+                        package com.android.server;
+                        public class TestClass2 extends IBar.Stub {
+                            public void testMethod() {}
+                        }
+                    """
+                )
+                    .indented(),
+                *stubs,
+            )
+            .run()
+            .expect(
+                """
+                    app: Information: "IFoo",
+                    "IBar", [PermissionAnnotationExemptAidlInterfaces]
+                    0 errors, 0 warnings
+                """
+            )
+    }
+
+    fun testSingleAidlInterfaceRepeated() {
+        lint()
+            .files(
+                java(
+                    createVisitedPath("TestClass1.java"),
+                    """
+                        package com.android.server;
+                        public class TestClass1 extends IFoo.Stub {
+                            public void testMethod() {}
+                        }
+                    """
+                )
+                    .indented(),
+                java(
+                    createVisitedPath("TestClass2.java"),
+                    """
+                        package com.android.server;
+                        public class TestClass2 extends IFoo.Stub {
+                            public void testMethod() {}
+                        }
+                    """
+                )
+                    .indented(),
+                *stubs,
+            )
+            .run()
+            .expect(
+                """
+                    app: Information: "IFoo", [PermissionAnnotationExemptAidlInterfaces]
+                    0 errors, 0 warnings
+                """
+            )
+    }
+
+    fun testAnonymousClassExtendsAidlStub() {
+        lint()
+            .files(
+                java(
+                    createVisitedPath("TestClass.java"),
+                    """
+                        package com.android.server;
+                        public class TestClass {
+                            private IBinder aidlImpl = new IFoo.Stub() {
+                                public void testMethod() {}
+                            };
+                        }
+                        """
+                )
+                    .indented(),
+                *stubs,
+            )
+            .run()
+            .expect(
+                """
+                    app: Information: "IFoo", [PermissionAnnotationExemptAidlInterfaces]
+                    0 errors, 0 warnings
+                """
+            )
+    }
+
+    fun testNoAidlInterfacesImplemented() {
+        lint()
+            .files(
+                java(
+                    createVisitedPath("TestClass.java"),
+                    """
+                        package com.android.server;
+                        public class TestClass {
+                            public void testMethod() {}
+                        }
+                    """
+                )
+                    .indented(),
+                *stubs
+            )
+            .run()
+            .expectClean()
+    }
+
+    fun testAidlInterfaceImplementedInIgnoredDirectory() {
+        lint()
+            .files(
+                java(
+                    ignoredPath,
+                    """
+                        package com.android.server;
+                        public class TestClass1 extends IFoo.Stub {
+                            public void testMethod() {}
+                        }
+                    """
+                )
+                    .indented(),
+                *stubs,
+            )
+            .run()
+            .expectClean()
+    }
+
+    private val interfaceIFoo: TestFile = java(
+        """
+            public interface IFoo extends android.os.IInterface {
+                public static abstract class Stub extends android.os.Binder implements IFoo {}
+                public void testMethod();
+            }
+        """
+    ).indented()
+
+    private val interfaceIBar: TestFile = java(
+        """
+            public interface IBar extends android.os.IInterface {
+                public static abstract class Stub extends android.os.Binder implements IBar {}
+                public void testMethod();
+            }
+        """
+    ).indented()
+
+    private val stubs = arrayOf(interfaceIFoo, interfaceIBar)
+
+    private fun createVisitedPath(filename: String) =
+        "src/frameworks/base/services/java/com/android/server/$filename"
+
+    private val ignoredPath = "src/test/pkg/TestClass.java"
+}
diff --git a/tools/lint/utils/generate-exempt-aidl-interfaces.sh b/tools/lint/utils/generate-exempt-aidl-interfaces.sh
new file mode 100755
index 0000000..44dcdd7
--- /dev/null
+++ b/tools/lint/utils/generate-exempt-aidl-interfaces.sh
@@ -0,0 +1,59 @@
+#
+# Copyright (C) 2024 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.
+#
+
+# Create a directory for the results and a nested temporary directory.
+mkdir -p $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/tmp
+
+# Create a copy of `AndroidGlobalLintChecker.jar` to restore it afterwards.
+cp $ANDROID_BUILD_TOP/prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar \
+    $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/AndroidGlobalLintChecker.jar
+
+# Configure the environment variable required for running the lint check on the entire source tree.
+export ANDROID_LINT_CHECK=PermissionAnnotationExemptAidlInterfaces
+
+# Build the target corresponding to the lint checks present in the `utils` directory.
+m AndroidUtilsLintChecker
+
+# Replace `AndroidGlobalLintChecker.jar` with the newly built `jar` file.
+cp $ANDROID_BUILD_TOP/out/host/linux-x86/framework/AndroidUtilsLintChecker.jar \
+    $ANDROID_BUILD_TOP/prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar;
+
+# Run the lint check on the entire source tree.
+m lint-check
+
+# Copy the archive containing the results of `lint-check` into the temporary directory.
+cp $ANDROID_BUILD_TOP/out/soong/lint-report-text.zip \
+    $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/tmp
+
+cd $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/tmp
+
+# Unzip the archive containing the results of `lint-check`.
+unzip lint-report-text.zip
+
+# Concatenate the results of `lint-check` into a single string.
+concatenated_reports=$(find . -type f | xargs cat)
+
+# Extract the fully qualified names of the AIDL Interfaces from the concatenated results. Output
+# this list into `out/soong/exempt_aidl_interfaces_generator_output/exempt_aidl_interfaces`.
+echo $concatenated_reports | grep -Eo '\"([a-zA-Z0-9_]*\.)+[a-zA-Z0-9_]*\",' | sort | uniq > ../exempt_aidl_interfaces
+
+# Remove the temporary directory.
+rm -rf $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/tmp
+
+# Restore the original copy of `AndroidGlobalLintChecker.jar` and delete the copy.
+cp $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/AndroidGlobalLintChecker.jar \
+    $ANDROID_BUILD_TOP/prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar
+rm $ANDROID_BUILD_TOP/out/soong/exempt_aidl_interfaces_generator_output/AndroidGlobalLintChecker.jar