Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 703e8c68 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Send special permission information to netd"

parents bede9f86 e16cd66a
Loading
Loading
Loading
Loading
+189 −5
Original line number Diff line number Diff line
@@ -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);