Merge "Update DownloadRequest API" am: fa42eea98f
am: 5e7c98bb6a
Change-Id: Ie5a323b8a0c96f7a7f63eea1b0163bb7e5bf4ea7
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user