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

Commit 4f23b091 authored by Chenbo Feng's avatar Chenbo Feng Committed by android-build-merger
Browse files

Merge "Send special permission information to netd" am: 703e8c68

am: 676e6316

Change-Id: I80271fa7c4e1c6a8e2eea55adf12abaece64d8da
parents 8a59e149 676e6316
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);