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

Unverified Commit d5b59677 authored by Oliver Scott's avatar Oliver Scott Committed by Michael Bestas
Browse files

Implement backup/restore for network policy

Modify network policy XML parser helper functions for backup/restore
scenarios, including secondary users

Test:
1. Block an app's (e.g. Chromium) wifi and mobile data through the firewall
2. Backup to internal storage on user 0
3. Create work profile
4. Copy .SeedVaultAndroid to the work profile (through its Files app)
5. Restore backup from work profile setup wizard
6. Verify the app's wifi and mobile data is still blocked

Issue: calyxos#2586
Bug: https://issuetracker.google.com/issues/207800947
Change-Id2: I44da196614a749c2c8b0113cade92215e88fed4a
Change-Id: I77158b344a47138a17fbce72d3ecb3be6927b870
parent ec398bec
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -94,4 +94,7 @@ interface INetworkPolicyManager {
    boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
    @EnforcePermission("OBSERVE_NETWORK_POLICY")
    boolean isUidRestrictedOnMeteredNetworks(int uid);

    byte[] getBackupPayload(int user);
    void applyRestore(in byte[] payload, int user);
}
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Calyx Institute
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.backup;

import android.app.backup.BlobBackupHelper;
import android.content.Context;
import android.net.INetworkPolicyManager;
import android.os.ServiceManager;
import android.util.Log;
import android.util.Slog;

public class NetworkPolicyBackupHelper extends BlobBackupHelper {
    private static final String TAG = "NetworkPolicyBackupHelper";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    // Current version of the blob schema
    static final int BLOB_VERSION = 1;

    // Key under which the payload blob is stored
    static final String KEY_NETWORK_POLICY = "network_policy";

    private final int mUserId;

    public NetworkPolicyBackupHelper(int userId) {
        super(BLOB_VERSION, KEY_NETWORK_POLICY);
        mUserId = userId;
    }

