Merge "Listen EXTERNAL_APPLICATIONS_AVAILABLE intent" into sc-dev

This commit is contained in:
Paul Hu
2021-07-02 14:17:17 +00:00
committed by Android (Google) Code Review
2 changed files with 198 additions and 33 deletions

View File

@@ -121,15 +121,23 @@ public class PermissionMonitor {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
final String action = intent.getAction(); final String action = intent.getAction();
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
final Uri packageData = intent.getData(); final Uri packageData = intent.getData();
final String packageName = final String packageName =
packageData != null ? packageData.getSchemeSpecificPart() : null; packageData != null ? packageData.getSchemeSpecificPart() : null;
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
onPackageAdded(packageName, uid); onPackageAdded(packageName, uid);
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { } 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); 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 { } else {
Log.wtf(TAG, "received unexpected intent: " + action); Log.wtf(TAG, "received unexpected intent: " + action);
} }
@@ -194,6 +202,12 @@ public class PermissionMonitor {
mIntentReceiver, intentFilter, null /* broadcastPermission */, mIntentReceiver, intentFilter, null /* broadcastPermission */,
null /* scheduler */); 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 // Register UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer
mDeps.registerContentObserver( mDeps.registerContentObserver(
userAllContext, userAllContext,
@@ -812,6 +826,21 @@ public class PermissionMonitor {
update(mUsers, removedUids, false /* add */); 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 */ /** Dump info to dumpsys */
public void dump(IndentingPrintWriter pw) { public void dump(IndentingPrintWriter pw) {
pw.println("Interface filtering rules:"); pw.println("Interface filtering rules:");

View File

@@ -95,6 +95,7 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@@ -203,16 +204,12 @@ public class PermissionMonitorTest {
return packageInfo; return packageInfo;
} }
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, private static PackageInfo buildPackageInfo(String packageName, int uid,
UserHandle user) { String... permissions) {
final PackageInfo pkgInfo; final PackageInfo pkgInfo;
if (hasSystemPermission) { pkgInfo = systemPackageInfoWithPermissions(permissions);
pkgInfo = systemPackageInfoWithPermissions( pkgInfo.packageName = packageName;
CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS); pkgInfo.applicationInfo.uid = uid;
} else {
pkgInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, new String[] {}, "");
}
pkgInfo.applicationInfo.uid = user.getUid(UserHandle.getAppId(uid));
return pkgInfo; return pkgInfo;
} }
@@ -598,15 +595,12 @@ public class PermissionMonitorTest {
@Test @Test
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception { public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] { List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_UID1, CHANGE_NETWORK_STATE,
buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1), CONNECTIVITY_USE_RESTRICTED_NETWORKS),
buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1), buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1), buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2),
buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1) buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
})); buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring(); mPermissionMonitor.startMonitoring();
// Every app on user 0 except MOCK_UID2 are under VPN. // Every app on user 0 except MOCK_UID2 are under VPN.
final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] { final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -650,13 +644,10 @@ public class PermissionMonitorTest {
@Test @Test
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception { public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
Arrays.asList(new PackageInfo[] { List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_UID1, CHANGE_NETWORK_STATE,
buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1), NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS),
buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1) buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
})); buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
mPermissionMonitor.startMonitoring(); mPermissionMonitor.startMonitoring();
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1)); final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
@@ -893,13 +884,26 @@ public class PermissionMonitorTest {
new int[]{ MOCK_UID2 }); 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 @Test
public void testIntentReceiver() throws Exception { public void testIntentReceiver() throws Exception {
final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService); final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService);
final ArgumentCaptor<BroadcastReceiver> receiverCaptor = final BroadcastReceiver receiver = expectBroadcastReceiver(
ArgumentCaptor.forClass(BroadcastReceiver.class); Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REMOVED);
verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any());
final BroadcastReceiver receiver = receiverCaptor.getValue();
// Verify receiving PACKAGE_ADDED intent. // Verify receiving PACKAGE_ADDED intent.
final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED, final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
@@ -1053,4 +1057,136 @@ public class PermissionMonitorTest {
netdMonitor.expectNoPermission( netdMonitor.expectNoPermission(
new UserHandle[]{MOCK_USER2}, new int[]{ MOCK_UID1, MOCK_UID2 }); 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});
}
} }