diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java index 99118accba..512d76772d 100755 --- a/service/src/com/android/server/connectivity/PermissionMonitor.java +++ b/service/src/com/android/server/connectivity/PermissionMonitor.java @@ -121,15 +121,23 @@ public class PermissionMonitor { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); - final Uri packageData = intent.getData(); - final String packageName = - packageData != null ? packageData.getSchemeSpecificPart() : null; if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + final Uri packageData = intent.getData(); + final String packageName = + packageData != null ? packageData.getSchemeSpecificPart() : null; onPackageAdded(packageName, uid); } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + final Uri packageData = intent.getData(); + final String packageName = + packageData != null ? packageData.getSchemeSpecificPart() : null; onPackageRemoved(packageName, uid); + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + final String[] pkgList = + intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + onExternalApplicationsAvailable(pkgList); } else { Log.wtf(TAG, "received unexpected intent: " + action); } @@ -194,6 +202,12 @@ public class PermissionMonitor { mIntentReceiver, intentFilter, null /* broadcastPermission */, null /* scheduler */); + final IntentFilter externalIntentFilter = + new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + userAllContext.registerReceiver( + mIntentReceiver, externalIntentFilter, null /* broadcastPermission */, + null /* scheduler */); + // Register UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer mDeps.registerContentObserver( userAllContext, @@ -812,6 +826,21 @@ public class PermissionMonitor { update(mUsers, removedUids, false /* add */); } + private synchronized void onExternalApplicationsAvailable(String[] pkgList) { + if (CollectionUtils.isEmpty(pkgList)) { + Log.e(TAG, "No available external application."); + return; + } + + for (String app : pkgList) { + final PackageInfo info = getPackageInfo(app); + if (info == null || info.applicationInfo == null) continue; + + final int appId = info.applicationInfo.uid; + onPackageAdded(app, appId); // Use onPackageAdded to add package one by one. + } + } + /** Dump info to dumpsys */ public void dump(IndentingPrintWriter pw) { pw.println("Interface filtering rules:"); diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java index 7f923d6767..8f4650825f 100644 --- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -98,6 +98,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Set; @RunWith(DevSdkIgnoreRunner.class) @@ -211,16 +212,12 @@ public class PermissionMonitorTest { return packageInfo; } - private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, - UserHandle user) { + private static PackageInfo buildPackageInfo(String packageName, int uid, + String... permissions) { final PackageInfo pkgInfo; - if (hasSystemPermission) { - pkgInfo = systemPackageInfoWithPermissions( - CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS); - } else { - pkgInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, new String[] {}, ""); - } - pkgInfo.applicationInfo.uid = user.getUid(UserHandle.getAppId(uid)); + pkgInfo = systemPackageInfoWithPermissions(permissions); + pkgInfo.packageName = packageName; + pkgInfo.applicationInfo.uid = uid; return pkgInfo; } @@ -606,15 +603,12 @@ public class PermissionMonitorTest { @Test public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception { when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - Arrays.asList(new PackageInfo[] { - buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1), - buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1), - buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1), - buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1) - })); - when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), - eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1)); + List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_UID1, CHANGE_NETWORK_STATE, + CONNECTIVITY_USE_RESTRICTED_NETWORKS), + buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1), + buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2), + buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID))); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1); mPermissionMonitor.startMonitoring(); // Every app on user 0 except MOCK_UID2 are under VPN. final Set vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] { @@ -658,13 +652,10 @@ public class PermissionMonitorTest { @Test public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception { when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - Arrays.asList(new PackageInfo[] { - buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1), - buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1) - })); - when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), - eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1)); + List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_UID1, CHANGE_NETWORK_STATE, + NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS), + buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID))); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1); mPermissionMonitor.startMonitoring(); final Set vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1)); @@ -901,13 +892,26 @@ public class PermissionMonitorTest { new int[]{ MOCK_UID2 }); } + private BroadcastReceiver expectBroadcastReceiver(String... actions) { + final ArgumentCaptor receiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), + argThat(filter -> { + for (String action : actions) { + if (!filter.hasAction(action)) { + return false; + } + } + return true; + }), any(), any()); + return receiverCaptor.getValue(); + } + @Test public void testIntentReceiver() throws Exception { final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService); - final ArgumentCaptor receiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any()); - final BroadcastReceiver receiver = receiverCaptor.getValue(); + final BroadcastReceiver receiver = expectBroadcastReceiver( + Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REMOVED); // Verify receiving PACKAGE_ADDED intent. final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED, @@ -1061,4 +1065,136 @@ public class PermissionMonitorTest { netdMonitor.expectNoPermission( new UserHandle[]{MOCK_USER2}, new int[]{ MOCK_UID1, MOCK_UID2 }); } + + @Test + public void testOnExternalApplicationsAvailable() throws Exception { + final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService); + final NetdMonitor netdMonitor = new NetdMonitor(mNetdService); + final BroadcastReceiver receiver = expectBroadcastReceiver( + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + + // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external + // and have different uids. There has no permission for both uids. + when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1)); + when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1), + buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2))); + mPermissionMonitor.startMonitoring(); + netdMonitor.expectNoPermission( + new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1, MOCK_UID2}); + netdServiceMonitor.expectPermission( + INetd.PERMISSION_NONE, new int[]{MOCK_UID1, MOCK_UID2}); + + // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd. + final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, + new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2}); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, + CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE, + UPDATE_DEVICE_STATS); + receiver.onReceive(mContext, externalIntent); + netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + netdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2}); + netdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[] { MOCK_UID1 }); + netdServiceMonitor.expectPermission( + INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID2}); + } + + @Test + public void testOnExternalApplicationsAvailable_AppsNotRegisteredOnStartMonitoring() + throws Exception { + final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService); + final NetdMonitor netdMonitor = new NetdMonitor(mNetdService); + final BroadcastReceiver receiver = expectBroadcastReceiver( + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + + // One user MOCK_USER1 + mPermissionMonitor.onUserAdded(MOCK_USER1); + + // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external + // and have different uids. There has no permission for both uids. + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, + CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE, + UPDATE_DEVICE_STATS); + + // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd. + final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, + new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2}); + receiver.onReceive(mContext, externalIntent); + netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + netdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2}); + netdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[] { MOCK_UID1 }); + netdServiceMonitor.expectPermission( + INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID2}); + } + + @Test + public void testOnExternalApplicationsAvailableWithSharedUid() + throws Exception { + final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService); + final NetdMonitor netdMonitor = new NetdMonitor(mNetdService); + final BroadcastReceiver receiver = expectBroadcastReceiver( + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + + // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external + // storage and shared on MOCK_UID1. There has no permission for MOCK_UID1. + when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1)); + when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1), + buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1))); + mPermissionMonitor.startMonitoring(); + netdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + netdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[] {MOCK_UID1}); + + // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd. + final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1}); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, UPDATE_DEVICE_STATS); + when(mPackageManager.getPackagesForUid(MOCK_UID1)) + .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2}); + receiver.onReceive(mContext, externalIntent); + netdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + netdServiceMonitor.expectPermission( + INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] {MOCK_UID1}); + } + + @Test + public void testOnExternalApplicationsAvailableWithSharedUid_DifferentStorage() + throws Exception { + final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService); + final NetdMonitor netdMonitor = new NetdMonitor(mNetdService); + final BroadcastReceiver receiver = expectBroadcastReceiver( + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + + // Initial the permission state. MOCK_PACKAGE1 is installed on external storage and + // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID1. + // MOCK_UID1 has NETWORK and INTERNET permissions. + when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1)); + when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1), + buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE, + INTERNET))); + mPermissionMonitor.startMonitoring(); + netdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + netdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[] {MOCK_UID1}); + + // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd. + final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1}); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, + CONNECTIVITY_USE_RESTRICTED_NETWORKS, UPDATE_DEVICE_STATS); + buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE, + INTERNET); + when(mPackageManager.getPackagesForUid(MOCK_UID1)) + .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2}); + receiver.onReceive(mContext, externalIntent); + netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + netdServiceMonitor.expectPermission( + INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, + new int[] {MOCK_UID1}); + } }