diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java index b3f46c22ba0a001669befe3e87fdfe1b5f599585..231f2c8660e9bd5f90bdc78aa2c62ec429f8bf81 100644 --- a/telephony/java/android/telephony/MbmsDownloadManager.java +++ b/telephony/java/android/telephony/MbmsDownloadManager.java @@ -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); diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java index 345729d82deaa8344cba1f8253c8150f32df4ddb..01e0bbdfc0a0ac78532720b4bdc7a521f01e2d3b 100644 --- a/telephony/java/android/telephony/mbms/DownloadRequest.java +++ b/telephony/java/android/telephony/mbms/DownloadRequest.java @@ -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); } } diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java index 5bc53a82a3bb963749d18219da4d45ecf53e9943..361716546fb9560f2fa26e3f0af91e70504d8d18 100644 --- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java +++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java @@ -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 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 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 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; } diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java index b28950ec758fadb94248e13f9f1f47594a29bf65..1e03fb9584b4a9e76cac92bb79b42066818eb9a2 100644 --- a/telephony/java/android/telephony/mbms/MbmsUtils.java +++ b/telephony/java/android/telephony/mbms/MbmsUtils.java @@ -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); } }