    @Override
    protected byte[] getBackupPayload(String key) {
        byte[] newPayload = null;
        if (KEY_NETWORK_POLICY.equals(key)) {
            try {
                INetworkPolicyManager npm = INetworkPolicyManager.Stub.asInterface(
                        ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                newPayload = npm.getBackupPayload(mUserId);
            } catch (Exception e) {
                // Treat as no data
                Slog.e(TAG, "Couldn't communicate with network policy manager", e);
                newPayload = null;
            }
        }
        return newPayload;
    }

    @Override
    protected void applyRestoredPayload(String key, byte[] payload) {
        if (DEBUG) {
            Slog.v(TAG, "Got restore of " + key);
        }

        if (KEY_NETWORK_POLICY.equals(key)) {
            try {
                INetworkPolicyManager.Stub.asInterface(
                        ServiceManager.getService(Context.NETWORK_POLICY_SERVICE))
                        .applyRestore(payload, mUserId);
            } catch (Exception e) {
                Slog.e(TAG, "Couldn't communicate with network policy manager", e);
            }
        }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
    private static final String APP_GENDER_HELPER = "app_gender";
    private static final String COMPANION_HELPER = "companion";
    private static final String SYSTEM_GENDER_HELPER = "system_gender";
    private static final String NETWORK_POLICY_HELPER = "network_policy";
    private static final String DISPLAY_HELPER = "display";
    private static final String INPUT_HELPER = "input";
    private static final String WEAR_BACKUP_HELPER = "wear";
@@ -108,6 +109,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
                    COMPANION_HELPER,
                    APP_GENDER_HELPER,
                    SYSTEM_GENDER_HELPER,
                    NETWORK_POLICY_HELPER,
                    DISPLAY_HELPER);

    /** Helpers that are enabled for full, non-system users. */
@@ -150,6 +152,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
        addHelperIfEligibleForUser(COMPANION_HELPER, new CompanionBackupHelper(mUserId));
        addHelperIfEligibleForUser(SYSTEM_GENDER_HELPER,
                new SystemGrammaticalGenderBackupHelper(mUserId));
        addHelperIfEligibleForUser(NETWORK_POLICY_HELPER, new NetworkPolicyBackupHelper(mUserId));
        addHelperIfEligibleForUser(DISPLAY_HELPER, new DisplayBackupHelper(mUserId));
        if (com.android.hardware.input.Flags.enableBackupAndRestoreForInputGestures()) {
            addHelperIfEligibleForUser(INPUT_HELPER, new InputBackupHelper(mUserId));
+131 −33
Original line number Diff line number Diff line
@@ -288,15 +288,22 @@ import dalvik.annotation.optimization.NeverCompile;

import libcore.io.IoUtils;

import org.xmlpull.v1.XmlPullParserException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
@@ -2832,11 +2839,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                                lastLimitSnooze, metered, inferred));
                    }
                } else if (TAG_UID_POLICY.equals(tag)) {
                    final int uid = readIntAttribute(in, ATTR_UID);
                    int uid = readUidAttribute(in, forRestore, userId);
                    final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
                    final int policy = readIntAttribute(in, ATTR_POLICY);

                    if (UserHandle.isApp(uid)) {
                        if (forRestore) {
                            setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
                        } else {
                            setUidPolicyUncheckedUL(uid, policy, false);
                        }
                    } else {
                        Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
                    }
@@ -2847,18 +2859,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                    // TODO: set for other users during upgrade
                    // app policy is deprecated so this is only used in pre system user split.
                    final int uid = UserHandle.getUid(UserHandle.USER_SYSTEM, appId);
                    final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
                    if (UserHandle.isApp(uid)) {
                        if (forRestore) {
                            setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
                        } else {
                            setUidPolicyUncheckedUL(uid, policy, false);
                        }
                    } else {
                        Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
                    }
                } else if (TAG_ALLOWLIST.equals(tag)) {
                    insideAllowlist = true;
                } else if (TAG_RESTRICT_BACKGROUND.equals(tag) && insideAllowlist) {
                    final int uid = readIntAttribute(in, ATTR_UID);
                    int uid = readUidAttribute(in, forRestore, userId);
                    restrictBackgroundAllowedUids.append(uid, true);
                } else if (TAG_REVOKED_RESTRICT_BACKGROUND.equals(tag) && insideAllowlist) {
                    final int uid = readIntAttribute(in, ATTR_UID);
                    int uid = readUidAttribute(in, forRestore, userId);
                    mRestrictBackgroundAllowlistRevokedUids.put(uid, true);
                }
            } else if (type == END_TAG) {
@@ -2882,7 +2899,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                final int newPolicy = policy | POLICY_ALLOW_METERED_BACKGROUND;
                if (LOGV)
                    Log.v(TAG, "new policy for " + uid + ": " + uidPoliciesToString(newPolicy));
                if (forRestore) {
                    setUidPolicyUncheckedUL(uid, policy, newPolicy, true);
                } else {
                    setUidPolicyUncheckedUL(uid, newPolicy, false);
                }
            } else {
                Slog.w(TAG, "unable to update policy on UID " + uid);
            }
