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

Commit 6a79f29c authored by Dianne Hackborn's avatar Dianne Hackborn Committed by Android (Google) Code Review
Browse files

Merge "Initial impl issue #143085640: Per-process network access control"

parents c9bbffd9 f6729fae
Loading
Loading
Loading
Loading
+88 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * 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 android.content.pm;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArraySet;

/**
 * Information about a process an app may run.  This corresponds to information collected from the
 * AndroidManifest.xml's <permission-group> tags.
 * @hide
 */
public class ProcessInfo implements Parcelable {
    /**
     * The name of the process, fully-qualified based on the app's package name.
     */
    public String name;

    /**
     * If non-null, these are permissions that are not allowed in this process.
     */
    @Nullable
    public ArraySet<String> deniedPermissions;

    public ProcessInfo(String name, ArraySet<String> deniedPermissions) {
        this.name = name;
        this.deniedPermissions = deniedPermissions;
    }

    @Deprecated
    public ProcessInfo(@NonNull ProcessInfo orig) {
        this.name = orig.name;
        this.deniedPermissions = orig.deniedPermissions;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int parcelableFlags) {
        dest.writeString(this.name);
        final int numDenied = this.deniedPermissions != null
                ? this.deniedPermissions.size() : 0;
        dest.writeInt(numDenied);
        for (int i = 0; i < numDenied; i++) {
            dest.writeString(this.deniedPermissions.valueAt(i));
        }
    }

    public static final @NonNull Creator<ProcessInfo> CREATOR =
            new Creator<ProcessInfo>() {
                public ProcessInfo createFromParcel(Parcel source) {
                    return new ProcessInfo(source);
                }
                public ProcessInfo[] newArray(int size) {
                    return new ProcessInfo[size];
                }
            };

