Loading services/core/java/com/android/server/connectivity/PermissionMonitor.java +189 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading @@ -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(); Loading @@ -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) { Loading @@ -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 Loading @@ -121,6 +203,7 @@ public class PermissionMonitor { log("Users: " + mUsers.size() + ", Apps: " + mApps.size()); update(mUsers, mApps, true); sendPackagePermissionsToNetd(netdPermsUids); } @VisibleForTesting Loading Loading @@ -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); Loading Loading
services/core/java/com/android/server/connectivity/PermissionMonitor.java +189 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading @@ -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(); Loading @@ -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) { Loading @@ -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 Loading @@ -121,6 +203,7 @@ public class PermissionMonitor { log("Users: " + mUsers.size() + ", Apps: " + mApps.size()); update(mUsers, mApps, true); sendPackagePermissionsToNetd(netdPermsUids); } @VisibleForTesting Loading Loading @@ -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); Loading