Listen EXTERNAL_APPLICATIONS_AVAILABLE intent
- Some applications are installed on external storage and they
are unavailable until exernal storage installed them
completely. And their permission need update after becoming
available. Thus, listen EXTERNAL_APPLICATIONS_AVAILABLE intent
and update those package whose is listed in chaged package
list.
- Remove user argument on buildPackageInfo() because the uid on
ApplicationInfo is based uid.
Bug: 145234989
Test: atest FrameworksNetTests
Ignore-AOSP-First: Need cherry-pick
Change-Id: Ia11ca4a7ba6fc1b87c27c0c40e36c91d9cdb322a
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 3328846..9ff9e3a 100644
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -121,15 +121,23 @@
@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 @@
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,
@@ -809,6 +823,21 @@
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 e98f5db..6c8f738 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -95,6 +95,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@@ -203,16 +204,12 @@
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;
}
@@ -579,15 +576,12 @@
@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<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -631,13 +625,10 @@
@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<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
@@ -874,13 +865,26 @@
new int[]{ MOCK_UID2 });
}
+ private BroadcastReceiver expectBroadcastReceiver(String... actions) {
+ final ArgumentCaptor<BroadcastReceiver> 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<BroadcastReceiver> 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,
@@ -1034,4 +1038,136 @@
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});
+ }
}