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

Commit 8bc4b047 authored by Alex Buynytskyy's avatar Alex Buynytskyy Committed by Android (Google) Code Review
Browse files

Merge "Implementing installation using DataLoader."

parents 1ec6418f 476138cc
Loading
Loading
Loading
Loading
+37 −14
Original line number Original line Diff line number Diff line
@@ -140,6 +140,7 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.LinkedList;
import java.util.List;
import java.util.List;
import java.util.Objects;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Collectors;


@@ -209,6 +210,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
    private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
    private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};
    private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};


    private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";

    // TODO: enforce INSTALL_ALLOW_TEST
    // TODO: enforce INSTALL_ALLOW_TEST
    // TODO: enforce INSTALL_ALLOW_DOWNGRADE
    // TODO: enforce INSTALL_ALLOW_DOWNGRADE


@@ -555,6 +558,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                                params.dataLoaderParams);
                                params.dataLoaderParams);
            }
            }
        }
        }

        if (isStreamingInstallation()
                && this.params.dataLoaderParams.getComponentName().getPackageName()
                == SYSTEM_DATA_LOADER_PACKAGE) {
            assertShellOrSystemCalling("System data loaders");
        }
    }
    }


    public SessionInfo generateInfo() {
    public SessionInfo generateInfo() {
@@ -770,6 +779,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        }
        }
    }
    }


    private void assertShellOrSystemCalling(String operation) {
        switch (Binder.getCallingUid()) {
            case android.os.Process.SHELL_UID:
            case android.os.Process.ROOT_UID:
            case android.os.Process.SYSTEM_UID:
                break;
            default:
                throw new SecurityException(operation + " only supported from shell or system");
        }
    }

    private void assertCanWrite(boolean reverseMode) {
    private void assertCanWrite(boolean reverseMode) {
        if (isDataLoaderInstallation()) {
        if (isDataLoaderInstallation()) {
            throw new IllegalStateException(
            throw new IllegalStateException(
@@ -780,15 +800,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            assertPreparedAndNotSealedLocked("assertCanWrite");
            assertPreparedAndNotSealedLocked("assertCanWrite");
        }
        }
        if (reverseMode) {
        if (reverseMode) {
            switch (Binder.getCallingUid()) {
            assertShellOrSystemCalling("Reverse mode");
                case android.os.Process.SHELL_UID:
                case android.os.Process.ROOT_UID:
                case android.os.Process.SYSTEM_UID:
                    break;
                default:
                    throw new SecurityException(
                            "Reverse mode only supported from shell or system");
            }
        }
        }
    }
    }


@@ -1025,13 +1037,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    }
    }


    private class FileSystemConnector extends IPackageInstallerSessionFileSystemConnector.Stub {
    private final class FileSystemConnector extends
            IPackageInstallerSessionFileSystemConnector.Stub {
        final Set<String> mAddedFiles;

        FileSystemConnector(List<InstallationFile> addedFiles) {
            mAddedFiles = addedFiles.stream().map(file -> file.getName()).collect(
                    Collectors.toSet());
        }

        @Override
        @Override
        public void writeData(String name, long offsetBytes, long lengthBytes,
        public void writeData(String name, long offsetBytes, long lengthBytes,
                ParcelFileDescriptor incomingFd) {
                ParcelFileDescriptor incomingFd) {
            if (incomingFd == null) {
            if (incomingFd == null) {
                throw new IllegalArgumentException("incomingFd can't be null");
                throw new IllegalArgumentException("incomingFd can't be null");
            }
            }
            if (!mAddedFiles.contains(name)) {
                throw new SecurityException("File name is not in the list of added files.");
            }
            try {
            try {
                doWriteInternal(name, offsetBytes, lengthBytes, incomingFd);
                doWriteInternal(name, offsetBytes, lengthBytes, incomingFd);
            } catch (IOException e) {
            } catch (IOException e) {
@@ -2467,8 +2490,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            return;
            return;
        }
        }


        FileSystemConnector connector = new FileSystemConnector();

        List<InstallationFile> addedFiles = mFiles.stream().filter(
        List<InstallationFile> addedFiles = mFiles.stream().filter(
                file -> sAddedFilter.accept(new File(file.name))).map(
                file -> sAddedFilter.accept(new File(file.name))).map(
                    file -> new InstallationFile(
                    file -> new InstallationFile(
@@ -2480,6 +2501,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                            0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect(
                            0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect(
                Collectors.toList());
                Collectors.toList());


        final FileSystemConnector connector = new FileSystemConnector(addedFiles);

        DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
        DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
        if (dataLoaderManager == null) {
        if (dataLoaderManager == null) {
            throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
            throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
@@ -2515,11 +2538,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            }
            }
        };
        };


        final DataLoaderParams params = this.params.dataLoaderParams;

        final FileSystemControlParcel control = new FileSystemControlParcel();
        final FileSystemControlParcel control = new FileSystemControlParcel();
        control.callback = connector;
        control.callback = connector;


        final DataLoaderParams params = this.params.dataLoaderParams;

        Bundle dataLoaderParams = new Bundle();
        Bundle dataLoaderParams = new Bundle();
        dataLoaderParams.putParcelable("componentName", params.getComponentName());
        dataLoaderParams.putParcelable("componentName", params.getComponentName());
        dataLoaderParams.putParcelable("control", control);
        dataLoaderParams.putParcelable("control", control);
+3 −9
Original line number Original line Diff line number Diff line
@@ -34,7 +34,6 @@ import android.content.IIntentSender;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderParams;
import android.content.pm.FeatureInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstaller;
@@ -137,10 +136,6 @@ class PackageManagerShellCommand extends ShellCommand {
    private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
    private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
    private static final int DEFAULT_WAIT_MS = 60 * 1000;
    private static final int DEFAULT_WAIT_MS = 60 * 1000;


    private static final String DATA_LOADER_PACKAGE = "android";
    private static final String DATA_LOADER_CLASS =
            "com.android.server.pm.PackageManagerShellCommandDataLoader";

    final IPackageManager mInterface;
    final IPackageManager mInterface;
    final IPermissionManager mPermissionManager;
    final IPermissionManager mPermissionManager;
    final private WeakHashMap<String, Resources> mResourceCache =
    final private WeakHashMap<String, Resources> mResourceCache =
@@ -1163,9 +1158,8 @@ class PackageManagerShellCommand extends ShellCommand {
    private int runStreamingInstall() throws RemoteException {
    private int runStreamingInstall() throws RemoteException {
        final InstallParams params = makeInstallParams();
        final InstallParams params = makeInstallParams();
        if (params.sessionParams.dataLoaderParams == null) {
        if (params.sessionParams.dataLoaderParams == null) {
            final DataLoaderParams dataLoaderParams = DataLoaderParams.forStreaming(
            params.sessionParams.setDataLoaderParams(
                    new ComponentName(DATA_LOADER_PACKAGE, DATA_LOADER_CLASS), "");
                    PackageManagerShellCommandDataLoader.getDataLoaderParams(this));
            params.sessionParams.setDataLoaderParams(dataLoaderParams);
        }
        }
        return doRunInstall(params);
        return doRunInstall(params);
    }
    }
+88 −16
Original line number Original line Diff line number Diff line
@@ -17,18 +17,22 @@
package com.android.server.pm;
package com.android.server.pm;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.pm.DataLoaderParams;
import android.content.pm.DataLoaderParams;
import android.content.pm.InstallationFile;
import android.content.pm.InstallationFile;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.os.ShellCommand;
import android.service.dataloader.DataLoaderService;
import android.service.dataloader.DataLoaderService;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArray;


import libcore.io.IoUtils;
import libcore.io.IoUtils;


import java.io.File;
import java.io.IOException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Collection;


/**
/**
@@ -37,41 +41,109 @@ import java.util.Collection;
public class PackageManagerShellCommandDataLoader extends DataLoaderService {
public class PackageManagerShellCommandDataLoader extends DataLoaderService {
    public static final String TAG = "PackageManagerShellCommandDataLoader";
    public static final String TAG = "PackageManagerShellCommandDataLoader";


    static class DataLoader implements DataLoaderService.DataLoader {
    private static final String PACKAGE = "android";
        private ParcelFileDescriptor mInFd = null;
    private static final String CLASS = PackageManagerShellCommandDataLoader.class.getName();
        private FileSystemConnector mConnector = null;

    static final SecureRandom sRandom = new SecureRandom();
    static final SparseArray<WeakReference<ShellCommand>> sShellCommands = new SparseArray<>();

    private static final char ARGS_DELIM = '&';
    private static final String SHELL_COMMAND_ID_PREFIX = "shellCommandId=";
    private static final int INVALID_SHELL_COMMAND_ID = -1;
    private static final int TOO_MANY_PENDING_SHELL_COMMANDS = 10;


    private static final String STDIN_PATH = "-";
    private static final String STDIN_PATH = "-";


    static DataLoaderParams getDataLoaderParams(ShellCommand shellCommand) {
        int commandId;
        synchronized (sShellCommands) {
            // Clean up old references.
            for (int i = sShellCommands.size() - 1; i >= 0; i--) {
                WeakReference<ShellCommand> oldRef = sShellCommands.valueAt(i);
                if (oldRef.get() == null) {
                    sShellCommands.removeAt(i);
                }
            }

            // Sanity check.
            if (sShellCommands.size() > TOO_MANY_PENDING_SHELL_COMMANDS) {
                Slog.e(TAG, "Too many pending shell commands: " + sShellCommands.size());
            }

            // Generate new id and put ref to the array.
            do {
                commandId = sRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
            } while (sShellCommands.contains(commandId));

            sShellCommands.put(commandId, new WeakReference<>(shellCommand));
        }

        final String args = SHELL_COMMAND_ID_PREFIX + commandId;
        return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS), args);
    }

    private static int extractShellCommandId(String args) {
        int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX);
        if (sessionIdIdx < 0) {
            Slog.e(TAG, "Missing shell command id param.");
            return INVALID_SHELL_COMMAND_ID;
        }
        sessionIdIdx += SHELL_COMMAND_ID_PREFIX.length();
        int delimIdx = args.indexOf(ARGS_DELIM, sessionIdIdx);
        try {
            if (delimIdx < 0) {
                return Integer.parseInt(args.substring(sessionIdIdx));
            } else {
                return Integer.parseInt(args.substring(sessionIdIdx, delimIdx));
            }
        } catch (NumberFormatException e) {
            Slog.e(TAG, "Incorrect shell command id format.", e);
            return INVALID_SHELL_COMMAND_ID;
        }
    }

    static class DataLoader implements DataLoaderService.DataLoader {
        private DataLoaderParams mParams = null;
        private FileSystemConnector mConnector = null;

        @Override
        @Override
        public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams,
        public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams,
                @NonNull FileSystemConnector connector) {
                @NonNull FileSystemConnector connector) {
            mParams = dataLoaderParams;
            mConnector = connector;
            mConnector = connector;
            return true;
            return true;
        }
        }

        @Override
        @Override
        public boolean onPrepareImage(Collection<InstallationFile> addedFiles,
        public boolean onPrepareImage(Collection<InstallationFile> addedFiles,
                Collection<String> removedFiles) {
                Collection<String> removedFiles) {
            final int commandId = extractShellCommandId(mParams.getArguments());
            if (commandId == INVALID_SHELL_COMMAND_ID) {
                return false;
            }

            final WeakReference<ShellCommand> shellCommandRef;
            synchronized (sShellCommands) {
                shellCommandRef = sShellCommands.get(commandId, null);
            }
            final ShellCommand shellCommand =
                    shellCommandRef != null ? shellCommandRef.get() : null;
            if (shellCommand == null) {
                Slog.e(TAG, "Missing shell command.");
                return false;
            }
            try {
            try {
                for (InstallationFile fileInfo : addedFiles) {
                for (InstallationFile fileInfo : addedFiles) {
                    String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8);
                    String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8);
                    if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
                    if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
                        // TODO(b/146080380): add support for STDIN installations.
                        final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
                        if (mInFd == null) {
                                shellCommand.getInFileDescriptor());
                            Slog.e(TAG, "Invalid stdin file descriptor.");
                            return false;
                        }
                        ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
                                mInFd.getFileDescriptor());
                        mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd);
                        mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd);
                    } else {
                    } else {
                        File localFile = new File(filePath);
                        ParcelFileDescriptor incomingFd = null;
                        ParcelFileDescriptor incomingFd = null;
                        try {
                        try {
                            // TODO(b/146080380): open files via callback into shell command.
                            incomingFd = shellCommand.openFileForSystem(filePath, "r");
                            incomingFd = ParcelFileDescriptor.open(localFile,
                            mConnector.writeData(fileInfo.getName(), 0, incomingFd.getStatSize(),
                                    ParcelFileDescriptor.MODE_READ_ONLY);
                            mConnector.writeData(fileInfo.getName(), 0, localFile.length(),
                                    incomingFd);
                                    incomingFd);
                        } finally {
                        } finally {
                            IoUtils.closeQuietly(incomingFd);
                            IoUtils.closeQuietly(incomingFd);