diff options
| -rw-r--r-- | services/core/java/com/android/server/connectivity/PermissionMonitor.java | 194 |
1 files changed, 189 insertions, 5 deletions
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 420b23e6a39f..d84a4d2db993 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -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); |