@@ -2981,6 +3002,59 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {

        out.startDocument(null, true);

        // only write UID-independent attributes during normal operation or system user backups
        if (!forBackup || userId == UserHandle.USER_SYSTEM) {
            writeUidIndependentAttributes(out);
        }

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

            // skip writing policies belonging to other users
            if (forBackup && UserHandle.getUserId(uid) != userId) {
                continue;
            }
            // skip writing empty policies
            if (policy == POLICY_NONE) continue;

            out.startTag(null, TAG_UID_POLICY);
            if (!forBackup) {
                writeIntAttribute(out, ATTR_UID, uid);
            } else {
                writeStringAttribute(out, ATTR_XML_UTILS_NAME, getPackageForUid(uid));
            }
            writeIntAttribute(out, ATTR_POLICY, policy);
            out.endTag(null, TAG_UID_POLICY);
        }

        // write all allowlists
        out.startTag(null, TAG_ALLOWLIST);

        // revoked restrict background allowlist
        int size = mRestrictBackgroundAllowlistRevokedUids.size();
        for (int i = 0; i < size; i++) {
            final int uid = mRestrictBackgroundAllowlistRevokedUids.keyAt(i);
            // skip writing policies belonging to other users
            if (forBackup && UserHandle.getUserId(uid) != userId) {
                continue;
            }
            out.startTag(null, TAG_REVOKED_RESTRICT_BACKGROUND);
            if (!forBackup) {
                writeIntAttribute(out, ATTR_UID, uid);
            } else {
                writeStringAttribute(out, ATTR_XML_UTILS_NAME, getPackageForUid(uid));
            }
            out.endTag(null, TAG_REVOKED_RESTRICT_BACKGROUND);
        }

        out.endTag(null, TAG_ALLOWLIST);

        out.endDocument();
    }

    private void writeUidIndependentAttributes(TypedXmlSerializer out) throws IOException {
        out.startTag(null, TAG_POLICY_LIST);
        writeIntAttribute(out, ATTR_VERSION, VERSION_LATEST);
        writeBooleanAttribute(out, ATTR_RESTRICT_BACKGROUND, mRestrictBackground);
@@ -3022,38 +3096,39 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
            writeBooleanAttribute(out, ATTR_INFERRED, policy.inferred);
            out.endTag(null, TAG_NETWORK_POLICY);
        }

        // write all known uid policies
        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_UID_POLICY);
            writeIntAttribute(out, ATTR_UID, uid);
            writeIntAttribute(out, ATTR_POLICY, policy);
            out.endTag(null, TAG_UID_POLICY);
        }

        out.endTag(null, TAG_POLICY_LIST);
    }

        // write all allowlists
        out.startTag(null, TAG_ALLOWLIST);

        // revoked restrict background allowlist
        int size = mRestrictBackgroundAllowlistRevokedUids.size();
        for (int i = 0; i < size; i++) {
            final int uid = mRestrictBackgroundAllowlistRevokedUids.keyAt(i);
            out.startTag(null, TAG_REVOKED_RESTRICT_BACKGROUND);
            writeIntAttribute(out, ATTR_UID, uid);
            out.endTag(null, TAG_REVOKED_RESTRICT_BACKGROUND);
    @Override
    public byte[] getBackupPayload(int user) {
        enforceSystemCaller();
        if (LOGD) Slog.d(TAG, "getBackupPayload u= " + user);
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            writePolicyXml(baos, true, user);
            return baos.toByteArray();
        } catch (IOException e) {
            Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
        }
        return null;
    }

        out.endTag(null, TAG_ALLOWLIST);
    @Override
    public void applyRestore(byte[] payload, int user) {
        enforceSystemCaller();
        if (LOGD) Slog.d(TAG, "applyRestore u=" + user + " payload="
                + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
        if (payload == null) {
            Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
            return;
        }

        out.endDocument();
        final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
        try {
            readPolicyXml(bais, true, user);
        } catch (IOException | XmlPullParserException e) {
            Slog.w(TAG, "applyRestore: error reading payload for user " + user, e);
        }
    }

    @EnforcePermission(MANAGE_NETWORK_POLICY)
@@ -3265,6 +3340,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        }
    }

    private void enforceSystemCaller() {
        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
            throw new SecurityException("Caller must be system");
        }
    }

    @Override
    public void registerListener(@NonNull INetworkPolicyListener listener) {
        Objects.requireNonNull(listener);
@@ -6680,6 +6761,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        }
    }

    private int readUidAttribute(TypedXmlPullParser in, boolean forRestore, int userId)
            throws IOException {
        if (!forRestore) {
            return readIntAttribute(in, ATTR_UID);
        } else {
            return getUidForPackage(readStringAttribute(in, ATTR_XML_UTILS_NAME), userId);
        }
    }

    private String getPackageForUid(int uid) {
        try {
            return mContext.getPackageManager().getPackagesForUid(uid)[0];
        } catch (Exception e) {
            return null;
        }
    }

    private int getUidForPackage(String packageName, int userId) {
        try {
            return mContext.getPackageManager().getPackageUidAsUser(packageName,