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

Commit 28345b2c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Incremental install via adb."

parents 890e80db cd4d3875
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -2007,18 +2007,15 @@ package android.content.pm {
  }
  public final class InstallationFile implements android.os.Parcelable {
    ctor public InstallationFile(@NonNull String, long, @Nullable byte[]);
    ctor public InstallationFile(int, @NonNull String, long, @Nullable byte[], @Nullable byte[]);
    method public int describeContents();
    method public int getFileType();
    method public long getLengthBytes();
    method public int getLocation();
    method @Nullable public byte[] getMetadata();
    method @NonNull public String getName();
    method public long getSize();
    method @Nullable public byte[] getSignature();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallationFile> CREATOR;
    field public static final int FILE_TYPE_APK = 0; // 0x0
    field public static final int FILE_TYPE_LIB = 1; // 0x1
    field public static final int FILE_TYPE_OBB = 2; // 0x2
    field public static final int FILE_TYPE_UNKNOWN = -1; // 0xffffffff
  }
  public final class InstantAppInfo implements android.os.Parcelable {
@@ -10317,7 +10314,7 @@ package android.service.dataloader {
  public abstract class DataLoaderService extends android.app.Service {
    ctor public DataLoaderService();
    method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader();
    method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
  }
  public static interface DataLoaderService.DataLoader {
+29 −51
Original line number Diff line number Diff line
@@ -16,82 +16,59 @@

package android.content.pm;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Defines the properties of a file in an installation session.
 * TODO(b/136132412): update with new APIs.
 *
 * @hide
 */
@SystemApi
public final class InstallationFile implements Parcelable {
    public static final int FILE_TYPE_UNKNOWN = -1;
    public static final int FILE_TYPE_APK = 0;
    public static final int FILE_TYPE_LIB = 1;
    public static final int FILE_TYPE_OBB = 2;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"FILE_TYPE_"}, value = {
            FILE_TYPE_APK,
            FILE_TYPE_LIB,
            FILE_TYPE_OBB,
    })
    public @interface FileType {
    }

    private String mFileName;
    private @FileType int mFileType;
    private long mFileSize;
    private byte[] mMetadata;

    public InstallationFile(@NonNull String fileName, long fileSize,
            @Nullable byte[] metadata) {
        mFileName = fileName;
        mFileSize = fileSize;
    private final @PackageInstaller.FileLocation int mLocation;
    private final @NonNull String mName;
    private final long mLengthBytes;
    private final @Nullable byte[] mMetadata;
    private final @Nullable byte[] mSignature;

    public InstallationFile(@PackageInstaller.FileLocation int location, @NonNull String name,
            long lengthBytes, @Nullable byte[] metadata, @Nullable byte[] signature) {
        mLocation = location;
        mName = name;
        mLengthBytes = lengthBytes;
        mMetadata = metadata;
        if (fileName.toLowerCase().endsWith(".apk")) {
            mFileType = FILE_TYPE_APK;
        } else if (fileName.toLowerCase().endsWith(".obb")) {
            mFileType = FILE_TYPE_OBB;
        } else if (fileName.toLowerCase().endsWith(".so") && fileName.toLowerCase().startsWith(
                "lib/")) {
            mFileType = FILE_TYPE_LIB;
        } else {
            mFileType = FILE_TYPE_UNKNOWN;
        }
        mSignature = signature;
    }

    public @FileType int getFileType() {
        return mFileType;
    public @PackageInstaller.FileLocation int getLocation() {
        return mLocation;
    }

    public @NonNull String getName() {
        return mFileName;
        return mName;
    }

    public long getSize() {
        return mFileSize;
    public long getLengthBytes() {
        return mLengthBytes;
    }

    public @Nullable byte[] getMetadata() {
        return mMetadata;
    }

    public @Nullable byte[] getSignature() {
        return mSignature;
    }

    private InstallationFile(Parcel source) {
        mFileName = source.readString();
        mFileType = source.readInt();
        mFileSize = source.readLong();
        mLocation = source.readInt();
        mName = source.readString();
        mLengthBytes = source.readLong();
        mMetadata = source.createByteArray();
        mSignature = source.createByteArray();
    }

    @Override
@@ -101,10 +78,11 @@ public final class InstallationFile implements Parcelable {

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString(mFileName);
        dest.writeInt(mFileType);
        dest.writeLong(mFileSize);
        dest.writeInt(mLocation);
        dest.writeString(mName);
        dest.writeLong(mLengthBytes);
        dest.writeByteArray(mMetadata);
        dest.writeByteArray(mSignature);
    }

    public static final @NonNull Creator<InstallationFile> CREATOR =
+6 −4
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ package android.os.incremental;
 * @throws IllegalStateException the session is not an Incremental installation session.
 */

import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -85,7 +87,7 @@ public final class IncrementalFileStorages {
        try {
            result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams);
            for (InstallationFile file : addedFiles) {
                if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
                if (file.getLocation() == LOCATION_DATA_APP) {
                    try {
                        result.addApkFile(file);
                    } catch (IOException e) {
@@ -95,7 +97,7 @@ public final class IncrementalFileStorages {
                                e);
                    }
                } else {
                    throw new IOException("Unknown file type: " + file.getFileType());
                    throw new IOException("Unknown file location: " + file.getLocation());
                }
            }

@@ -147,8 +149,8 @@ public final class IncrementalFileStorages {
        String apkName = apk.getName();
        File targetFile = Paths.get(stageDirPath, apkName).toFile();
        if (!targetFile.exists()) {
            mDefaultStorage.makeFile(apkName, apk.getSize(), null,
                    apk.getMetadata(), 0, null, null, null);
            mDefaultStorage.makeFile(apkName, apk.getLengthBytes(), null, apk.getMetadata(),
                    apk.getSignature());
        }
    }

+9 −0
Original line number Diff line number Diff line
@@ -299,7 +299,16 @@ public final class IncrementalManager {
        return nativeIsIncrementalPath(path);
    }

    /**
     * Returns raw signature for file if it's on Incremental File System.
     * Unsafe, use only if you are sure what you are doing.
     */
    public static @Nullable byte[] unsafeGetFileSignature(@NonNull String path) {
        return nativeUnsafeGetFileSignature(path);
    }

    /* Native methods */
    private static native boolean nativeIsEnabled();
    private static native boolean nativeIsIncrementalPath(@NonNull String path);
    private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
}
+70 −32
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -169,10 +171,11 @@ public final class IncrementalStorage {
     * @param path             Relative path of the new file.
     * @param size             Size of the new file in bytes.
     * @param metadata         Metadata bytes.
     * @param v4signatureBytes Serialized V4SignatureProto.
     */
    public void makeFile(@NonNull String path, long size, @Nullable UUID id,
            @Nullable byte[] metadata, int hashAlgorithm, @Nullable byte[] rootHash,
            @Nullable byte[] additionalData, @Nullable byte[] signature) throws IOException {
            @Nullable byte[] metadata, @Nullable byte[] v4signatureBytes)
            throws IOException {
        try {
            if (id == null && metadata == null) {
                throw new IOException("File ID and metadata cannot both be null");
@@ -181,13 +184,7 @@ public final class IncrementalStorage {
            params.size = size;
            params.metadata = (metadata == null ? new byte[0] : metadata);
            params.fileId = idToBytes(id);
            if (hashAlgorithm != 0 || signature != null) {
                params.signature = new IncrementalSignature();
                params.signature.hashAlgorithm = hashAlgorithm;
                params.signature.rootHash = rootHash;
                params.signature.additionalData = additionalData;
                params.signature.signature = signature;
            }
            params.signature = parseV4Signature(v4signatureBytes);
            int res = mService.makeFile(mId, path, params);
            if (res != 0) {
                throw new IOException("makeFile() failed with errno " + -res);
@@ -197,6 +194,7 @@ public final class IncrementalStorage {
        }
    }


    /**
     * Creates a file in Incremental storage. The content of the file is mapped from a range inside
     * a source file in the same storage.
@@ -349,6 +347,37 @@ public final class IncrementalStorage {
        }
    }

    /**
     * Returns the metadata object of an IncFs File.
     *
     * @param id The file id.
     * @return Byte array that contains metadata bytes.
     */
    @Nullable
    public byte[] getFileMetadata(@NonNull UUID id) {
        try {
            final byte[] rawId = idToBytes(id);
            return mService.getMetadataById(mId, rawId);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
            return null;
        }
    }

    /**
     * Informs the data loader service associated with the current storage to start data loader
     *
     * @return True if data loader is successfully started.
     */
    public boolean startLoading() {
        try {
            return mService.startLoading(mId);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
            return false;
        }
    }

    private static final int UUID_BYTE_SIZE = 16;

    /**
@@ -386,35 +415,44 @@ public final class IncrementalStorage {
        return new UUID(msb, lsb);
    }

    private static final int INCFS_HASH_SHA256 = 1;
    private static final int INCFS_MAX_HASH_SIZE = 32; // SHA256
    private static final int INCFS_MAX_ADD_DATA_SIZE = 128;

    /**
     * Returns the metadata object of an IncFs File.
     *
     * @param id The file id.
     * @return Byte array that contains metadata bytes.
     * Deserialize and validate v4 signature bytes.
     */
    @Nullable
    public byte[] getFileMetadata(@NonNull UUID id) {
        try {
            final byte[] rawId = idToBytes(id);
            return mService.getMetadataById(mId, rawId);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
    private static IncrementalSignature parseV4Signature(@Nullable byte[] v4signatureBytes)
            throws IOException {
        if (v4signatureBytes == null) {
            return null;
        }

        final V4Signature signature;
        try (DataInputStream input = new DataInputStream(
                new ByteArrayInputStream(v4signatureBytes))) {
            signature = V4Signature.readFrom(input);
        }

    /**
     * Informs the data loader service associated with the current storage to start data loader
     *
     * @return True if data loader is successfully started.
     */
    public boolean startLoading() {
        try {
            return mService.startLoading(mId);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
            return false;
        final byte[] rootHash = signature.verityRootHash;
        final byte[] additionalData = signature.v3Digest;
        final byte[] pkcs7Signature = signature.pkcs7SignatureBlock;

        if (rootHash.length != INCFS_MAX_HASH_SIZE) {
            throw new IOException("rootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
        }
        if (additionalData.length > INCFS_MAX_ADD_DATA_SIZE) {
            throw new IOException(
                    "additionalData has to be at most " + INCFS_MAX_ADD_DATA_SIZE + " bytes");
        }

        IncrementalSignature result = new IncrementalSignature();
        result.hashAlgorithm = INCFS_HASH_SHA256;
        result.rootHash = rootHash;
        result.additionalData = additionalData;
        result.signature = pkcs7Signature;

        return result;
    }

    /**
Loading