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

Commit 843cd7b1 authored by Chun-Wei Wang's avatar Chun-Wei Wang Committed by Android (Google) Code Review
Browse files

Merge "Install constraints API"

parents befaeff5 c0def4a5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -11876,6 +11876,7 @@ package android.content.pm {
  public class PackageInstaller {
    method public void abandonSession(int);
    method public void checkInstallConstraints(@NonNull java.util.List<java.lang.String>, @NonNull android.content.pm.PackageInstaller.InstallConstraints, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.content.pm.PackageInstaller.InstallConstraintsResult>);
    method public void commitSessionAfterInstallConstraintsAreMet(int, @NonNull android.content.IntentSender, @NonNull android.content.pm.PackageInstaller.InstallConstraints, long);
    method public int createSession(@NonNull android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
    method @Deprecated @Nullable public android.content.pm.PackageInstaller.SessionInfo getActiveStagedSession();
    method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getActiveStagedSessions();
@@ -11920,6 +11921,7 @@ package android.content.pm {
    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_STORAGE = 6; // 0x6
    field public static final int STATUS_FAILURE_TIMEOUT = 8; // 0x8
    field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
    field public static final int STATUS_SUCCESS = 0; // 0x0
  }
+2 −0
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@ interface IPackageInstallerSession {
    void commit(in IntentSender statusReceiver, boolean forTransferred);
    void transfer(in String packageName);
    void abandon();
    void seal();
    List<String> fetchPackageNames();

    DataLoaderParamsParcel getDataLoaderParams();
    void addFile(int location, String name, long lengthBytes, in byte[] metadata, in byte[] signature);
+69 −2
Original line number Diff line number Diff line
@@ -44,8 +44,11 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityThread;
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;
@@ -59,9 +62,11 @@ import android.graphics.Bitmap;
import android.icu.util.ULocale;
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;
@@ -226,8 +231,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}, or
     * {@link #STATUS_FAILURE_STORAGE}.
     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID},
     * {@link #STATUS_FAILURE_STORAGE}, or {@link #STATUS_FAILURE_TIMEOUT}.
     * <p>
     * More information about a status may be available through additional
     * extras; see the individual status documentation for details.
@@ -440,6 +445,13 @@ public class PackageInstaller {
     */
    public static final int STATUS_FAILURE_INCOMPATIBLE = 7;

    /**
     * The operation failed because it didn't complete within the specified timeout.
     *
     * @see #EXTRA_STATUS_MESSAGE
     */
    public static final int STATUS_FAILURE_TIMEOUT = 8;

    /**
     * Default value, non-streaming installation session.
     *
@@ -1015,6 +1027,61 @@ public class PackageInstaller {
        }
    }

    /**
     * Commit the session when all constraints are satisfied. This is a convenient method to
     * combine {@link #waitForInstallConstraints(List, InstallConstraints, IntentSender, long)}
     * and {@link Session#commit(IntentSender)}.
     * <p>
     * Once this method is called, the session is sealed and no additional mutations
     * may be performed on the session. In the case of timeout, you may commit the
     * session again using this method or {@link Session#commit(IntentSender)} for retries.
     *
     * @param statusReceiver Called when the state of the session changes. Intents
     *                       sent to this receiver contain {@link #EXTRA_STATUS}.
     *                       Refer to the individual status codes on how to handle them.
     * @param constraints The requirements to satisfy before committing the session.
     * @param timeoutMillis The maximum time to wait, in milliseconds until the
     *                      constraints are satisfied. The caller will be notified via
     *                      {@code statusReceiver} if timeout happens before commit.
     */
    public void commitSessionAfterInstallConstraintsAreMet(int sessionId,
            @NonNull IntentSender statusReceiver, @NonNull InstallConstraints constraints,
            @DurationMillisLong long timeoutMillis) {
        try {
            var session = mInstaller.openSession(sessionId);
            session.seal();
            var packageNames = session.fetchPackageNames();
            var intentSender = new IntentSender((IIntentSender) new IIntentSender.Stub() {
                @Override
                public void send(int code, Intent intent, String resolvedType,
                        IBinder allowlistToken, IIntentReceiver finishedReceiver,
                        String requiredPermission, Bundle options)  {
                    var result = intent.getParcelableExtra(
                            PackageInstaller.EXTRA_INSTALL_CONSTRAINTS_RESULT,
                            InstallConstraintsResult.class);
                    try {
                        if (result.isAllConstraintsSatisfied()) {
                            session.commit(statusReceiver, false);
                        } else {
                            // timeout
                            final Intent fillIn = new Intent();
                            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
                            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, STATUS_FAILURE_TIMEOUT);
                            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
                                    "Install constraints not satisfied within timeout");
                            statusReceiver.sendIntent(
                                    ActivityThread.currentApplication(), 0, fillIn, null, null);
                        }
                    } catch (Exception ignore) {
                    }
                }
            });
            waitForInstallConstraints(packageNames, constraints, intentSender, timeoutMillis);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Events for observing session lifecycle.
     * <p>
+59 −0
Original line number Diff line number Diff line
@@ -1842,6 +1842,60 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        dispatchSessionSealed();
    }

    @Override
    public void seal() {
        assertNotChild("seal");
        assertCallerIsOwnerOrRoot();
        try {
            sealInternal();
            for (var child : getChildSessions()) {
                child.sealInternal();
            }
        } catch (PackageManagerException e) {
            throw new IllegalStateException("Package is not valid", e);
        }
    }

    private void sealInternal() throws PackageManagerException {
        synchronized (mLock) {
            sealLocked();
        }
    }

    @Override
    public List<String> fetchPackageNames() {
        assertNotChild("fetchPackageNames");
        assertCallerIsOwnerOrRoot();
        var sessions = getSelfOrChildSessions();
        var result = new ArrayList<String>(sessions.size());
        for (var s : sessions) {
            result.add(s.fetchPackageName());
        }
        return result;
    }

    private String fetchPackageName() {
        assertSealed("fetchPackageName");
        synchronized (mLock) {
            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
            final List<File> addedFiles = getAddedApksLocked();
            for (File addedFile : addedFiles) {
                final ParseResult<ApkLite> result =
                        ApkLiteParseUtils.parseApkLite(input.reset(), addedFile, 0);
                if (result.isError()) {
                    throw new IllegalStateException(
                            "Can't parse package for session=" + sessionId, result.getException());
                }
                final ApkLite apk = result.getResult();
                var packageName = apk.getPackageName();
                if (packageName != null) {
                    return packageName;
                }
            }
            throw new IllegalStateException("Can't fetch package name for session=" + sessionId);
        }
    }

    /**
     * Kicks off the install flow. The first step is to persist 'sealed' flags
     * to prevent mutations of hard links created later.
@@ -2095,6 +2149,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        }
    }

    @NonNull
    private List<PackageInstallerSession> getSelfOrChildSessions() {
        return isMultiPackage() ? getChildSessions() : Collections.singletonList(this);
    }

    /**
     * Seal the session to prevent further modification.
     *