Send special permission information to netd

Netd need the list of apps that have permission INTERNET or permission
UPDATE_DEVICE_STATS at run time to make decisions about application
request. To avoid protential deadlock and reduce the traffic between
netd and system server, Use packageManagerServcie to send the list of
apps that have those permissions to netd when device boot and when new
apps get installed.

Bug: 111560570
Bug: 111560739
Test: install and uninstall packages and dumpsys netd trafficcontroller
Change-Id: Idb9905f424557a5c59e35d41f5eafe345aca87e0
This commit is contained in:
Chenbo Feng
2019-01-07 16:14:26 -08:00
parent 296c5ed68d
commit 002c556145

View File

@@ -19,10 +19,11 @@ package com.android.server.connectivity;
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.NETWORK_STACK;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -32,23 +33,31 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.net.INetd;
import android.net.util.NetdService;
import android.os.Build;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
@@ -75,6 +84,59 @@ public class PermissionMonitor {
// Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
private final Map<Integer, Boolean> mApps = new HashMap<>();
// Keys are App packageNames, Values are app uids. . We need to keep track of this information
// because PackageListObserver#onPackageRemoved does not pass the UID.
@GuardedBy("mPackageNameUidMap")
private final Map<String, Integer> mPackageNameUidMap = new HashMap<>();
private class PackageListObserver implements PackageManagerInternal.PackageListObserver {
@Override
public void onPackageAdded(String packageName) {
final PackageInfo app = getPackageInfo(packageName);
if (app == null) {
Slog.wtf(TAG, "Failed to get information of installed package: " + packageName);
return;
}
int uid = (app.applicationInfo != null) ? app.applicationInfo.uid : INVALID_UID;
if (uid == INVALID_UID) {
Slog.wtf(TAG, "Failed to get the uid of installed package: " + packageName
+ "uid: " + uid);
return;
}
if (app.requestedPermissions == null) {
return;
}
sendPackagePermissionsForUid(uid,
filterPermission(Arrays.asList(app.requestedPermissions)));
synchronized (mPackageNameUidMap) {
mPackageNameUidMap.put(packageName, uid);
}
}
@Override
public void onPackageRemoved(String packageName) {
int uid;
synchronized (mPackageNameUidMap) {
if (!mPackageNameUidMap.containsKey(packageName)) {
return;
}
uid = mPackageNameUidMap.get(packageName);
mPackageNameUidMap.remove(packageName);
}
int permission = 0;
String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
for (String name : packages) {
final PackageInfo app = getPackageInfo(name);
if (app != null && app.requestedPermissions != null) {
permission |= filterPermission(Arrays.asList(app.requestedPermissions));
}
}
}
sendPackagePermissionsForUid(uid, permission);
}
}
public PermissionMonitor(Context context, INetworkManagementService netd) {
mContext = context;
mPackageManager = context.getPackageManager();
@@ -87,12 +149,21 @@ public class PermissionMonitor {
public synchronized void startMonitoring() {
log("Monitoring");
List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
if (pmi != null) {
pmi.getPackageList(new PackageListObserver());
} else {
loge("failed to get the PackageManagerInternal service");
}
List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
| MATCH_ANY_USER);
if (apps == null) {
loge("No apps");
return;
}
SparseIntArray netdPermsUids = new SparseIntArray();
for (PackageInfo app : apps) {
int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
if (uid < 0) {
@@ -110,6 +181,17 @@ public class PermissionMonitor {
mApps.put(uid, hasRestrictedPermission);
}
}
//TODO: unify the management of the permissions into one codepath.
if (app.requestedPermissions != null) {
int otherNetdPerms = filterPermission(Arrays.asList(app.requestedPermissions));
if (otherNetdPerms != 0) {
netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
synchronized (mPackageNameUidMap) {
mPackageNameUidMap.put(app.applicationInfo.packageName, uid);
}
}
}
}
List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users
@@ -121,6 +203,7 @@ public class PermissionMonitor {
log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
update(mUsers, mApps, true);
sendPackagePermissionsToNetd(netdPermsUids);
}
@VisibleForTesting
@@ -339,6 +422,107 @@ public class PermissionMonitor {
}
}
private static int filterPermission(List<String> requestedPermissions) {
int permissions = 0;
if (requestedPermissions.contains(INTERNET)) {
permissions |= INetd.PERMISSION_INTERNET;
}
if (requestedPermissions.contains(UPDATE_DEVICE_STATS)) {
permissions |= INetd.PERMISSION_UPDATE_DEVICE_STATS;
}
return permissions;
}
private PackageInfo getPackageInfo(String packageName) {
try {
PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS
| MATCH_ANY_USER);
return app;
} catch (NameNotFoundException e) {
// App not found.
loge("NameNotFoundException " + packageName);
return null;
}
}
/**
* Called by PackageListObserver when a package is installed/uninstalled. Send the updated
* permission information to netd.
*
* @param uid the app uid of the package installed
* @param permissions the permissions the app requested and netd cares about.
*
* @hide
*/
private void sendPackagePermissionsForUid(int uid, int permissions) {
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
netdPermissionsAppIds.put(uid, permissions);
sendPackagePermissionsToNetd(netdPermissionsAppIds);
}
/**
* Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET
* and/or UPDATE_DEVICE_STATS permission of the uids in array.
*
* @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the
* permission is 0, revoke all permissions of that uid.
*
* @hide
*/
private void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
INetd netdService = NetdService.getInstance();
if (netdService == null) {
Log.e(TAG, "Failed to get the netd service");
return;
}
ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
int permissions = netdPermissionsAppIds.valueAt(i);
switch(permissions) {
case (INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS):
allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
case INetd.PERMISSION_INTERNET:
internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
case INetd.PERMISSION_UPDATE_DEVICE_STATS:
updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
case INetd.NO_PERMISSIONS:
uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
default:
Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+ netdPermissionsAppIds.keyAt(i));
}
}
try {
// TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
if (allPermissionAppIds.size() != 0) {
netdService.trafficSetNetPermForUids(
INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
ArrayUtils.convertToIntArray(allPermissionAppIds));
}
if (internetPermissionAppIds.size() != 0) {
netdService.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
ArrayUtils.convertToIntArray(internetPermissionAppIds));
}
if (updateStatsPermissionAppIds.size() != 0) {
netdService.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
}
if (uninstalledAppIds.size() != 0) {
netdService.trafficSetNetPermForUids(INetd.NO_PERMISSIONS,
ArrayUtils.convertToIntArray(uninstalledAppIds));
}
} catch (RemoteException e) {
Log.e(TAG, "Pass appId list of special permission failed." + e);
}
}
private static void log(String s) {
if (DBG) {
Log.d(TAG, s);