    private ProcessInfo(Parcel source) {
        this.name = source.readString();
        final int numDenied = source.readInt();
        if (numDenied > 0) {
            this.deniedPermissions = new ArraySet<>(numDenied);
            for (int i = numDenied - 1; i >= 0; i--) {
                this.deniedPermissions.add(TextUtils.safeIntern(source.readString()));
            }
        }
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.content.pm.parsing.ComponentParseUtils.ParsedService;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;

@@ -379,6 +380,9 @@ public interface AndroidPackage extends Parcelable {
    @Nullable
    long[] getUsesStaticLibrariesVersions();

    @Nullable
    ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses();

    int getVersionCode();

    int getVersionCodeMajor();
+15 −0
Original line number Diff line number Diff line
@@ -2425,6 +2425,21 @@ public class ApkParseUtils {

                    XmlUtils.skipCurrentTag(parser);

                    break;
                case "processes":
                    ArrayMap<String, ComponentParseUtils.ParsedProcess> processes =
                            ComponentParseUtils.parseProcesses(separateProcesses,
                                    parsingPackage,
                                    res, parser, flags,
                                    outError);
                    if (processes == null) {
                        return parseInput.error(
                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                                outError[0]
                        );
                    }

                    parsingPackage.setProcesses(processes);
                    break;
                case "uses-package":
                    // Dependencies for app installers; we don't currently try to
+250 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
@@ -1366,6 +1367,72 @@ public class ComponentParseUtils {
                };
    }

    public static class ParsedProcess implements Parcelable {

        public String name;
        @Nullable
        public ArraySet<String> deniedPermissions;

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(this.name);
            final int numDenied = this.deniedPermissions != null
                    ? this.deniedPermissions.size() : 0;
            dest.writeInt(numDenied);
            for (int i = 0; i < numDenied; i++) {
                dest.writeString(this.deniedPermissions.valueAt(i));
            }
        }

        public ParsedProcess() {
        }

        public ParsedProcess(@NonNull ParsedProcess other) {
            name = other.name;
            if (other.deniedPermissions != null) {
                deniedPermissions = new ArraySet<>(other.deniedPermissions);
            }
        }

        public void addStateFrom(@NonNull ParsedProcess other) {
            if (other.deniedPermissions != null) {
                for (int i = other.deniedPermissions.size() - 1; i >= 0; i--) {
                    if (deniedPermissions == null) {
                        deniedPermissions = new ArraySet<>(other.deniedPermissions.size());
                    }
                    deniedPermissions.add(other.deniedPermissions.valueAt(i));
                }
            }
        }

        protected ParsedProcess(Parcel in) {
            this.name = TextUtils.safeIntern(in.readString());
            final int numDenied = in.readInt();
            if (numDenied > 0) {
                this.deniedPermissions = new ArraySet<>(numDenied);
                this.deniedPermissions.add(TextUtils.safeIntern(in.readString()));
            }
        }

        public static final Creator<ParsedProcess> CREATOR =
                new Creator<ParsedProcess>() {
                    @Override
                    public ParsedProcess createFromParcel(Parcel source) {
                        return new ParsedProcess(source);
                    }

                    @Override
                    public ParsedProcess[] newArray(int size) {
                        return new ParsedProcess[size];
                    }
                };
    }

    public static ParsedActivity parseActivity(
            String[] separateProcesses,
            ParsingPackage parsingPackage,
@@ -3266,6 +3333,189 @@ public class ComponentParseUtils {
        return result;
    }

    private static @Nullable ArraySet<String> parseDenyPermission(
            ArraySet<String> perms,
            Resources res,
            XmlResourceParser parser,
            String[] outError
    ) throws IOException, XmlPullParserException {
        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
        if (sa == null) {
            outError[0] = "<deny-permission> could not be parsed";
            return null;
        }

        try {
            String perm = sa.getNonConfigurationString(
                    R.styleable.AndroidManifestDenyPermission_name,0);
            if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
                if (perms == null) {
                    perms = new ArraySet<>();
                }
                perms.add(perm);
            }
        } finally {
            sa.recycle();
        }
        XmlUtils.skipCurrentTag(parser);
        return perms;
    }

    private static ArraySet<String> parseAllowPermission(
            ArraySet<String> perms,
            Resources res,
            XmlResourceParser parser,
            String[] outError
    ) throws IOException, XmlPullParserException {
        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
        if (sa == null) {
            outError[0] = "<allow-permission> could not be parsed";
            return null;
        }

        try {
            String perm = sa.getNonConfigurationString(
                    R.styleable.AndroidManifestAllowPermission_name,0);
            if (perm != null && perm.equals(android.Manifest.permission.INTERNET)
                    && perms != null) {
                perms.remove(perm);
                if (perms.size() <= 0) {
                    perms = null;
                }
            }
        } finally {
            sa.recycle();
        }
        XmlUtils.skipCurrentTag(parser);
        return perms;
    }

    public static ParsedProcess parseProcess(
            ArraySet<String> perms,
            String[] separateProcesses,
            ParsingPackage parsingPackage,
            Resources res,
            XmlResourceParser parser,
            int flags,
            String[] outError
    ) throws IOException, XmlPullParserException {
        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
        if (sa == null) {
            outError[0] = "<process> could not be parsed";
            return null;
        }

        ParsedProcess proc = new ParsedProcess();
        if (perms != null) {
            proc.deniedPermissions = new ArraySet(perms);
        }

        try {
            proc.name = sa.getNonConfigurationString(
                    R.styleable.AndroidManifestProcess_process,0);
            proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
                    null, proc.name, flags, separateProcesses, outError);

            if (proc.name == null || proc.name.length() <= 0) {
                outError[0] = "<process> does not specify android:process";
                return null;
            }
            proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
                    parsingPackage.getPackageName(), proc.name,
                    flags, separateProcesses, outError);
            if (outError[0] != null) {
                return null;
            }
        } finally {
            sa.recycle();
        }

