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

Commit d0c6ccba authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Move NetworkPolicy from apps to UID.

For multi-user devices, switch to storing policy per-user instead of
per-app.  Also watch for user added/removed broadcasts to clean up
policies and apply global restrictions.

Bug: 7121279
Change-Id: Ia7326bd0ebe0586fa4ec6d3a62f6313dc8814007
parent ee100445
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1473,7 +1473,7 @@ public class Intent implements Parcelable, Cloneable {
     * Broadcast Action: A new application package has been installed on the
     * device. The data contains the name of the package.  Note that the
     * newly installed package does <em>not</em> receive this broadcast.
     * <p>My include the following extras:
     * <p>May include the following extras:
     * <ul>
     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package.
     * <li> {@link #EXTRA_REPLACING} is set to true if this is following
@@ -1489,7 +1489,7 @@ public class Intent implements Parcelable, Cloneable {
     * Broadcast Action: A new version of an application package has been
     * installed, replacing an existing version that was previously installed.
     * The data contains the name of the package.
     * <p>My include the following extras:
     * <p>May include the following extras:
     * <ul>
     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package.
     * </ul>
+3 −3
Original line number Diff line number Diff line
@@ -30,9 +30,9 @@ import android.net.NetworkTemplate;
interface INetworkPolicyManager {

    /** Control UID policies. */
    void setAppPolicy(int appId, int policy);
    int getAppPolicy(int appId);
    int[] getAppsWithPolicy(int policy);
    void setUidPolicy(int uid, int policy);
    int getUidPolicy(int uid);
    int[] getUidsWithPolicy(int policy);

    boolean isUidForeground(int uid);

+9 −9
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.format.Time;

import com.google.android.collect.Sets;
@@ -72,29 +73,29 @@ public class NetworkPolicyManager {
    }

    /**
     * Set policy flags for specific application.
     * Set policy flags for specific UID.
     *
     * @param policy {@link #POLICY_NONE} or combination of flags like
     *            {@link #POLICY_REJECT_METERED_BACKGROUND}.
     */
    public void setAppPolicy(int appId, int policy) {
    public void setUidPolicy(int uid, int policy) {
        try {
            mService.setAppPolicy(appId, policy);
            mService.setUidPolicy(uid, policy);
        } catch (RemoteException e) {
        }
    }

    public int getAppPolicy(int appId) {
    public int getUidPolicy(int uid) {
        try {
            return mService.getAppPolicy(appId);
            return mService.getUidPolicy(uid);
        } catch (RemoteException e) {
            return POLICY_NONE;
        }
    }

    public int[] getAppsWithPolicy(int policy) {
    public int[] getUidsWithPolicy(int policy) {
        try {
            return mService.getAppsWithPolicy(policy);
            return mService.getUidsWithPolicy(policy);
        } catch (RemoteException e) {
            return new int[0];
        }
@@ -236,8 +237,7 @@ public class NetworkPolicyManager {
    @Deprecated
    public static boolean isUidValidForPolicy(Context context, int uid) {
        // first, quick-reject non-applications
        if (uid < android.os.Process.FIRST_APPLICATION_UID
                || uid > android.os.Process.LAST_APPLICATION_UID) {
        if (!UserHandle.isApp(uid)) {
            return false;
        }

+8 −4
Original line number Diff line number Diff line
@@ -87,15 +87,19 @@ public final class UserHandle implements Parcelable {

    /** @hide */
    public static final boolean isIsolated(int uid) {
        uid = getAppId(uid);
        return uid >= Process.FIRST_ISOLATED_UID && uid <= Process.LAST_ISOLATED_UID;
        if (uid > 0) {
            final int appId = getAppId(uid);
            return appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID;
        } else {
            return false;
        }
    }

    /** @hide */
    public static boolean isApp(int uid) {
        if (uid > 0) {
            uid = UserHandle.getAppId(uid);
            return uid >= Process.FIRST_APPLICATION_UID && uid <= Process.LAST_APPLICATION_UID;
            final int appId = getAppId(uid);
            return appId >= Process.FIRST_APPLICATION_UID && appId <= Process.LAST_APPLICATION_UID;
        } else {
            return false;
        }
+141 −80
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -179,7 +181,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    private static final int VERSION_ADDED_INFERRED = 7;
    private static final int VERSION_SWITCH_APP_ID = 8;
    private static final int VERSION_ADDED_NETWORK_ID = 9;
    private static final int VERSION_LATEST = VERSION_ADDED_NETWORK_ID;
    private static final int VERSION_SWITCH_UID = 10;
    private static final int VERSION_LATEST = VERSION_SWITCH_UID;

    // @VisibleForTesting
    public static final int TYPE_WARNING = 0x1;
@@ -250,8 +253,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    /** Currently active network rules for ifaces. */
    private HashMap<NetworkPolicy, String[]> mNetworkRules = Maps.newHashMap();

    /** Defined app policies. */
    private SparseIntArray mAppPolicy = new SparseIntArray();
    /** Defined UID policies. */
    private SparseIntArray mUidPolicy = new SparseIntArray();
    /** Currently derived rules for each UID. */
    private SparseIntArray mUidRules = new SparseIntArray();

@@ -357,12 +360,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE);
        mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);

        // listen for package/uid changes to update policy
        // listen for package changes to update policy
        final IntentFilter packageFilter = new IntentFilter();
        packageFilter.addAction(ACTION_PACKAGE_ADDED);
        packageFilter.addAction(ACTION_UID_REMOVED);
        packageFilter.addDataScheme("package");
        mContext.registerReceiver(mPackageReceiver, packageFilter, null, mHandler);

        // listen for UID changes to update policy
        mContext.registerReceiver(
                mUidRemovedReceiver, new IntentFilter(ACTION_UID_REMOVED), null, mHandler);

        // listen for user changes to update policy
        final IntentFilter userFilter = new IntentFilter();
        userFilter.addAction(ACTION_USER_ADDED);
        userFilter.addAction(ACTION_USER_REMOVED);
        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);

        // listen for stats update events
        final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
        mContext.registerReceiver(
@@ -421,34 +434,59 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    private BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // on background handler thread, and PACKAGE_ADDED and UID_REMOVED
            // are protected broadcasts.
            // on background handler thread, and PACKAGE_ADDED is protected

            final String action = intent.getAction();
            final int uid = intent.getIntExtra(EXTRA_UID, 0);
            final int appId = UserHandle.getAppId(uid);
            synchronized (mRulesLock) {
                if (ACTION_PACKAGE_ADDED.equals(action)) {
                    // NOTE: PACKAGE_ADDED is currently only sent once, and is
                    // not broadcast when users are added.
            final int uid = intent.getIntExtra(EXTRA_UID, -1);
            if (uid == -1) return;

            if (ACTION_PACKAGE_ADDED.equals(action)) {
                // update rules for UID, since it might be subject to
                    // global background data policy.
                // global background data policy
                if (LOGV) Slog.v(TAG, "ACTION_PACKAGE_ADDED for uid=" + uid);
                    updateRulesForAppLocked(appId);
                synchronized (mRulesLock) {
                    updateRulesForUidLocked(uid);
                }
            }
        }
    };

    private BroadcastReceiver mUidRemovedReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // on background handler thread, and UID_REMOVED is protected

                } else if (ACTION_UID_REMOVED.equals(action)) {
                    // NOTE: UID_REMOVED is currently only sent once, and is not
                    // broadcast when users are removed.
            final int uid = intent.getIntExtra(EXTRA_UID, -1);
            if (uid == -1) return;

                    // remove any policy and update rules to clean up.
            // remove any policy and update rules to clean up
            if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid);

                    mAppPolicy.delete(appId);
                    updateRulesForAppLocked(appId);
            synchronized (mRulesLock) {
                mUidPolicy.delete(uid);
                updateRulesForUidLocked(uid);
                writePolicyLocked();
            }
        }
    };

    private BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // on background handler thread, and USER_ADDED and USER_REMOVED
            // broadcasts are protected

            final String action = intent.getAction();
            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
            if (userId == -1) return;

            // Remove any policies for given user; both cleaning up after a
            // USER_REMOVED, and one last sanity check during USER_ADDED
            removePoliciesForUserLocked(userId);

            // Update global restrict for new user
            synchronized (mRulesLock) {
                updateRulesForRestrictBackgroundLocked();
            }
        }
    };

@@ -1107,7 +1145,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {

        // clear any existing policy and read from disk
        mNetworkPolicy.clear();
        mAppPolicy.clear();
        mUidPolicy.clear();

        FileInputStream fis = null;
        try {
@@ -1188,24 +1226,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                                cycleTimezone, warningBytes, limitBytes, lastWarningSnooze,
                                lastLimitSnooze, metered, inferred));

                    } else if (TAG_UID_POLICY.equals(tag) && version < VERSION_SWITCH_APP_ID) {
                    } else if (TAG_UID_POLICY.equals(tag)) {
                        final int uid = readIntAttribute(in, ATTR_UID);
                        final int policy = readIntAttribute(in, ATTR_POLICY);

                        final int appId = UserHandle.getAppId(uid);
                        if (UserHandle.isApp(appId)) {
                            setAppPolicyUnchecked(appId, policy, false);
                        if (UserHandle.isApp(uid)) {
                            setUidPolicyUnchecked(uid, policy, false);
                        } else {
                            Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
                        }
                    } else if (TAG_APP_POLICY.equals(tag) && version >= VERSION_SWITCH_APP_ID) {
                    } else if (TAG_APP_POLICY.equals(tag)) {
                        final int appId = readIntAttribute(in, ATTR_APP_ID);
                        final int policy = readIntAttribute(in, ATTR_POLICY);

                        if (UserHandle.isApp(appId)) {
                            setAppPolicyUnchecked(appId, policy, false);
                        // TODO: set for other users during upgrade
                        final int uid = UserHandle.getUid(UserHandle.USER_OWNER, appId);
                        if (UserHandle.isApp(uid)) {
                            setUidPolicyUnchecked(uid, policy, false);
                        } else {
                            Slog.w(TAG, "unable to apply policy to appId " + appId + "; ignoring");
                            Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
                        }
                    }
                }
@@ -1280,17 +1319,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
            }

            // write all known uid policies
            for (int i = 0; i < mAppPolicy.size(); i++) {
                final int appId = mAppPolicy.keyAt(i);
                final int policy = mAppPolicy.valueAt(i);
            for (int i = 0; i < mUidPolicy.size(); i++) {
                final int uid = mUidPolicy.keyAt(i);
                final int policy = mUidPolicy.valueAt(i);

                // skip writing empty policies
                if (policy == POLICY_NONE) continue;

                out.startTag(null, TAG_APP_POLICY);
                writeIntAttribute(out, ATTR_APP_ID, appId);
                out.startTag(null, TAG_UID_POLICY);
                writeIntAttribute(out, ATTR_UID, uid);
                writeIntAttribute(out, ATTR_POLICY, policy);
                out.endTag(null, TAG_APP_POLICY);
                out.endTag(null, TAG_UID_POLICY);
            }

            out.endTag(null, TAG_POLICY_LIST);
@@ -1305,24 +1344,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    }

    @Override
    public void setAppPolicy(int appId, int policy) {
    public void setUidPolicy(int uid, int policy) {
        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);

        if (!UserHandle.isApp(appId)) {
            throw new IllegalArgumentException("cannot apply policy to appId " + appId);
        if (!UserHandle.isApp(uid)) {
            throw new IllegalArgumentException("cannot apply policy to UID " + uid);
        }

        setAppPolicyUnchecked(appId, policy, true);
        setUidPolicyUnchecked(uid, policy, true);
    }

    private void setAppPolicyUnchecked(int appId, int policy, boolean persist) {
    private void setUidPolicyUnchecked(int uid, int policy, boolean persist) {
        final int oldPolicy;
        synchronized (mRulesLock) {
            oldPolicy = getAppPolicy(appId);
            mAppPolicy.put(appId, policy);
            oldPolicy = getUidPolicy(uid);
            mUidPolicy.put(uid, policy);

            // uid policy changed, recompute rules and persist policy.
            updateRulesForAppLocked(appId);
            updateRulesForUidLocked(uid);
            if (persist) {
                writePolicyLocked();
            }
@@ -1330,29 +1369,53 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    }

    @Override
    public int getAppPolicy(int appId) {
    public int getUidPolicy(int uid) {
        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);

        synchronized (mRulesLock) {
            return mAppPolicy.get(appId, POLICY_NONE);
            return mUidPolicy.get(uid, POLICY_NONE);
        }
    }

    @Override
    public int[] getAppsWithPolicy(int policy) {
    public int[] getUidsWithPolicy(int policy) {
        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);

        int[] appIds = new int[0];
        int[] uids = new int[0];
        synchronized (mRulesLock) {
            for (int i = 0; i < mAppPolicy.size(); i++) {
                final int appId = mAppPolicy.keyAt(i);
                final int appPolicy = mAppPolicy.valueAt(i);
                if (appPolicy == policy) {
                    appIds = appendInt(appIds, appId);
            for (int i = 0; i < mUidPolicy.size(); i++) {
                final int uid = mUidPolicy.keyAt(i);
                final int uidPolicy = mUidPolicy.valueAt(i);
                if (uidPolicy == policy) {
                    uids = appendInt(uids, uid);
                }
            }
        }
        return uids;
    }

    /**
     * Remove any policies associated with given {@link UserHandle}, persisting
     * if any changes are made.
     */
    private void removePoliciesForUserLocked(int userId) {
        if (LOGV) Slog.v(TAG, "removePoliciesForUserLocked()");

        int[] uids = new int[0];
        for (int i = 0; i < mUidPolicy.size(); i++) {
            final int uid = mUidPolicy.keyAt(i);
            if (UserHandle.getUserId(uid) == userId) {
                uids = appendInt(uids, uid);
            }
        }

        if (uids.length > 0) {
            for (int uid : uids) {
                mUidPolicy.delete(uid);
                updateRulesForUidLocked(uid);
            }
            writePolicyLocked();
        }
        return appIds;
    }

    @Override
@@ -1586,14 +1649,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
            }
            fout.decreaseIndent();

            fout.println("Policy for apps:");
            fout.println("Policy for UIDs:");
            fout.increaseIndent();
            int size = mAppPolicy.size();
            int size = mUidPolicy.size();
            for (int i = 0; i < size; i++) {
                final int appId = mAppPolicy.keyAt(i);
                final int policy = mAppPolicy.valueAt(i);
                fout.print("appId=");
                fout.print(appId);
                final int uid = mUidPolicy.keyAt(i);
                final int policy = mUidPolicy.valueAt(i);
                fout.print("UID=");
                fout.print(uid);
                fout.print(" policy=");
                dumpPolicy(fout, policy);
                fout.println();
@@ -1698,12 +1761,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
     * Update rules that might be changed by {@link #mRestrictBackground} value.
     */
    private void updateRulesForRestrictBackgroundLocked() {
        // update rules for all installed applications
        final PackageManager pm = mContext.getPackageManager();
        final List<ApplicationInfo> apps = pm.getInstalledApplications(0);
        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);

        // update rules for all installed applications
        final List<UserInfo> users = um.getUsers();
        final List<ApplicationInfo> apps = pm.getInstalledApplications(
                PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS);

        for (UserInfo user : users) {
            for (ApplicationInfo app : apps) {
            final int appId = UserHandle.getAppId(app.uid);
            updateRulesForAppLocked(appId);
                final int uid = UserHandle.getUid(user.id, app.uid);
                updateRulesForUidLocked(uid);
            }
        }

        // limit data usage for some internal system services
@@ -1711,14 +1781,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        updateRulesForUidLocked(android.os.Process.DRM_UID);
    }

    private void updateRulesForAppLocked(int appId) {
        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        for (UserInfo user : um.getUsers()) {
            final int uid = UserHandle.getUid(user.id, appId);
            updateRulesForUidLocked(uid);
        }
    }

    private static boolean isUidValidForRules(int uid) {
        // allow rules on specific system services, and any apps
        if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID
@@ -1732,13 +1794,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    private void updateRulesForUidLocked(int uid) {
        if (!isUidValidForRules(uid)) return;

        final int appId = UserHandle.getAppId(uid);
        final int appPolicy = getAppPolicy(appId);
        final int uidPolicy = getUidPolicy(uid);
        final boolean uidForeground = isUidForeground(uid);

        // derive active rules based on policy and active state
        int uidRules = RULE_ALLOW_ALL;
        if (!uidForeground && (appPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
        if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
            // uid in background, and policy says to block metered data
            uidRules = RULE_REJECT_METERED;
        }
Loading