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
Merged-In: Ia11ca4a7ba6fc1b87c27c0c40e36c91d9cdb322a

Change-Id: Ia11ca4a7ba6fc1b87c27c0c40e36c91d9cdb322a
This commit is contained in:
paulhu
2021-06-30 17:03:06 +08:00
parent 05752a5316
commit 8a4ce40acb
2 changed files with 198 additions and 33 deletions

View File

@@ -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:");

View File

@@ -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<UidRange> 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<UidRange> 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<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,
@@ -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});
}
}