        int type;
        final int innerDepth = parser.getDepth();
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if (tagName.equals("deny-permission")) {
                proc.deniedPermissions = parseDenyPermission(proc.deniedPermissions, res, parser,
                        outError);
                if (outError[0] != null) {
                    return null;
                }
            } else if (tagName.equals("allow-permission")) {
                proc.deniedPermissions = parseAllowPermission(proc.deniedPermissions, res, parser,
                        outError);
                if (outError[0] != null) {
                    return null;
                }
            } else {
                Slog.w(TAG, "Unknown element under <process>: " + tagName
                        + " at " + parsingPackage.getBaseCodePath() + " "
                        + parser.getPositionDescription());
                XmlUtils.skipCurrentTag(parser);
                continue;
            }
        }

        return proc;
    }

    public static ArrayMap<String, ParsedProcess> parseProcesses(
            String[] separateProcesses,
            ParsingPackage parsingPackage,
            Resources res,
            XmlResourceParser parser,
            int flags,
            String[] outError
    ) throws IOException, XmlPullParserException {
        ArraySet<String> deniedPerms = null;
        ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();

        int type;
        final int innerDepth = parser.getDepth();
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if (tagName.equals("deny-permission")) {
                deniedPerms = parseDenyPermission(deniedPerms, res, parser, outError);
                if (outError[0] != null) {
                    return null;
                }
            } else if (tagName.equals("allow-permission")) {
                deniedPerms = parseAllowPermission(deniedPerms, res, parser, outError);
                if (outError[0] != null) {
                    return null;
                }
            } else if (tagName.equals("process")) {
                ParsedProcess proc = parseProcess(deniedPerms, separateProcesses, parsingPackage,
                        res, parser, flags, outError);
                if (outError[0] != null) {
                    return null;
                }
                if (processes.get(proc.name) != null) {
                    outError[0] = "<process> specified existing name '" + proc.name + "'";
                    return null;
                }
                processes.put(proc.name, proc);
            } else {
                Slog.w(TAG, "Unknown element under <processes>: " + tagName
                        + " at " + parsingPackage.getBaseCodePath() + " "
                        + parser.getPositionDescription());
                XmlUtils.skipCurrentTag(parser);
                continue;
            }
        }

        return processes;
    }

    public static ActivityInfo.WindowLayout parseLayout(Resources res, AttributeSet attrs) {
        TypedArray sw = res.obtainAttributes(attrs,
                R.styleable.AndroidManifestLayout);
+30 −0
Original line number Diff line number Diff line
@@ -215,6 +215,9 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
    @Nullable
    private ArrayList<String> queriesPackages;

    @Nullable
    private ArrayMap<String, ComponentParseUtils.ParsedProcess> processes;

    private String[] splitClassLoaderNames;
    private String[] splitCodePaths;
    private SparseArray<int[]> splitDependencies;
@@ -527,6 +530,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
        return usesStaticLibraries;
    }

    @Nullable
    @Override
    public ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses() {
        return processes;
    }

    @Override
    public boolean isBaseHardwareAccelerated() {
        return baseHardwareAccelerated;
@@ -947,6 +956,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
        return this;
    }

    @Override
    public PackageImpl setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes) {
        this.processes = processes;
        return this;
    }

    @Override
    public PackageImpl setSupportsSmallScreens(int supportsSmallScreens) {
        if (supportsSmallScreens == 1) {
@@ -3010,6 +3025,11 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
        dest.writeStringList(this.usesOptionalLibraries);
        dest.writeStringList(this.usesStaticLibraries);
        dest.writeLongArray(this.usesStaticLibrariesVersions);
        final int numProcesses = this.processes != null ? this.processes.size() : 0;
        dest.writeInt(numProcesses);
        for (int i = 0; i < numProcesses; i++) {
            this.processes.valueAt(i).writeToParcel(dest, 0);
        }

        if (this.usesStaticLibrariesCertDigests == null) {
            dest.writeInt(-1);
@@ -3161,6 +3181,16 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
        this.usesStaticLibraries = in.createStringArrayList();
        internStringArrayList(usesStaticLibraries);
        this.usesStaticLibrariesVersions = in.createLongArray();
        final int numProcesses = in.readInt();
        if (numProcesses > 0) {
            this.processes = new ArrayMap<>(numProcesses);
            for (int i = 0; i < numProcesses; i++) {
                ComponentParseUtils.ParsedProcess proc = new ComponentParseUtils.ParsedProcess(in);
                this.processes.put(proc.name, proc);
            }
        } else {
            this.processes = null;
        }

        int digestsSize = in.readInt();
        if (digestsSize >= 0) {
Loading