| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package android.telephony.mbms.vendor; |
| |
| import android.annotation.NonNull; |
| import android.annotation.SystemApi; |
| import android.content.Intent; |
| import android.os.Binder; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.telephony.MbmsDownloadSession; |
| import android.telephony.mbms.DownloadRequest; |
| import android.telephony.mbms.DownloadStateCallback; |
| import android.telephony.mbms.FileInfo; |
| import android.telephony.mbms.FileServiceInfo; |
| import android.telephony.mbms.IDownloadStateCallback; |
| import android.telephony.mbms.IMbmsDownloadSessionCallback; |
| import android.telephony.mbms.MbmsDownloadSessionCallback; |
| import android.telephony.mbms.MbmsErrors; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Base class for MbmsDownloadService. The middleware should return an instance of this object from |
| * its {@link android.app.Service#onBind(Intent)} method. |
| * @hide |
| */ |
| @SystemApi |
| public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { |
| private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>(); |
| private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>(); |
| |
| /** |
| * Initialize the download service for this app and subId, registering the listener. |
| * |
| * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which |
| * will be intercepted and passed to the app as |
| * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE} |
| * |
| * May return any value from {@link MbmsErrors.InitializationErrors} |
| * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via |
| * {@link IMbmsDownloadSessionCallback#onError(int, String)}. |
| * |
| * @param callback The callback to use to communicate with the app. |
| * @param subscriptionId The subscription ID to use. |
| */ |
| public int initialize(int subscriptionId, MbmsDownloadSessionCallback callback) |
| throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Actual AIDL implementation -- hides the callback AIDL from the API. |
| * @hide |
| */ |
| @Override |
| public final int initialize(final int subscriptionId, |
| final IMbmsDownloadSessionCallback callback) throws RemoteException { |
| final int uid = Binder.getCallingUid(); |
| callback.asBinder().linkToDeath(new DeathRecipient() { |
| @Override |
| public void binderDied() { |
| onAppCallbackDied(uid, subscriptionId); |
| } |
| }, 0); |
| |
| return initialize(subscriptionId, new MbmsDownloadSessionCallback() { |
| @Override |
| public void onError(int errorCode, String message) { |
| try { |
| callback.onError(errorCode, message); |
| } catch (RemoteException e) { |
| onAppCallbackDied(uid, subscriptionId); |
| } |
| } |
| |
| @Override |
| public void onFileServicesUpdated(List<FileServiceInfo> services) { |
| try { |
| callback.onFileServicesUpdated(services); |
| } catch (RemoteException e) { |
| onAppCallbackDied(uid, subscriptionId); |
| } |
| } |
| |
| @Override |
| public void onMiddlewareReady() { |
| try { |
| callback.onMiddlewareReady(); |
| } catch (RemoteException e) { |
| onAppCallbackDied(uid, subscriptionId); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Registers serviceClasses of interest with the appName/subId key. |
| * Starts async fetching data on streaming services of matching classes to be reported |
| * later via {@link IMbmsDownloadSessionCallback#onFileServicesUpdated(List)} |
| * |
| * Note that subsequent calls with the same uid and subId will replace |
| * the service class list. |
| * |
| * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException} |
| * |
| * @param subscriptionId The subscription id to use. |
| * @param serviceClasses The service classes that the app wishes to get info on. The strings |
| * may contain arbitrary data as negotiated between the app and the |
| * carrier. |
| * @return One of {@link MbmsErrors#SUCCESS} or |
| * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}, |
| */ |
| @Override |
| public int requestUpdateFileServices(int subscriptionId, List<String> serviceClasses) |
| throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Sets the temp file root directory for this app/subscriptionId combination. The middleware |
| * should persist {@code rootDirectoryPath} and send it back when sending intents to the |
| * app's {@link android.telephony.mbms.MbmsDownloadReceiver}. |
| * |
| * If the calling app (as identified by the calling UID) currently has any pending download |
| * requests that have not been canceled, the middleware must return |
| * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here. |
| * |
| * @param subscriptionId The subscription id the download is operating under. |
| * @param rootDirectoryPath The path to the app's temp file root directory. |
| * @return {@link MbmsErrors#SUCCESS}, |
| * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or |
| * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} |
| */ |
| @Override |
| public int setTempFileRootDirectory(int subscriptionId, |
| String rootDirectoryPath) throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Issues a request to download a set of files. |
| * |
| * The middleware should expect that {@link #setTempFileRootDirectory(int, String)} has been |
| * called for this app between when the app was installed and when this method is called. If |
| * this is not the case, an {@link IllegalStateException} may be thrown. |
| * |
| * @param downloadRequest An object describing the set of files to be downloaded. |
| * @return Any error from {@link MbmsErrors.GeneralErrors} |
| * or {@link MbmsErrors#SUCCESS} |
| */ |
| @Override |
| public int download(DownloadRequest downloadRequest) throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Registers a download state callbacks for the provided {@link DownloadRequest}. |
| * |
| * This method is called by the app when it wants to request updates on the progress or |
| * status of the download. |
| * |
| * If the middleware is not aware of a download having been requested with the provided |
| * |
| * {@link DownloadRequest} in the past, |
| * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST} |
| * must be returned. |
| * |
| * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download |
| * for which progress updates are being requested. |
| * @param callback The callback object to use. |
| */ |
| public int registerStateCallback(DownloadRequest downloadRequest, |
| DownloadStateCallback callback) throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Actual AIDL implementation -- hides the callback AIDL from the API. |
| * @hide |
| */ |
| @Override |
| public final int registerStateCallback( |
| final DownloadRequest downloadRequest, final IDownloadStateCallback callback) |
| throws RemoteException { |
| final int uid = Binder.getCallingUid(); |
| DeathRecipient deathRecipient = new DeathRecipient() { |
| @Override |
| public void binderDied() { |
| onAppCallbackDied(uid, downloadRequest.getSubscriptionId()); |
| mDownloadCallbackBinderMap.remove(callback.asBinder()); |
| mDownloadCallbackDeathRecipients.remove(callback.asBinder()); |
| } |
| }; |
| mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient); |
| callback.asBinder().linkToDeath(deathRecipient, 0); |
| |
| DownloadStateCallback exposedCallback = new DownloadStateCallback() { |
| @Override |
| public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int |
| currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int |
| fullDecodedSize) { |
| try { |
| callback.onProgressUpdated(request, fileInfo, currentDownloadSize, |
| fullDownloadSize, |
| currentDecodedSize, fullDecodedSize); |
| } catch (RemoteException e) { |
| onAppCallbackDied(uid, downloadRequest.getSubscriptionId()); |
| } |
| } |
| |
| @Override |
| public void onStateUpdated(DownloadRequest request, FileInfo fileInfo, |
| @MbmsDownloadSession.DownloadStatus int state) { |
| try { |
| callback.onStateUpdated(request, fileInfo, state); |
| } catch (RemoteException e) { |
| onAppCallbackDied(uid, downloadRequest.getSubscriptionId()); |
| } |
| } |
| }; |
| |
| mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback); |
| |
| return registerStateCallback(downloadRequest, exposedCallback); |
| } |
| |
| /** |
| * Un-registers a download state callbacks for the provided {@link DownloadRequest}. |
| * |
| * This method is called by the app when it no longer wants to request updates on the |
| * download. |
| * |
| * If the middleware is not aware of a download having been requested with the provided |
| * {@link DownloadRequest} in the past, |
| * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST} |
| * must be returned. |
| * |
| * @param downloadRequest The {@link DownloadRequest} that was used to register the callback |
| * @param callback The callback object that |
| * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback)} |
| * was called with. |
| */ |
| public int unregisterStateCallback(DownloadRequest downloadRequest, |
| DownloadStateCallback callback) throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Actual AIDL implementation -- hides the callback AIDL from the API. |
| * @hide |
| */ |
| @Override |
| public final int unregisterStateCallback( |
| final DownloadRequest downloadRequest, final IDownloadStateCallback callback) |
| throws RemoteException { |
| DeathRecipient deathRecipient = |
| mDownloadCallbackDeathRecipients.remove(callback.asBinder()); |
| if (deathRecipient == null) { |
| throw new IllegalArgumentException("Unknown callback"); |
| } |
| |
| callback.asBinder().unlinkToDeath(deathRecipient, 0); |
| |
| DownloadStateCallback exposedCallback = |
| mDownloadCallbackBinderMap.remove(callback.asBinder()); |
| if (exposedCallback == null) { |
| throw new IllegalArgumentException("Unknown callback"); |
| } |
| |
| return unregisterStateCallback(downloadRequest, exposedCallback); |
| } |
| |
| /** |
| * Returns a list of pending {@link DownloadRequest}s that originated from the calling |
| * application, identified by its uid. A pending request is one that was issued via |
| * {@link #download(DownloadRequest)} but not cancelled through |
| * {@link #cancelDownload(DownloadRequest)}. |
| * The middleware must return a non-null result synchronously or throw an exception |
| * inheriting from {@link RuntimeException}. |
| * @return A list, possibly empty, of {@link DownloadRequest}s |
| */ |
| @Override |
| public @NonNull List<DownloadRequest> listPendingDownloads(int subscriptionId) |
| throws RemoteException { |
| return null; |
| } |
| |
| /** |
| * Issues a request to cancel the specified download request. |
| * |
| * If the middleware is unable to cancel the request for whatever reason, it should return |
| * synchronously with an error. If this method returns {@link MbmsErrors#SUCCESS}, the app |
| * will no longer be expecting any more file-completed intents from the middleware for this |
| * {@link DownloadRequest}. |
| * @param downloadRequest The request to cancel |
| * @return {@link MbmsErrors#SUCCESS}, |
| * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}, |
| * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} |
| */ |
| @Override |
| public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Gets information about the status of a file pending download. |
| * |
| * If the middleware has not yet been properly initialized or if it has no records of the |
| * file indicated by {@code fileInfo} being associated with {@code downloadRequest}, |
| * {@link MbmsDownloadSession#STATUS_UNKNOWN} must be returned. |
| * |
| * @param downloadRequest The download request to query. |
| * @param fileInfo The particular file within the request to get information on. |
| * @return The status of the download. |
| */ |
| @Override |
| public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) |
| throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Resets the middleware's knowledge of previously-downloaded files in this download request. |
| * |
| * When this method is called, the middleware must attempt to re-download all the files |
| * specified by the {@link DownloadRequest}, even if the files have not changed on the server. |
| * In addition, current in-progress downloads must not be interrupted. |
| * |
| * If the middleware is not aware of the specified download request, return |
| * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}. |
| * |
| * @param downloadRequest The request to re-download files for. |
| */ |
| @Override |
| public int resetDownloadKnowledge(DownloadRequest downloadRequest) |
| throws RemoteException { |
| return 0; |
| } |
| |
| /** |
| * Signals that the app wishes to dispose of the session identified by the |
| * {@code subscriptionId} argument and the caller's uid. No notification back to the |
| * app is required for this operation, and the corresponding callback provided via |
| * {@link #initialize(int, IMbmsDownloadSessionCallback)} should no longer be used |
| * after this method has been called by the app. |
| * |
| * Any download requests issued by the app should remain in effect until the app calls |
| * {@link #cancelDownload(DownloadRequest)} on another session. |
| * |
| * May throw an {@link IllegalStateException} |
| * |
| * @param subscriptionId The subscription id to use. |
| */ |
| @Override |
| public void dispose(int subscriptionId) throws RemoteException { |
| } |
| |
| /** |
| * Indicates that the app identified by the given UID and subscription ID has died. |
| * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}. |
| * @param subscriptionId The subscription ID the app is using. |
| */ |
| public void onAppCallbackDied(int uid, int subscriptionId) { |
| } |
| } |