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

Commit 354d6697 authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

Remove validation of APKs on transfer.

It's unnecessary as it will be done on commit anyway.
This allows to make transfer simple and blocking.

Test: atest PackageManagerShellCommandTest
Bug: b/136132412

Change-Id: Ib017c6dc8a845a7d72898b0c0032c17932375c96
parent 7d8deb56
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -11700,11 +11700,8 @@ package android.content.pm {
    field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
    field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
    field public static final int STATUS_FAILURE_CONFLICT = 5; // 0x5
    field public static final int STATUS_FAILURE_ILLEGAL_STATE = 9; // 0x9
    field public static final int STATUS_FAILURE_INCOMPATIBLE = 7; // 0x7
    field public static final int STATUS_FAILURE_INVALID = 4; // 0x4
    field public static final int STATUS_FAILURE_NAME_NOT_FOUND = 8; // 0x8
    field public static final int STATUS_FAILURE_SECURITY = 10; // 0xa
    field public static final int STATUS_FAILURE_STORAGE = 6; // 0x6
    field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
    field public static final int STATUS_SUCCESS = 0; // 0x0
@@ -11726,7 +11723,6 @@ package android.content.pm {
    method public void removeChildSessionId(int);
    method public void removeSplit(@NonNull String) throws java.io.IOException;
    method public void setStagingProgress(float);
    method public void transfer(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
    method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
  }
+1 −1
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@ interface IPackageInstallerSession {

    void close();
    void commit(in IntentSender statusReceiver, boolean forTransferred);
    void transfer(in String packageName, in IntentSender statusReceiver);
    void transfer(in String packageName);
    void abandon();

    void addFile(String name, long lengthBytes, in byte[] metadata);
+5 −108
Original line number Diff line number Diff line
@@ -29,8 +29,6 @@ import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager.DeleteFlags;
@@ -38,11 +36,9 @@ import android.content.pm.PackageManager.InstallReason;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.FileBridge;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -71,8 +67,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

@@ -182,9 +176,8 @@ public class PackageInstaller {
     * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
     * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
     * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID},
     * {@link #STATUS_FAILURE_STORAGE}, {@link #STATUS_FAILURE_NAME_NOT_FOUND},
     * {@link #STATUS_FAILURE_ILLEGAL_STATE} or {@link #STATUS_FAILURE_SECURITY}.
     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
     * {@link #STATUS_FAILURE_STORAGE}.
     * <p>
     * More information about a status may be available through additional
     * extras; see the individual status documentation for details.
@@ -332,34 +325,6 @@ public class PackageInstaller {
     */
    public static final int STATUS_FAILURE_INCOMPATIBLE = 7;

    /**
     * The transfer failed because a target package can't be found. For example
     * transferring a session to a non-existing package.
     * <p>
     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
     * missing package.
     *
     * @see #EXTRA_STATUS_MESSAGE
     * @see #EXTRA_OTHER_PACKAGE_NAME
     */
    public static final int STATUS_FAILURE_NAME_NOT_FOUND = 8;

    /**
     * The transfer failed because a session is in invalid state. For example
     * transferring an already committed session.
     *
     * @see #EXTRA_STATUS_MESSAGE
     */
    public static final int STATUS_FAILURE_ILLEGAL_STATE = 9;

    /**
     * The transfer failed for security reasons. For example transferring
     * to a package which does not have INSTALL_PACKAGES permission.
     *
     * @see #EXTRA_STATUS_MESSAGE
     */
    public static final int STATUS_FAILURE_SECURITY = 10;

    private final IPackageInstaller mInstaller;
    private final int mUserId;
    private final String mInstallerPackageName;
@@ -1143,8 +1108,7 @@ public class PackageInstaller {
        }

        /**
         * Attempt to commit a session that has been {@link #transfer(String, IntentSender)
         * transferred}.
         * Attempt to commit a session that has been {@link #transfer(String) transferred}.
         *
         * <p>If the device reboots before the session has been finalized, you may commit the
         * session again.
@@ -1185,14 +1149,6 @@ public class PackageInstaller {
         *
         * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
         *                    permission.
         * @param statusReceiver Called when the state of the session changes. Intents sent to
         *                       this receiver contain {@link #EXTRA_STATUS}. Possible statuses:
         *                       {@link #STATUS_FAILURE_NAME_NOT_FOUND},
         *                       {@link #STATUS_FAILURE_ILLEGAL_STATE},
         *                       {@link #STATUS_FAILURE_SECURITY},
         *                       {@link #STATUS_FAILURE}.
         *                       Refer to the individual transfer status codes on how to handle
         *                       them.
         *
         * @throws PackageManager.NameNotFoundException if the new owner could not be found.
         * @throws SecurityException if called after the session has been committed or abandoned.
@@ -1200,13 +1156,12 @@ public class PackageInstaller {
         * @throws SecurityException if streams opened through
         *                           {@link #openWrite(String, long, long) are still open.
         */
        public void transfer(@NonNull String packageName, @NonNull IntentSender statusReceiver)
        public void transfer(@NonNull String packageName)
                throws PackageManager.NameNotFoundException {
            Objects.requireNonNull(statusReceiver);
            Objects.requireNonNull(packageName);

            try {
                mSession.transfer(packageName, statusReceiver);
                mSession.transfer(packageName);
            } catch (ParcelableException e) {
                e.maybeRethrow(PackageManager.NameNotFoundException.class);
                throw new RuntimeException(e);
@@ -1215,64 +1170,6 @@ public class PackageInstaller {
            }
        }

        /**
         * Transfer the session to a new owner.
         * This is a convenience blocking wrapper around {@link #transfer(String, IntentSender)}.
         * Converts all statuses into exceptions.
         *
         * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
         *                    permission.
         *
         * @throws PackageManager.NameNotFoundException if the new owner could not be found.
         * @throws SecurityException if called after the session has been committed or abandoned.
         * @throws SecurityException if the session does not update the original installer
         * @throws SecurityException if streams opened through
         *                           {@link #openWrite(String, long, long) are still open.
         */
        public void transfer(@NonNull String packageName)
                throws PackageManager.NameNotFoundException {
            Objects.requireNonNull(packageName);

            CompletableFuture<Intent> intentFuture = new CompletableFuture<Intent>();
            try {
                IIntentSender localSender = new IIntentSender.Stub() {
                    @Override
                    public void send(int code, Intent intent, String resolvedType,
                            IBinder whitelistToken,
                            IIntentReceiver finishedReceiver, String requiredPermission,
                            Bundle options) {
                        intentFuture.complete(intent);
                    }
                };
                transfer(packageName, new IntentSender(localSender));
            } catch (ParcelableException e) {
                e.maybeRethrow(PackageManager.NameNotFoundException.class);
                throw new RuntimeException(e);
            }

            try {
                Intent intent = intentFuture.get();
                final int status = intent.getIntExtra(EXTRA_STATUS, Integer.MIN_VALUE);
                final String statusMessage = intent.getStringExtra(EXTRA_STATUS_MESSAGE);
                switch (status) {
                    case STATUS_SUCCESS:
                        break;
                    case STATUS_FAILURE_NAME_NOT_FOUND:
                        throw new PackageManager.NameNotFoundException(statusMessage);
                    case STATUS_FAILURE_ILLEGAL_STATE:
                        throw new IllegalStateException(statusMessage);
                    case STATUS_FAILURE_SECURITY:
                        throw new SecurityException(statusMessage);
                    default:
                        throw new RuntimeException(statusMessage);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * Release this session object. You can open the session again if it
         * hasn't been finalized.
+22 −109
Original line number Diff line number Diff line
@@ -152,7 +152,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private static final int MSG_COMMIT = 1;
    private static final int MSG_ON_PACKAGE_INSTALLED = 2;
    private static final int MSG_SEAL = 3;
    private static final int MSG_TRANSFER = 4;

    /** XML constants used for persisting a session */
    static final String TAG_SESSION = "session";
@@ -388,32 +387,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            SomeArgs args;
            String packageName;
            IntentSender statusReceiver;
            switch (msg.what) {
                case MSG_SEAL:
                    statusReceiver = (IntentSender) msg.obj;

                    handleSeal(statusReceiver);
                    handleSeal((IntentSender) msg.obj);
                    break;
                case MSG_COMMIT:
                    handleCommit();
                    break;
                case MSG_TRANSFER:
                    args = (SomeArgs) msg.obj;
                    packageName = (String) args.arg1;
                    statusReceiver = (IntentSender) args.arg2;
                    args.recycle();

                    handleTransfer(statusReceiver, packageName);
                    break;
                case MSG_ON_PACKAGE_INSTALLED:
                    args = (SomeArgs) msg.obj;
                    packageName = (String) args.arg1;
                    final SomeArgs args = (SomeArgs) msg.obj;
                    final String packageName = (String) args.arg1;
                    final String message = (String) args.arg2;
                    final Bundle extras = (Bundle) args.arg3;
                    statusReceiver = (IntentSender) args.arg4;
                    final IntentSender statusReceiver = (IntentSender) args.arg4;
                    final int returnCode = args.argi1;
                    args.recycle();

@@ -459,7 +445,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     * Checks if the permissions still need to be confirmed.
     *
     * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
     * installer might still {@link #transfer(String, IntentSender) change}.
     * installer might still {@link #transfer(String) change}.
     *
     * @return {@code true} iff we need to ask to confirm the permissions?
     */
@@ -1403,11 +1389,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        }
    }

    private int assertCanBeTransferredAndReturnNewOwner(String packageName)
            throws PackageManager.NameNotFoundException {
    @Override
    public void transfer(String packageName) {
        Objects.requireNonNull(packageName);

        ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
        if (newOwnerAppInfo == null) {
            throw new PackageManager.NameNotFoundException(packageName);
            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
        }

        if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
@@ -1422,106 +1410,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            throw new SecurityException("Can only transfer sessions that use public options");
        }

        return newOwnerAppInfo.uid;
    }

    @Override
    public void transfer(String packageName, IntentSender statusReceiver) {
        Objects.requireNonNull(statusReceiver);
        Objects.requireNonNull(packageName);

        try {
            assertCanBeTransferredAndReturnNewOwner(packageName);
        } catch (PackageManager.NameNotFoundException e) {
            throw new ParcelableException(e);
        }

        synchronized (mLock) {
            assertCallerIsOwnerOrRootLocked();
            assertPreparedAndNotSealedLocked("transfer");
        }

        final SomeArgs args = SomeArgs.obtain();
        args.arg1 = packageName;
        args.arg2 = statusReceiver;

        mHandler.obtainMessage(MSG_TRANSFER, args).sendToTarget();
    }

    private void handleTransfer(IntentSender statusReceiver, String packageName) {
        List<PackageInstallerSession> childSessions = getChildSessions();

        try {
            final int uid = assertCanBeTransferredAndReturnNewOwner(packageName);

        synchronized (mLock) {
            assertCallerIsOwnerOrRootLocked();
            assertPreparedAndNotSealedLocked("transfer");

            try {
                    sealAndValidateLocked(childSessions);
                } catch (StreamingException e) {
                    throw new IllegalArgumentException("Streaming failed", e);
                sealLocked(childSessions);
            } catch (PackageManagerException e) {
                throw new IllegalArgumentException("Package is not valid", e);
            }

            if (!mPackageName.equals(mInstallSource.installerPackageName)) {
                    throw new SecurityException(
                            "Can only transfer sessions that update the original installer");
                throw new SecurityException("Can only transfer sessions that update the original "
                        + "installer");
            }

                mInstallerUid = uid;
            mInstallerUid = newOwnerAppInfo.uid;
            mInstallSource = InstallSource.create(packageName, null, packageName);
        }
        } catch (PackageManager.NameNotFoundException e) {
            onSessionTransferStatus(statusReceiver, packageName,
                    PackageInstaller.STATUS_FAILURE_NAME_NOT_FOUND, e.getMessage());
            return;
        } catch (IllegalStateException e) {
            onSessionTransferStatus(statusReceiver, packageName,
                    PackageInstaller.STATUS_FAILURE_ILLEGAL_STATE, e.getMessage());
            return;
        } catch (SecurityException e) {
            onSessionTransferStatus(statusReceiver, packageName,
                    PackageInstaller.STATUS_FAILURE_SECURITY, e.getMessage());
            return;
        } catch (Throwable e) {
            onSessionTransferStatus(statusReceiver, packageName, PackageInstaller.STATUS_FAILURE,
                    e.getMessage());
            return;
        }

        // Persist the fact that we've sealed ourselves to prevent
        // mutations of any hard links we create. We do this without holding
        // the session lock, since otherwise it's a lock inversion.
        mCallback.onSessionSealedBlocking(this);

        // Report success.
        onSessionTransferStatus(statusReceiver, packageName, PackageInstaller.STATUS_SUCCESS, null);
    }

    private void onSessionTransferStatus(IntentSender statusReceiver, String otherPackageName,
            int status, String statusMessage) {
        final String packageName;
        synchronized (mLock) {
            packageName = mPackageName;
        }

        final Intent fillIn = new Intent();
        fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);
        fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
        fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, otherPackageName);

        fillIn.putExtra(PackageInstaller.EXTRA_STATUS, status);
        if (!TextUtils.isEmpty(statusMessage)) {
            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
        }

        try {
            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
        } catch (IntentSender.SendIntentException ignored) {
        }

    }

    private void handleCommit() {