Merge "Make changes to MBMS API following recs"

This commit is contained in:
Hall Liu
2018-03-31 00:28:06 +00:00
committed by Gerrit Code Review
12 changed files with 275 additions and 73 deletions

View File

@@ -40388,17 +40388,17 @@ package android.telephony {
} }
public class MbmsDownloadSession implements java.lang.AutoCloseable { public class MbmsDownloadSession implements java.lang.AutoCloseable {
method public int addProgressListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadProgressListener); method public void addProgressListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadProgressListener);
method public int addStatusListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStatusListener); method public void addStatusListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStatusListener);
method public int cancelDownload(android.telephony.mbms.DownloadRequest); method public void cancelDownload(android.telephony.mbms.DownloadRequest);
method public void close(); method public void close();
method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback); method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback);
method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback); method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback);
method public int download(android.telephony.mbms.DownloadRequest); method public void download(android.telephony.mbms.DownloadRequest);
method public java.io.File getTempFileRootDirectory(); method public java.io.File getTempFileRootDirectory();
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(); method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener); method public void removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener);
method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener); method public void removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener);
method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo); method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
method public void requestUpdateFileServices(java.util.List<java.lang.String>); method public void requestUpdateFileServices(java.util.List<java.lang.String>);
method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest); method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
@@ -41320,6 +41320,7 @@ package android.telephony.mbms {
field public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; // 0x2 field public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; // 0x2
field public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1; // 0x1 field public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1; // 0x1
field public static final int SUCCESS = 0; // 0x0 field public static final int SUCCESS = 0; // 0x0
field public static final int UNKNOWN = -1; // 0xffffffff
} }
public static class MbmsErrors.DownloadErrors { public static class MbmsErrors.DownloadErrors {

View File

@@ -16,6 +16,8 @@
package android.telephony; package android.telephony;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
@@ -32,14 +34,14 @@ import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.telephony.mbms.DownloadProgressListener; import android.telephony.mbms.DownloadProgressListener;
import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.DownloadStatusListener; import android.telephony.mbms.DownloadStatusListener;
import android.telephony.mbms.FileInfo; import android.telephony.mbms.FileInfo;
import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.InternalDownloadProgressListener; import android.telephony.mbms.InternalDownloadProgressListener;
import android.telephony.mbms.InternalDownloadSessionCallback; import android.telephony.mbms.InternalDownloadSessionCallback;
import android.telephony.mbms.InternalDownloadStatusListener; import android.telephony.mbms.InternalDownloadStatusListener;
import android.telephony.mbms.MbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsDownloadReceiver; import android.telephony.mbms.MbmsDownloadReceiver;
import android.telephony.mbms.MbmsDownloadSessionCallback;
import android.telephony.mbms.MbmsErrors; import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsTempFileProvider; import android.telephony.mbms.MbmsTempFileProvider;
import android.telephony.mbms.MbmsUtils; import android.telephony.mbms.MbmsUtils;
@@ -58,8 +60,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
/** /**
* This class provides functionality for file download over MBMS. * This class provides functionality for file download over MBMS.
*/ */
@@ -337,6 +337,12 @@ public class MbmsDownloadSession implements AutoCloseable {
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
} }
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an"
+ " unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, "Error returned during initialization"); sendErrorToApp(result, "Error returned during initialization");
sIsInitialized.set(false); sIsInitialized.set(false);
@@ -388,6 +394,11 @@ public class MbmsDownloadSession implements AutoCloseable {
} }
try { try {
int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList); int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
if (returnCode == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (returnCode != MbmsErrors.SUCCESS) { if (returnCode != MbmsErrors.SUCCESS) {
sendErrorToApp(returnCode, null); sendErrorToApp(returnCode, null);
} }
@@ -443,6 +454,11 @@ public class MbmsDownloadSession implements AutoCloseable {
try { try {
int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath); int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, null); sendErrorToApp(result, null);
return; return;
@@ -514,11 +530,13 @@ public class MbmsDownloadSession implements AutoCloseable {
* *
* Asynchronous errors through the callback may include any error not specific to the * Asynchronous errors through the callback may include any error not specific to the
* streaming use-case. * streaming use-case.
*
* If no error is delivered via the callback after calling this method, that means that the
* middleware has successfully started the download or scheduled the download, if the download
* is at a future time.
* @param request The request that specifies what should be downloaded. * @param request The request that specifies what should be downloaded.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/ */
public int download(@NonNull DownloadRequest request) { public void download(@NonNull DownloadRequest request) {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound"); throw new IllegalStateException("Middleware not yet bound");
@@ -540,12 +558,19 @@ public class MbmsDownloadSession implements AutoCloseable {
int result = downloadService.download(request); int result = downloadService.download(request);
if (result == MbmsErrors.SUCCESS) { if (result == MbmsErrors.SUCCESS) {
writeDownloadRequestToken(request); writeDownloadRequestToken(request);
} else {
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown"
+ " error code");
}
sendErrorToApp(result, null);
} }
return result;
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
return MbmsErrors.ERROR_MIDDLEWARE_LOST; sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
} }
} }
@@ -581,14 +606,15 @@ public class MbmsDownloadSession implements AutoCloseable {
* If the middleware is not aware of the specified download request, * If the middleware is not aware of the specified download request,
* this method will throw an {@link IllegalArgumentException}. * this method will throw an {@link IllegalArgumentException}.
* *
* If the operation encountered an error, the error code will be delivered via
* {@link MbmsDownloadSessionCallback#onError}.
*
* @param request The {@link DownloadRequest} that you want updates on. * @param request The {@link DownloadRequest} that you want updates on.
* @param executor The {@link Executor} on which calls to {@code listener } should be executed. * @param executor The {@link Executor} on which calls to {@code listener } should be executed.
* @param listener The listener that should be called when the middleware has information to * @param listener The listener that should be called when the middleware has information to
* share on the status download. * share on the status download.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/ */
public int addStatusListener(@NonNull DownloadRequest request, public void addStatusListener(@NonNull DownloadRequest request,
@NonNull Executor executor, @NonNull DownloadStatusListener listener) { @NonNull Executor executor, @NonNull DownloadStatusListener listener) {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
@@ -600,20 +626,25 @@ public class MbmsDownloadSession implements AutoCloseable {
try { try {
int result = downloadService.addStatusListener(request, internalListener); int result = downloadService.addStatusListener(request, internalListener);
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) { if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request."); throw new IllegalArgumentException("Unknown download request.");
} }
return result; sendErrorToApp(result, null);
return;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
return MbmsErrors.ERROR_MIDDLEWARE_LOST; sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return;
} }
mInternalDownloadStatusListeners.put(listener, internalListener); mInternalDownloadStatusListeners.put(listener, internalListener);
return MbmsErrors.SUCCESS;
} }
/** /**
@@ -625,12 +656,13 @@ public class MbmsDownloadSession implements AutoCloseable {
* If the middleware is not aware of the specified download request, * If the middleware is not aware of the specified download request,
* this method will throw an {@link IllegalArgumentException}. * this method will throw an {@link IllegalArgumentException}.
* *
* If the operation encountered an error, the error code will be delivered via
* {@link MbmsDownloadSessionCallback#onError}.
*
* @param request The {@link DownloadRequest} provided during registration * @param request The {@link DownloadRequest} provided during registration
* @param listener The listener provided during registration. * @param listener The listener provided during registration.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/ */
public int removeStatusListener(@NonNull DownloadRequest request, public void removeStatusListener(@NonNull DownloadRequest request,
@NonNull DownloadStatusListener listener) { @NonNull DownloadStatusListener listener) {
try { try {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
@@ -646,16 +678,24 @@ public class MbmsDownloadSession implements AutoCloseable {
try { try {
int result = downloadService.removeStatusListener(request, internalListener); int result = downloadService.removeStatusListener(request, internalListener);
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an"
+ " unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) { if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request."); throw new IllegalArgumentException("Unknown download request.");
} }
return result; sendErrorToApp(result, null);
return;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
return MbmsErrors.ERROR_MIDDLEWARE_LOST; sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return;
} }
} finally { } finally {
InternalDownloadStatusListener internalCallback = InternalDownloadStatusListener internalCallback =
@@ -664,7 +704,6 @@ public class MbmsDownloadSession implements AutoCloseable {
internalCallback.stop(); internalCallback.stop();
} }
} }
return MbmsErrors.SUCCESS;
} }
/** /**
@@ -676,14 +715,15 @@ public class MbmsDownloadSession implements AutoCloseable {
* If the middleware is not aware of the specified download request, * If the middleware is not aware of the specified download request,
* this method will throw an {@link IllegalArgumentException}. * this method will throw an {@link IllegalArgumentException}.
* *
* If the operation encountered an error, the error code will be delivered via
* {@link MbmsDownloadSessionCallback#onError}.
*
* @param request The {@link DownloadRequest} that you want updates on. * @param request The {@link DownloadRequest} that you want updates on.
* @param executor The {@link Executor} on which calls to {@code listener} should be executed. * @param executor The {@link Executor} on which calls to {@code listener} should be executed.
* @param listener The listener that should be called when the middleware has information to * @param listener The listener that should be called when the middleware has information to
* share on the progress of the download. * share on the progress of the download.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/ */
public int addProgressListener(@NonNull DownloadRequest request, public void addProgressListener(@NonNull DownloadRequest request,
@NonNull Executor executor, @NonNull DownloadProgressListener listener) { @NonNull Executor executor, @NonNull DownloadProgressListener listener) {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
@@ -695,19 +735,25 @@ public class MbmsDownloadSession implements AutoCloseable {
try { try {
int result = downloadService.addProgressListener(request, internalListener); int result = downloadService.addProgressListener(request, internalListener);
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) { if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request."); throw new IllegalArgumentException("Unknown download request.");
} }
return result; sendErrorToApp(result, null);
return;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
return MbmsErrors.ERROR_MIDDLEWARE_LOST; sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return;
} }
mInternalDownloadProgressListeners.put(listener, internalListener); mInternalDownloadProgressListeners.put(listener, internalListener);
return MbmsErrors.SUCCESS;
} }
/** /**
@@ -719,12 +765,13 @@ public class MbmsDownloadSession implements AutoCloseable {
* If the middleware is not aware of the specified download request, * If the middleware is not aware of the specified download request,
* this method will throw an {@link IllegalArgumentException}. * this method will throw an {@link IllegalArgumentException}.
* *
* If the operation encountered an error, the error code will be delivered via
* {@link MbmsDownloadSessionCallback#onError}.
*
* @param request The {@link DownloadRequest} provided during registration * @param request The {@link DownloadRequest} provided during registration
* @param listener The listener provided during registration. * @param listener The listener provided during registration.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/ */
public int removeProgressListener(@NonNull DownloadRequest request, public void removeProgressListener(@NonNull DownloadRequest request,
@NonNull DownloadProgressListener listener) { @NonNull DownloadProgressListener listener) {
try { try {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
@@ -740,16 +787,24 @@ public class MbmsDownloadSession implements AutoCloseable {
try { try {
int result = downloadService.removeProgressListener(request, internalListener); int result = downloadService.removeProgressListener(request, internalListener);
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not"
+ " return an unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) { if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request."); throw new IllegalArgumentException("Unknown download request.");
} }
return result; sendErrorToApp(result, null);
return;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
return MbmsErrors.ERROR_MIDDLEWARE_LOST; sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
return;
} }
} finally { } finally {
InternalDownloadProgressListener internalCallback = InternalDownloadProgressListener internalCallback =
@@ -758,20 +813,17 @@ public class MbmsDownloadSession implements AutoCloseable {
internalCallback.stop(); internalCallback.stop();
} }
} }
return MbmsErrors.SUCCESS;
} }
/** /**
* Attempts to cancel the specified {@link DownloadRequest}. * Attempts to cancel the specified {@link DownloadRequest}.
* *
* If the middleware is not aware of the specified download request, * If the operation encountered an error, the error code will be delivered via
* this method will throw an {@link IllegalArgumentException}. * {@link MbmsDownloadSessionCallback#onError}.
* *
* @param downloadRequest The download request that you wish to cancel. * @param downloadRequest The download request that you wish to cancel.
* @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
* and some other error code otherwise.
*/ */
public int cancelDownload(@NonNull DownloadRequest downloadRequest) { public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
IMbmsDownloadService downloadService = mService.get(); IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) { if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound"); throw new IllegalStateException("Middleware not yet bound");
@@ -779,18 +831,20 @@ public class MbmsDownloadSession implements AutoCloseable {
try { try {
int result = downloadService.cancelDownload(downloadRequest); int result = downloadService.cancelDownload(downloadRequest);
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) { sendErrorToApp(result, null);
throw new IllegalArgumentException("Unknown download request.");
}
} else { } else {
deleteDownloadRequestToken(downloadRequest); deleteDownloadRequestToken(downloadRequest);
} }
return result;
} catch (RemoteException e) { } catch (RemoteException e) {
mService.set(null); mService.set(null);
sIsInitialized.set(false); sIsInitialized.set(false);
return MbmsErrors.ERROR_MIDDLEWARE_LOST; sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
} }
} }
@@ -818,6 +872,11 @@ public class MbmsDownloadSession implements AutoCloseable {
try { try {
int result = downloadService.requestDownloadState(downloadRequest, fileInfo); int result = downloadService.requestDownloadState(downloadRequest, fileInfo);
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) { if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request."); throw new IllegalArgumentException("Unknown download request.");
@@ -862,6 +921,11 @@ public class MbmsDownloadSession implements AutoCloseable {
try { try {
int result = downloadService.resetDownloadKnowledge(downloadRequest); int result = downloadService.resetDownloadKnowledge(downloadRequest);
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) { if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request."); throw new IllegalArgumentException("Unknown download request.");
@@ -978,10 +1042,6 @@ public class MbmsDownloadSession implements AutoCloseable {
} }
private void sendErrorToApp(int errorCode, String message) { private void sendErrorToApp(int errorCode, String message) {
try { mInternalCallback.onError(errorCode, message);
mInternalCallback.onError(errorCode, message);
} catch (RemoteException e) {
// Ignore, should not happen locally.
}
} }
} }

View File

@@ -16,6 +16,8 @@
package android.telephony; package android.telephony;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.SdkConstant; import android.annotation.SdkConstant;
@@ -26,8 +28,8 @@ import android.content.Context;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.telephony.mbms.InternalStreamingSessionCallback;
import android.telephony.mbms.InternalStreamingServiceCallback; import android.telephony.mbms.InternalStreamingServiceCallback;
import android.telephony.mbms.InternalStreamingSessionCallback;
import android.telephony.mbms.MbmsErrors; import android.telephony.mbms.MbmsErrors;
import android.telephony.mbms.MbmsStreamingSessionCallback; import android.telephony.mbms.MbmsStreamingSessionCallback;
import android.telephony.mbms.MbmsUtils; import android.telephony.mbms.MbmsUtils;
@@ -44,8 +46,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
/** /**
* This class provides functionality for streaming media over MBMS. * This class provides functionality for streaming media over MBMS.
*/ */
@@ -208,6 +208,11 @@ public class MbmsStreamingSession implements AutoCloseable {
try { try {
int returnCode = streamingService.requestUpdateStreamingServices( int returnCode = streamingService.requestUpdateStreamingServices(
mSubscriptionId, serviceClassList); mSubscriptionId, serviceClassList);
if (returnCode == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (returnCode != MbmsErrors.SUCCESS) { if (returnCode != MbmsErrors.SUCCESS) {
sendErrorToApp(returnCode, null); sendErrorToApp(returnCode, null);
} }
@@ -255,6 +260,11 @@ public class MbmsStreamingSession implements AutoCloseable {
try { try {
int returnCode = streamingService.startStreaming( int returnCode = streamingService.startStreaming(
mSubscriptionId, serviceInfo.getServiceId(), serviceCallback); mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
if (returnCode == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return an unknown error code");
}
if (returnCode != MbmsErrors.SUCCESS) { if (returnCode != MbmsErrors.SUCCESS) {
sendErrorToApp(returnCode, null); sendErrorToApp(returnCode, null);
return null; return null;
@@ -301,6 +311,12 @@ public class MbmsStreamingSession implements AutoCloseable {
sIsInitialized.set(false); sIsInitialized.set(false);
return; return;
} }
if (result == MbmsErrors.UNKNOWN) {
// Unbind and throw an obvious error
close();
throw new IllegalStateException("Middleware must not return"
+ " an unknown error code");
}
if (result != MbmsErrors.SUCCESS) { if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, "Error returned during initialization"); sendErrorToApp(result, "Error returned during initialization");
sIsInitialized.set(false); sIsInitialized.set(false);

View File

@@ -17,7 +17,6 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.os.Binder; import android.os.Binder;
import android.os.RemoteException;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -36,7 +35,7 @@ public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallbac
} }
@Override @Override
public void onError(final int errorCode, final String message) throws RemoteException { public void onError(final int errorCode, final String message) {
if (mIsStopped) { if (mIsStopped) {
return; return;
} }
@@ -55,7 +54,7 @@ public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallbac
} }
@Override @Override
public void onFileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException { public void onFileServicesUpdated(final List<FileServiceInfo> services) {
if (mIsStopped) { if (mIsStopped) {
return; return;
} }
@@ -74,7 +73,7 @@ public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallbac
} }
@Override @Override
public void onMiddlewareReady() throws RemoteException { public void onMiddlewareReady() {
if (mIsStopped) { if (mIsStopped) {
return; return;
} }

View File

@@ -31,6 +31,8 @@ import android.telephony.MbmsDownloadSession;
import android.telephony.mbms.vendor.VendorUtils; import android.telephony.mbms.vendor.VendorUtils;
import android.util.Log; import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
@@ -268,7 +270,10 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
Uri finalLocation; Uri finalLocation;
try { try {
finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination); String relativeLocation = getFileRelativePath(request.getSourceUri().getPath(),
completedFileInfo.getUri().getPath());
finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination,
relativeLocation);
} catch (IOException e) { } catch (IOException e) {
Log.w(LOG_TAG, "Failed to move temp file to final destination"); Log.w(LOG_TAG, "Failed to move temp file to final destination");
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR); setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
@@ -442,7 +447,8 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
/* /*
* Moves a tempfile located at fromPath to its final home where the app wants it * Moves a tempfile located at fromPath to its final home where the app wants it
*/ */
private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath) throws IOException { private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath,
String relativeLocation) throws IOException {
if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) { if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
Log.w(LOG_TAG, "Downloaded file location uri " + fromPath + Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
" does not have a file scheme"); " does not have a file scheme");
@@ -450,16 +456,46 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
} }
Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath()); Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
if (!Files.isDirectory(appSpecifiedPath)) { Path toFile = appSpecifiedPath.resolve(relativeLocation);
Files.createDirectory(appSpecifiedPath);
if (!Files.isDirectory(toFile.getParent())) {
Files.createDirectories(toFile.getParent());
} }
// TODO: do we want to support directory trees within the download directory? Path result = Files.move(fromFile, toFile,
Path result = Files.move(fromFile, appSpecifiedPath.resolve(fromFile.getFileName()),
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
return Uri.fromFile(result.toFile()); return Uri.fromFile(result.toFile());
} }
/**
* @hide
*/
@VisibleForTesting
public static String getFileRelativePath(String sourceUriPath, String fileInfoPath) {
if (sourceUriPath.endsWith("*")) {
// This is a wildcard path. Strip the last path component and use that as the root of
// the relative path.
int lastSlash = sourceUriPath.lastIndexOf('/');
sourceUriPath = sourceUriPath.substring(0, lastSlash);
}
if (!fileInfoPath.startsWith(sourceUriPath)) {
Log.e(LOG_TAG, "File location specified in FileInfo does not match the source URI."
+ " source: " + sourceUriPath + " fileinfo path: " + fileInfoPath);
return null;
}
if (fileInfoPath.length() == sourceUriPath.length()) {
// This is the single-file download case. Return the name of the file so that the
// receiver puts the file directly into the dest directory.
return sourceUriPath.substring(sourceUriPath.lastIndexOf('/') + 1);
}
String prefixOmittedPath = fileInfoPath.substring(sourceUriPath.length());
if (prefixOmittedPath.startsWith("/")) {
prefixOmittedPath = prefixOmittedPath.substring(1);
}
return prefixOmittedPath;
}
private static boolean verifyTempFilePath(Context context, String serviceId, private static boolean verifyTempFilePath(Context context, String serviceId,
Uri filePath) { Uri filePath) {
if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) { if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {

View File

@@ -16,8 +16,11 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.annotation.IntDef;
import android.telephony.MbmsDownloadSession; import android.telephony.MbmsDownloadSession;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
/** /**
@@ -25,6 +28,26 @@ import java.util.List;
* cell-broadcast. * cell-broadcast.
*/ */
public class MbmsDownloadSessionCallback { public class MbmsDownloadSessionCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
MbmsErrors.ERROR_MIDDLEWARE_LOST,
MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
MbmsErrors.GeneralErrors.ERROR_IN_E911,
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
MbmsErrors.DownloadErrors.ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT,
MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST,
MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO}, prefix = { "ERROR_" })
private @interface DownloadError{}
/** /**
* Indicates that the middleware has encountered an asynchronous error. * Indicates that the middleware has encountered an asynchronous error.
@@ -32,7 +55,7 @@ public class MbmsDownloadSessionCallback {
* @param message A message, intended for debugging purposes, describing the error in further * @param message A message, intended for debugging purposes, describing the error in further
* detail. * detail.
*/ */
public void onError(int errorCode, String message) { public void onError(@DownloadError int errorCode, String message) {
// default implementation empty // default implementation empty
} }

View File

@@ -19,6 +19,13 @@ package android.telephony.mbms;
import android.telephony.MbmsStreamingSession; import android.telephony.MbmsStreamingSession;
public class MbmsErrors { public class MbmsErrors {
/**
* Indicates that the middleware has sent an error code that is not defined in the version of
* the SDK targeted by your app. This is an illegal value for the middleware to return -- it
* should only ever be generated by the framework.
*/
public static final int UNKNOWN = -1;
/** Indicates that the operation was successful. */ /** Indicates that the operation was successful. */
public static final int SUCCESS = 0; public static final int SUCCESS = 0;

View File

@@ -16,11 +16,13 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.annotation.IntDef;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.os.Handler;
import android.telephony.MbmsStreamingSession; import android.telephony.MbmsStreamingSession;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -30,13 +32,34 @@ import java.util.concurrent.Executor;
* {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}. * {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}.
*/ */
public class MbmsStreamingSessionCallback { public class MbmsStreamingSessionCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
MbmsErrors.ERROR_MIDDLEWARE_LOST,
MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
MbmsErrors.GeneralErrors.ERROR_IN_E911,
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
private @interface StreamingError{}
/** /**
* Called by the middleware when it has detected an error condition. The possible error codes * Called by the middleware when it has detected an error condition. The possible error codes
* are listed in {@link MbmsErrors}. * are listed in {@link MbmsErrors}.
* @param errorCode The error code. * @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes. * @param message A human-readable message generated by the middleware for debugging purposes.
*/ */
public void onError(int errorCode, @Nullable String message) { public void onError(@StreamingError int errorCode, @Nullable String message) {
// default implementation empty // default implementation empty
} }

View File

@@ -130,8 +130,12 @@ public class MbmsUtils {
* Returns a File linked to the directory used to store temp files for this file service * Returns a File linked to the directory used to store temp files for this file service
*/ */
public static File getEmbmsTempFileDirForService(Context context, String serviceId) { public static File getEmbmsTempFileDirForService(Context context, String serviceId) {
// Replace all non-alphanumerics/underscores with an underscore. Some filesystems don't
// like special characters.
String sanitizedServiceId = serviceId.replaceAll("[^a-zA-Z0-9_]", "_");
File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context); File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
return new File(embmsTempFileDir, serviceId); return new File(embmsTempFileDir, sanitizedServiceId);
} }
} }

View File

@@ -16,13 +16,34 @@
package android.telephony.mbms; package android.telephony.mbms;
import android.annotation.IntDef;
import android.annotation.Nullable; import android.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** /**
* A callback class for use when the application is actively streaming content. The middleware * A callback class for use when the application is actively streaming content. The middleware
* will provide updates on the status of the stream via this callback. * will provide updates on the status of the stream via this callback.
*/ */
public class StreamingServiceCallback { public class StreamingServiceCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
MbmsErrors.ERROR_MIDDLEWARE_LOST,
MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
MbmsErrors.GeneralErrors.ERROR_IN_E911,
MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
private @interface StreamingServiceError{}
/** /**
* Indicates broadcast signal strength is not available for this service. * Indicates broadcast signal strength is not available for this service.
@@ -39,7 +60,7 @@ public class StreamingServiceCallback {
* @param errorCode The error code. * @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes. * @param message A human-readable message generated by the middleware for debugging purposes.
*/ */
public void onError(int errorCode, @Nullable String message) { public void onError(@StreamingServiceError int errorCode, @Nullable String message) {
// default implementation empty // default implementation empty
} }

View File

@@ -130,6 +130,10 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
@Override @Override
public void onError(int errorCode, String message) { public void onError(int errorCode, String message) {
try { try {
if (errorCode == MbmsErrors.UNKNOWN) {
throw new IllegalArgumentException(
"Middleware cannot send an unknown error.");
}
callback.onError(errorCode, message); callback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);

View File

@@ -77,6 +77,10 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
@Override @Override
public void onError(final int errorCode, final String message) { public void onError(final int errorCode, final String message) {
try { try {
if (errorCode == MbmsErrors.UNKNOWN) {
throw new IllegalArgumentException(
"Middleware cannot send an unknown error.");
}
callback.onError(errorCode, message); callback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);
@@ -173,6 +177,10 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
@Override @Override
public void onError(final int errorCode, final String message) { public void onError(final int errorCode, final String message) {
try { try {
if (errorCode == MbmsErrors.UNKNOWN) {
throw new IllegalArgumentException(
"Middleware cannot send an unknown error.");
}
callback.onError(errorCode, message); callback.onError(errorCode, message);
} catch (RemoteException e) { } catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId); onAppCallbackDied(uid, subscriptionId);