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

Commit fa42eea9 authored by Hall Liu's avatar Hall Liu Committed by Gerrit Code Review
Browse files

Merge "Update DownloadRequest API"

parents 21187ba5 3bedd88c
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -93,19 +93,21 @@ public class MbmsDownloadManager {
    /**
     * Integer extra indicating the result code of the download. One of
     * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}.
     * TODO: Not systemapi.
     */
    public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT";

    /**
     * Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result
     * is for. Must not be null.
     * TODO: future systemapi (here and and all extras) except the two for the app intent
     * TODO: Not systemapi.
     */
    public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";

    /**
     * Extra containing the {@link DownloadRequest} for which the download result or file
     * descriptor request is for. Must not be null.
     * TODO: future systemapi (here and and all extras) except the three for the app intent
     */
    public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";

@@ -180,6 +182,7 @@ public class MbmsDownloadManager {
     * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}.
     * Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to
     * {@link #RESULT_SUCCESSFUL}.
     * TODO: Not systemapi.
     */
    public static final String EXTRA_COMPLETED_FILE_URI =
            "android.telephony.mbms.extra.COMPLETED_FILE_URI";
@@ -554,7 +557,7 @@ public class MbmsDownloadManager {

    private File getDownloadRequestTokenPath(DownloadRequest request) {
        File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
                request.getFileServiceInfo());
                request.getFileServiceId());
        String downloadTokenFileName = request.getHash()
                + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
        return new File(tempFileLocation, downloadTokenFileName);
+103 −31
Original line number Diff line number Diff line
@@ -21,7 +21,14 @@ import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Base64;

import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@@ -35,24 +42,52 @@ import java.util.Objects;
public class DownloadRequest implements Parcelable {
    // Version code used to keep token calculation consistent.
    private static final int CURRENT_VERSION = 1;
    private static final String LOG_TAG = "MbmsDownloadRequest";

    /**
     * Maximum permissible length for the app's download-completion intent, when serialized via
     * {@link Intent#toUri(int)}.
     */
    public static final int MAX_APP_INTENT_SIZE = 50000;

    /**
     * Maximum permissible length for the app's destination path, when serialized via
     * {@link Uri#toString()}.
     */
    public static final int MAX_DESTINATION_URI_SIZE = 50000;

    /** @hide */
    private static class OpaqueDataContainer implements Serializable {
        private final String destinationUri;
        private final String appIntent;
        private final int version;

        public OpaqueDataContainer(String destinationUri, String appIntent, int version) {
            this.destinationUri = destinationUri;
            this.appIntent = appIntent;
            this.version = version;
        }
    }

    public static class Builder {
        private int id;
        private FileServiceInfo serviceInfo;
        private String fileServiceId;
        private Uri source;
        private Uri dest;
        private int subscriptionId;
        private String appIntent;
        private int version = CURRENT_VERSION;

        public Builder setId(int id) {
            this.id = id;
        public Builder setServiceInfo(FileServiceInfo serviceInfo) {
            fileServiceId = serviceInfo.getServiceId();
            return this;
        }

        public Builder setServiceInfo(FileServiceInfo serviceInfo) {
            this.serviceInfo = serviceInfo;
        /**
         * @hide
         * TODO: systemapi
         */
        public Builder setServiceId(String serviceId) {
            fileServiceId = serviceId;
            return this;
        }

@@ -62,6 +97,10 @@ public class DownloadRequest implements Parcelable {
        }

        public Builder setDest(Uri dest) {
            if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
                throw new IllegalArgumentException("Destination uri must not exceed length " +
                        MAX_DESTINATION_URI_SIZE);
            }
            this.dest = dest;
            return this;
        }
@@ -73,33 +112,53 @@ public class DownloadRequest implements Parcelable {

        public Builder setAppIntent(Intent intent) {
            this.appIntent = intent.toUri(0);
            if (this.appIntent.length() > MAX_APP_INTENT_SIZE) {
                throw new IllegalArgumentException("App intent must not exceed length " +
                        MAX_APP_INTENT_SIZE);
            }
            return this;
        }

        public Builder setVersion(int version) {
            this.version = version;
        /**
         * For use by middleware only
         * TODO: systemapi
         * @hide
         */
        public Builder setOpaqueData(byte[] data) {
            try {
                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
                OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
                version = dataContainer.version;
                appIntent = dataContainer.appIntent;
                dest = Uri.parse(dataContainer.destinationUri);
            } catch (IOException e) {
                // Really should never happen
                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
                throw new IllegalArgumentException(e);
            } catch (ClassNotFoundException e) {
                Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
                throw new IllegalArgumentException(e);
            }
            return this;
        }

        public DownloadRequest build() {
            return new DownloadRequest(id, serviceInfo, source, dest,
            return new DownloadRequest(fileServiceId, source, dest,
                    subscriptionId, appIntent, version);
        }
    }

    private final int downloadId;
    private final FileServiceInfo fileServiceInfo;
    private final String fileServiceId;
    private final Uri sourceUri;
    private final Uri destinationUri;
    private final int subscriptionId;
    private final String serializedResultIntentForApp;
    private final int version;

    private DownloadRequest(int id, FileServiceInfo serviceInfo,
    private DownloadRequest(String fileServiceId,
            Uri source, Uri dest,
            int sub, String appIntent, int version) {
        downloadId = id;
        fileServiceInfo = serviceInfo;
        this.fileServiceId = fileServiceId;
        sourceUri = source;
        destinationUri = dest;
        subscriptionId = sub;
@@ -112,8 +171,7 @@ public class DownloadRequest implements Parcelable {
    }

    private DownloadRequest(DownloadRequest dr) {
        downloadId = dr.downloadId;
        fileServiceInfo = dr.fileServiceInfo;
        fileServiceId = dr.fileServiceId;
        sourceUri = dr.sourceUri;
        destinationUri = dr.destinationUri;
        subscriptionId = dr.subscriptionId;
@@ -122,8 +180,7 @@ public class DownloadRequest implements Parcelable {
    }

    private DownloadRequest(Parcel in) {
        downloadId = in.readInt();
        fileServiceInfo = in.readParcelable(getClass().getClassLoader());
        fileServiceId = in.readString();
        sourceUri = in.readParcelable(getClass().getClassLoader());
        destinationUri = in.readParcelable(getClass().getClassLoader());
        subscriptionId = in.readInt();
@@ -136,8 +193,7 @@ public class DownloadRequest implements Parcelable {
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(downloadId);
        out.writeParcelable(fileServiceInfo, flags);
        out.writeString(fileServiceId);
        out.writeParcelable(sourceUri, flags);
        out.writeParcelable(destinationUri, flags);
        out.writeInt(subscriptionId);
@@ -145,12 +201,8 @@ public class DownloadRequest implements Parcelable {
        out.writeInt(version);
    }

    public int getDownloadId() {
        return downloadId;
    }

    public FileServiceInfo getFileServiceInfo() {
        return fileServiceInfo;
    public String getFileServiceId() {
        return fileServiceId;
    }

    public Uri getSourceUri() {
@@ -173,6 +225,27 @@ public class DownloadRequest implements Parcelable {
        }
    }

    /**
     * @hide
     * TODO: systemapi
     */
    public byte[] getOpaqueData() {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
            OpaqueDataContainer container = new OpaqueDataContainer(
                    destinationUri.toString(), serializedResultIntentForApp, version);
            stream.writeObject(container);
            stream.flush();
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            // Really should never happen
            Log.e(LOG_TAG, "Got IOException trying to serialize opaque data");
            return null;
        }
    }

    /** @hide */
    public int getVersion() {
        return version;
    }
@@ -228,10 +301,9 @@ public class DownloadRequest implements Parcelable {
            return false;
        }
        DownloadRequest request = (DownloadRequest) o;
        return downloadId == request.downloadId &&
                subscriptionId == request.subscriptionId &&
        return subscriptionId == request.subscriptionId &&
                version == request.version &&
                Objects.equals(fileServiceInfo, request.fileServiceInfo) &&
                Objects.equals(fileServiceId, request.fileServiceId) &&
                Objects.equals(sourceUri, request.sourceUri) &&
                Objects.equals(destinationUri, request.destinationUri) &&
                Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
@@ -239,7 +311,7 @@ public class DownloadRequest implements Parcelable {

    @Override
    public int hashCode() {
        return Objects.hash(downloadId, fileServiceInfo, sourceUri, destinationUri,
        return Objects.hash(fileServiceId, sourceUri, destinationUri,
                subscriptionId, serializedResultIntentForApp, version);
    }
}
+16 −12
Original line number Diff line number Diff line
@@ -83,7 +83,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
    public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4;

    /**
     * Indicates that the manager weas unable to generate one or more of the requested file
     * Indicates that the manager was unable to generate one or more of the requested file
     * descriptors.
     * This is a non-fatal result code -- some file descriptors may still be generated, but there
     * is no guarantee that they will be the same number as requested.
@@ -149,7 +149,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
            DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST);
            String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
            File expectedTokenFile = new File(
                    MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceInfo()),
                    MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
                    expectedTokenFileName);
            if (!expectedTokenFile.exists()) {
                Log.w(LOG_TAG, "Supplied download request does not match a token that we have. " +
@@ -201,22 +201,24 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {

        Uri destinationUri = request.getDestinationUri();
        Uri finalTempFile = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FINAL_URI);
        if (!verifyTempFilePath(context, request.getFileServiceInfo(), finalTempFile)) {
        if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
            Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
            setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
            return;
        }

        String relativePath = calculateDestinationFileRelativePath(request,
                (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO));
        FileInfo completedFileInfo =
                (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO);
        String relativePath = calculateDestinationFileRelativePath(request, completedFileInfo);

        Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
        if (finalFileLocation == null) {
            Log.w(LOG_TAG, "Failed to move temp file to final destination");
            // TODO: how do we notify the app of this?
            setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
            return;
        }
        intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation);
        intentForApp.putExtra(MbmsDownloadManager.EXTRA_FILE_INFO, completedFileInfo);

        context.sendBroadcast(intentForApp);
        setResultCode(RESULT_OK);
@@ -235,7 +237,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
        }

        for (Uri tempFileUri : tempFiles) {
            if (verifyTempFilePath(context, request.getFileServiceInfo(), tempFileUri)) {
            if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
                File tempFile = new File(tempFileUri.getSchemeSpecificPart());
                tempFile.delete();
            }
@@ -276,7 +278,8 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
    private ArrayList<UriPathPair> generateFreshTempFiles(Context context,
            FileServiceInfo serviceInfo,
            int freshFdCount) {
        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceInfo);
        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
                serviceInfo.getServiceId());
        if (!tempFileDir.exists()) {
            tempFileDir.mkdirs();
        }
@@ -327,7 +330,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
        ArrayList<UriPathPair> result = new ArrayList<>(pausedFiles.size());

        for (Uri fileUri : pausedFiles) {
            if (!verifyTempFilePath(context, serviceInfo, fileUri)) {
            if (!verifyTempFilePath(context, serviceInfo.getServiceId(), fileUri)) {
                Log.w(LOG_TAG, "Supplied file " + fileUri + " is not a valid temp file to resume");
                setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
                continue;
@@ -351,7 +354,8 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
    private void cleanupTempFiles(Context context, Intent intent) {
        FileServiceInfo serviceInfo =
                intent.getParcelableExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO);
        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceInfo);
        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
                serviceInfo.getServiceId());
        List<Uri> filesInUse =
                intent.getParcelableArrayListExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE);
        File[] filesToDelete = tempFileDir.listFiles(new FileFilter() {
@@ -439,7 +443,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
        return null;
    }

    private static boolean verifyTempFilePath(Context context, FileServiceInfo serviceInfo,
    private static boolean verifyTempFilePath(Context context, String serviceId,
            Uri filePath) {
        if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {
            Log.w(LOG_TAG, "Uri " + filePath + " does not have a file scheme");
@@ -454,7 +458,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
        }

        if (!MbmsUtils.isContainedIn(
                MbmsUtils.getEmbmsTempFileDirForService(context, serviceInfo), tempFile)) {
                MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
            return false;
        }

+2 −3
Original line number Diff line number Diff line
@@ -89,10 +89,9 @@ public class MbmsUtils {
    /**
     * Returns a File linked to the directory used to store temp files for this file service
     */
    public static File getEmbmsTempFileDirForService(Context context, FileServiceInfo serviceInfo) {
    public static File getEmbmsTempFileDirForService(Context context, String serviceId) {
        File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);

        String tempFileDirName = String.valueOf(serviceInfo.getServiceId());
        return new File(embmsTempFileDir, tempFileDirName);
        return new File(embmsTempFileDir, serviceId);
    }
}