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

Commit 812d0866 authored by Hall Liu's avatar Hall Liu Committed by android-build-merger
Browse files

Merge "Update DownloadRequest API" am: fa42eea9

am: 5e7c98bb

Change-Id: Ie5a323b8a0c96f7a7f63eea1b0163bb7e5bf4ea7
parents bdbe454b 5e7c98bb
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);
    }
}