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

Commit 941a8ba1 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Installing splits into ASECs!

Sessions can now zero-copy data directly into pre-allocated ASEC
containers.  Then at commit time, we compute the total size of the
final app, including any inherited APKs and unpacked libraries, and
resize the container in one step.

This supports both brand new ASEC installs and inheriting from
existing ASEC installs.  To keep things simple, it currently requires
copying any inherited ASEC contents, but this could be optimized in
the future.

Expose new vold resize command, and allow read-write mounting of ASEC
containers.  Move native library extraction into the installer flow,
since it needs to happen before ASEC is sealed.  Move multiArch flag
into NativeLibraryHelper, instead of making everyone pass it
around.  Migrate size calculation to shared location.

Separate "other" package name in public API, provide a path to a
storage device when relevant, and add more docs.

Bug: 16514385
Change-Id: I06c6ce588d312ee7e64cce02733895d640b88456
parent 7653a30e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -8680,10 +8680,12 @@ package android.content.pm {
    method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
    method public void uninstall(java.lang.String, android.content.IntentSender);
    field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
    field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
    field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
    field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
    field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
    field public static final java.lang.String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
    field public static final int STATUS_FAILURE = 1; // 0x1
    field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
    field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
+4 −0
Original line number Diff line number Diff line
@@ -1004,6 +1004,10 @@ public final class Pm {
                params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
            } else if (opt.equals("-p")) {
                params.mode = SessionParams.MODE_INHERIT_EXISTING;
                params.appPackageName = nextOptionData();
                if (params.appPackageName == null) {
                    throw new IllegalArgumentException("Missing inherit package name");
                }
            } else if (opt.equals("-S")) {
                params.setSize(Long.parseLong(nextOptionData()));
            } else if (opt.equals("--abi")) {
+60 −5
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

@@ -86,6 +87,8 @@ public class PackageInstaller {
     * <p>
     * In some cases, a matching Activity may not exist, so ensure you safeguard
     * against this.
     * <p>
     * The session to show details for is defined in {@link #EXTRA_SESSION_ID}.
     */
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
@@ -95,21 +98,57 @@ public class PackageInstaller {
            ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";

    /**
     * An integer session ID.
     * An integer session ID that an operation is working with.
     *
     * @see #ACTION_SESSION_DETAILS
     * @see Intent#getIntExtra(String, int)
     */
    public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";

    /**
     * Package name that an operation is working with.
     *
     * @see Intent#getStringExtra(String)
     */
    public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";

    /**
     * Current status of an operation. Will be one of
     * {@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}.
     * <p>
     * More information about a status may be available through additional
     * extras; see the individual status documentation for details.
     *
     * @see Intent#getIntExtra(String, int)
     */
    public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";

    /**
     * Detailed string representation of the status, including raw details that
     * are useful for debugging.
     *
     * @see Intent#getStringExtra(String)
     */
    public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";

    /**
     * Package name relevant to a status.
     * Another package name relevant to a status. This is typically the package
     * responsible for causing an operation failure.
     *
     * @see Intent#getStringExtra(String)
     */
    public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
    public static final String
            EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";

    /**
     * Storage path relevant to a status.
     *
     * @see Intent#getStringExtra(String)
     */
    public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";

    /** {@hide} */
    @Deprecated
@@ -153,8 +192,12 @@ public class PackageInstaller {
     * The operation failed because it was blocked. For example, a device policy
     * may be blocking the operation, a package verifier may have blocked the
     * operation, or the app may be required for core system operation.
     * <p>
     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
     * specific package blocking the install.
     *
     * @see #EXTRA_STATUS_MESSAGE
     * @see #EXTRA_OTHER_PACKAGE_NAME
     */
    public static final int STATUS_FAILURE_BLOCKED = 2;

@@ -182,10 +225,11 @@ public class PackageInstaller {
     * permission, incompatible certificates, etc. The user may be able to
     * uninstall another app to fix the issue.
     * <p>
     * The result may also contain {@link #EXTRA_PACKAGE_NAME} with the
     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
     * specific package identified as the cause of the conflict.
     *
     * @see #EXTRA_STATUS_MESSAGE
     * @see #EXTRA_OTHER_PACKAGE_NAME
     */
    public static final int STATUS_FAILURE_CONFLICT = 5;

@@ -193,8 +237,12 @@ public class PackageInstaller {
     * The operation failed because of storage issues. For example, the device
     * may be running low on space, or external media may be unavailable. The
     * user may be able to help free space or insert different external media.
     * <p>
     * The result may also contain {@link #EXTRA_STORAGE_PATH} with the path to
     * the storage device that caused the failure.
     *
     * @see #EXTRA_STATUS_MESSAGE
     * @see #EXTRA_STORAGE_PATH
     */
    public static final int STATUS_FAILURE_STORAGE = 6;

@@ -281,6 +329,13 @@ public class PackageInstaller {
     * To succeed, the caller must be the current home app.
     */
    public @NonNull List<SessionInfo> getAllSessions() {
        final ApplicationInfo info = mContext.getApplicationInfo();
        if ("com.google.android.googlequicksearchbox".equals(info.packageName)
                && info.versionCode <= 300400070) {
            Log.d(TAG, "Ignoring callback request from old prebuilt");
            return Collections.EMPTY_LIST;
        }

        try {
            return mInstaller.getAllSessions(mUserId);
        } catch (RemoteException e) {
+1 −1
Original line number Diff line number Diff line
@@ -264,7 +264,7 @@ public class PackageParser {
        public final boolean coreApp;
        public final boolean multiArch;

        private PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
        public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
                String[] splitCodePaths) {
            this.packageName = baseApk.packageName;
            this.versionCode = baseApk.versionCode;
+45 −3
Original line number Diff line number Diff line
@@ -321,7 +321,7 @@ public interface IMountService extends IInterface {
             * Mount a secure container with the specified key and owner UID.
             * Returns an int consistent with MountServiceResultCode
             */
            public int mountSecureContainer(String id, String key, int ownerUid)
            public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
                    throws RemoteException {
                Parcel _data = Parcel.obtain();
                Parcel _reply = Parcel.obtain();
@@ -331,6 +331,7 @@ public interface IMountService extends IInterface {
                    _data.writeString(id);
                    _data.writeString(key);
                    _data.writeInt(ownerUid);
                    _data.writeInt(readOnly ? 1 : 0);
                    mRemote.transact(Stub.TRANSACTION_mountSecureContainer, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
@@ -834,6 +835,27 @@ public interface IMountService extends IInterface {
                }
                return _result;
            }

            @Override
            public int resizeSecureContainer(String id, int sizeMb, String key)
                    throws RemoteException {
                Parcel _data = Parcel.obtain();
                Parcel _reply = Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(id);
                    _data.writeInt(sizeMb);
                    _data.writeString(key);
                    mRemote.transact(Stub.TRANSACTION_resizeSecureContainer, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        private static final String DESCRIPTOR = "IMountService";
@@ -918,6 +940,8 @@ public interface IMountService extends IInterface {

        static final int TRANSACTION_getField = IBinder.FIRST_CALL_TRANSACTION + 39;

        static final int TRANSACTION_resizeSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 40;

        /**
         * Cast an IBinder object into an IMountService interface, generating a
         * proxy if needed.
@@ -1082,7 +1106,9 @@ public interface IMountService extends IInterface {
                    key = data.readString();
                    int ownerUid;
                    ownerUid = data.readInt();
                    int resultCode = mountSecureContainer(id, key, ownerUid);
                    boolean readOnly;
                    readOnly = data.readInt() != 0;
                    int resultCode = mountSecureContainer(id, key, ownerUid, readOnly);
                    reply.writeNoException();
                    reply.writeInt(resultCode);
                    return true;
@@ -1308,6 +1334,19 @@ public interface IMountService extends IInterface {
                    reply.writeString(contents);
                    return true;
                }
                case TRANSACTION_resizeSecureContainer: {
                    data.enforceInterface(DESCRIPTOR);
                    String id;
                    id = data.readString();
                    int sizeMb;
                    sizeMb = data.readInt();
                    String key;
                    key = data.readString();
                    int resultCode = resizeSecureContainer(id, sizeMb, key);
                    reply.writeNoException();
                    reply.writeInt(resultCode);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
@@ -1405,7 +1444,8 @@ public interface IMountService extends IInterface {
     * Mount a secure container with the specified key and owner UID. Returns an
     * int consistent with MountServiceResultCode
     */
    public int mountSecureContainer(String id, String key, int ownerUid) throws RemoteException;
    public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
            throws RemoteException;

    /**
     * Mount external storage at given mount point. Returns an int consistent
@@ -1571,4 +1611,6 @@ public interface IMountService extends IInterface {
     * @return contents of field
     */
    public String getField(String field) throws RemoteException;

    public int resizeSecureContainer(String id, int sizeMb, String key) throws RemoteException;
}
Loading