Loading apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl +39 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,25 @@ import android.app.job.JobWorkItem; * {@hide} */ interface IJobCallback { /** * Immediate callback to the system after sending a data transfer download progress request * signal; used to quickly detect ANR. * * @param jobId Unique integer used to identify this job. * @param workId Unique integer used to identify a specific work item. * @param transferredBytes How much data has been downloaded, in bytes. */ void acknowledgeGetTransferredDownloadBytesMessage(int jobId, int workId, long transferredBytes); /** * Immediate callback to the system after sending a data transfer upload progress request * signal; used to quickly detect ANR. * * @param jobId Unique integer used to identify this job. * @param workId Unique integer used to identify a specific work item. * @param transferredBytes How much data has been uploaded, in bytes. */ void acknowledgeGetTransferredUploadBytesMessage(int jobId, int workId, long transferredBytes); /** * Immediate callback to the system after sending a start signal, used to quickly detect ANR. * Loading Loading @@ -65,4 +84,24 @@ interface IJobCallback { */ @UnsupportedAppUsage void jobFinished(int jobId, boolean reschedule); /* * Inform JobScheduler of a change in the estimated transfer payload. * * @param jobId Unique integer used to identify this job. * @param item The particular JobWorkItem this progress is associated with, if any. * @param downloadBytes How many bytes the app expects to download. * @param uploadBytes How many bytes the app expects to upload. */ void updateEstimatedNetworkBytes(int jobId, in JobWorkItem item, long downloadBytes, long uploadBytes); /* * Update JobScheduler of how much data the job has successfully transferred. * * @param jobId Unique integer used to identify this job. * @param item The particular JobWorkItem this progress is associated with, if any. * @param transferredDownloadBytes The number of bytes that have successfully been downloaded. * @param transferredUploadBytes The number of bytes that have successfully been uploaded. */ void updateTransferredNetworkBytes(int jobId, in JobWorkItem item, long transferredDownloadBytes, long transferredUploadBytes); } apex/jobscheduler/framework/java/android/app/job/IJobService.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.app.job; import android.app.job.JobParameters; import android.app.job.JobWorkItem; /** * Interface that the framework uses to communicate with application code that implements a Loading @@ -31,4 +32,8 @@ oneway interface IJobService { /** Stop execution of application's job. */ @UnsupportedAppUsage void stopJob(in JobParameters jobParams); /** Update JS of how much data has been downloaded. */ void getTransferredDownloadBytes(in JobParameters jobParams, in JobWorkItem jobWorkItem); /** Update JS of how much data has been uploaded. */ void getTransferredUploadBytes(in JobParameters jobParams, in JobWorkItem jobWorkItem); } apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +13 −0 Original line number Diff line number Diff line Loading @@ -22,8 +22,11 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.ClipData; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; Loading Loading @@ -93,6 +96,16 @@ import java.util.List; */ @SystemService(Context.JOB_SCHEDULER_SERVICE) public abstract class JobScheduler { /** * Whether to throw an exception when an app doesn't properly implement all the necessary * data transfer APIs. * * @hide */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION = 255371817L; /** @hide */ @IntDef(prefix = { "RESULT_" }, value = { RESULT_FAILURE, Loading apex/jobscheduler/framework/java/android/app/job/JobService.java +185 −0 Original line number Diff line number Diff line Loading @@ -16,7 +16,13 @@ package android.app.job; import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION; import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; import android.compat.Compatibility; import android.content.Intent; import android.os.IBinder; Loading Loading @@ -72,6 +78,28 @@ public abstract class JobService extends Service { public boolean onStopJob(JobParameters params) { return JobService.this.onStopJob(params); } @Override @BytesLong public long getTransferredDownloadBytes(@NonNull JobParameters params, @Nullable JobWorkItem item) { if (item == null) { return JobService.this.getTransferredDownloadBytes(); } else { return JobService.this.getTransferredDownloadBytes(item); } } @Override @BytesLong public long getTransferredUploadBytes(@NonNull JobParameters params, @Nullable JobWorkItem item) { if (item == null) { return JobService.this.getTransferredUploadBytes(); } else { return JobService.this.getTransferredUploadBytes(item); } } }; } return mEngine.getBinder(); Loading Loading @@ -171,4 +199,161 @@ public abstract class JobService extends Service { * to end the job entirely. Regardless of the value returned, your job must stop executing. */ public abstract boolean onStopJob(JobParameters params); /** * Update how much data this job will transfer. This method can * be called multiple times within the first 30 seconds after * {@link #onStartJob(JobParameters)} has been called. Only * one call will be heeded after that time has passed. * * This method (or an overload) must be called within the first * 30 seconds for a data transfer job if a payload size estimate * was not provided at the time of scheduling. * * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long) */ public final void updateEstimatedNetworkBytes(@NonNull JobParameters params, @BytesLong long downloadBytes, @BytesLong long uploadBytes) { mEngine.updateEstimatedNetworkBytes(params, null, downloadBytes, uploadBytes); } /** * Update how much data will transfer for the JobWorkItem. This * method can be called multiple times within the first 30 seconds * after {@link #onStartJob(JobParameters)} has been called. * Only one call will be heeded after that time has passed. * * This method (or an overload) must be called within the first * 30 seconds for a data transfer job if a payload size estimate * was not provided at the time of scheduling. * * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long) */ public final void updateEstimatedNetworkBytes(@NonNull JobParameters params, @NonNull JobWorkItem jobWorkItem, @BytesLong long downloadBytes, @BytesLong long uploadBytes) { mEngine.updateEstimatedNetworkBytes(params, jobWorkItem, downloadBytes, uploadBytes); } /** * Tell JobScheduler how much data has successfully been transferred for the data transfer job. */ public final void updateTransferredNetworkBytes(@NonNull JobParameters params, @BytesLong long transferredDownloadBytes, @BytesLong long transferredUploadBytes) { mEngine.updateTransferredNetworkBytes(params, null, transferredDownloadBytes, transferredUploadBytes); } /** * Tell JobScheduler how much data has been transferred for the data transfer * {@link JobWorkItem}. */ public final void updateTransferredNetworkBytes(@NonNull JobParameters params, @NonNull JobWorkItem item, @BytesLong long transferredDownloadBytes, @BytesLong long transferredUploadBytes) { mEngine.updateTransferredNetworkBytes(params, item, transferredDownloadBytes, transferredUploadBytes); } /** * Get the number of bytes the app has successfully downloaded for this job. JobScheduler * will call this if the job has specified positive estimated download bytes and * {@link #updateTransferredNetworkBytes(JobParameters, long, long)} * hasn't been called recently. * * <p> * This must be implemented for all data transfer jobs. * * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long) * @see JobInfo#NETWORK_BYTES_UNKNOWN */ // TODO(255371817): specify the actual time JS will wait for progress before requesting @BytesLong public long getTransferredDownloadBytes() { if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { // Regular jobs don't have to implement this and JobScheduler won't call this API for // non-data transfer jobs. throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Get the number of bytes the app has successfully downloaded for this job. JobScheduler * will call this if the job has specified positive estimated upload bytes and * {@link #updateTransferredNetworkBytes(JobParameters, long, long)} * hasn't been called recently. * * <p> * This must be implemented for all data transfer jobs. * * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long) * @see JobInfo#NETWORK_BYTES_UNKNOWN */ // TODO(255371817): specify the actual time JS will wait for progress before requesting @BytesLong public long getTransferredUploadBytes() { if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { // Regular jobs don't have to implement this and JobScheduler won't call this API for // non-data transfer jobs. throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Get the number of bytes the app has successfully downloaded for this job. JobScheduler * will call this if the job has specified positive estimated download bytes and * {@link #updateTransferredNetworkBytes(JobParameters, JobWorkItem, long, long)} * hasn't been called recently and the job has * {@link JobWorkItem JobWorkItems} that have been * {@link JobParameters#dequeueWork dequeued} but not * {@link JobParameters#completeWork(JobWorkItem) completed}. * * <p> * This must be implemented for all data transfer jobs. * * @see JobInfo#NETWORK_BYTES_UNKNOWN */ // TODO(255371817): specify the actual time JS will wait for progress before requesting @BytesLong public long getTransferredDownloadBytes(@NonNull JobWorkItem item) { if (item == null) { return getTransferredDownloadBytes(); } if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { // Regular jobs don't have to implement this and JobScheduler won't call this API for // non-data transfer jobs. throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Get the number of bytes the app has successfully downloaded for this job. JobScheduler * will call this if the job has specified positive estimated upload bytes and * {@link #updateTransferredNetworkBytes(JobParameters, JobWorkItem, long, long)} * hasn't been called recently and the job has * {@link JobWorkItem JobWorkItems} that have been * {@link JobParameters#dequeueWork dequeued} but not * {@link JobParameters#completeWork(JobWorkItem) completed}. * * <p> * This must be implemented for all data transfer jobs. * * @see JobInfo#NETWORK_BYTES_UNKNOWN */ // TODO(255371817): specify the actual time JS will wait for progress before requesting @BytesLong public long getTransferredUploadBytes(@NonNull JobWorkItem item) { if (item == null) { return getTransferredUploadBytes(); } if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { // Regular jobs don't have to implement this and JobScheduler won't call this API for // non-data transfer jobs. throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } } apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java +220 −5 Original line number Diff line number Diff line Loading @@ -16,7 +16,13 @@ package android.app.job; import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION; import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; import android.compat.Compatibility; import android.content.Intent; import android.os.Handler; import android.os.IBinder; Loading @@ -25,6 +31,8 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; import com.android.internal.os.SomeArgs; import java.lang.ref.WeakReference; /** Loading @@ -51,6 +59,20 @@ public abstract class JobServiceEngine { * Message that the client has completed execution of this job. */ private static final int MSG_JOB_FINISHED = 2; /** * Message that will result in a call to * {@link #getTransferredDownloadBytes(JobParameters, JobWorkItem)}. */ private static final int MSG_GET_TRANSFERRED_DOWNLOAD_BYTES = 3; /** * Message that will result in a call to * {@link #getTransferredUploadBytes(JobParameters, JobWorkItem)}. */ private static final int MSG_GET_TRANSFERRED_UPLOAD_BYTES = 4; /** Message that the client wants to update JobScheduler of the data transfer progress. */ private static final int MSG_UPDATE_TRANSFERRED_NETWORK_BYTES = 5; /** Message that the client wants to update JobScheduler of the estimated transfer size. */ private static final int MSG_UPDATE_ESTIMATED_NETWORK_BYTES = 6; private final IJobService mBinder; Loading @@ -67,6 +89,32 @@ public abstract class JobServiceEngine { mService = new WeakReference<>(service); } @Override public void getTransferredDownloadBytes(@NonNull JobParameters jobParams, @Nullable JobWorkItem jobWorkItem) throws RemoteException { JobServiceEngine service = mService.get(); if (service != null) { SomeArgs args = SomeArgs.obtain(); args.arg1 = jobParams; args.arg2 = jobWorkItem; service.mHandler.obtainMessage(MSG_GET_TRANSFERRED_DOWNLOAD_BYTES, args) .sendToTarget(); } } @Override public void getTransferredUploadBytes(@NonNull JobParameters jobParams, @Nullable JobWorkItem jobWorkItem) throws RemoteException { JobServiceEngine service = mService.get(); if (service != null) { SomeArgs args = SomeArgs.obtain(); args.arg1 = jobParams; args.arg2 = jobWorkItem; service.mHandler.obtainMessage(MSG_GET_TRANSFERRED_UPLOAD_BYTES, args) .sendToTarget(); } } @Override public void startJob(JobParameters jobParams) throws RemoteException { JobServiceEngine service = mService.get(); Loading Loading @@ -98,9 +146,9 @@ public abstract class JobServiceEngine { @Override public void handleMessage(Message msg) { final JobParameters params = (JobParameters) msg.obj; switch (msg.what) { case MSG_EXECUTE_JOB: case MSG_EXECUTE_JOB: { final JobParameters params = (JobParameters) msg.obj; try { boolean workOngoing = JobServiceEngine.this.onStartJob(params); ackStartMessage(params, workOngoing); Loading @@ -109,7 +157,9 @@ public abstract class JobServiceEngine { throw new RuntimeException(e); } break; case MSG_STOP_JOB: } case MSG_STOP_JOB: { final JobParameters params = (JobParameters) msg.obj; try { boolean ret = JobServiceEngine.this.onStopJob(params); ackStopMessage(params, ret); Loading @@ -118,7 +168,9 @@ public abstract class JobServiceEngine { throw new RuntimeException(e); } break; case MSG_JOB_FINISHED: } case MSG_JOB_FINISHED: { final JobParameters params = (JobParameters) msg.obj; final boolean needsReschedule = (msg.arg2 == 1); IJobCallback callback = params.getCallback(); if (callback != null) { Loading @@ -132,12 +184,110 @@ public abstract class JobServiceEngine { Log.e(TAG, "finishJob() called for a nonexistent job id."); } break; } case MSG_GET_TRANSFERRED_DOWNLOAD_BYTES: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; final JobWorkItem item = (JobWorkItem) args.arg2; try { long ret = JobServiceEngine.this.getTransferredDownloadBytes(params, item); ackGetTransferredDownloadBytesMessage(params, item, ret); } catch (Exception e) { Log.e(TAG, "Application unable to handle getTransferredDownloadBytes.", e); throw new RuntimeException(e); } args.recycle(); break; } case MSG_GET_TRANSFERRED_UPLOAD_BYTES: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; final JobWorkItem item = (JobWorkItem) args.arg2; try { long ret = JobServiceEngine.this.getTransferredUploadBytes(params, item); ackGetTransferredUploadBytesMessage(params, item, ret); } catch (Exception e) { Log.e(TAG, "Application unable to handle getTransferredUploadBytes.", e); throw new RuntimeException(e); } args.recycle(); break; } case MSG_UPDATE_TRANSFERRED_NETWORK_BYTES: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; IJobCallback callback = params.getCallback(); if (callback != null) { try { callback.updateTransferredNetworkBytes(params.getJobId(), (JobWorkItem) args.arg2, args.argl1, args.argl2); } catch (RemoteException e) { Log.e(TAG, "Error updating data transfer progress to system:" + " binder has gone away."); } } else { Log.e(TAG, "updateDataTransferProgress() called for a nonexistent job id."); } args.recycle(); break; } case MSG_UPDATE_ESTIMATED_NETWORK_BYTES: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; IJobCallback callback = params.getCallback(); if (callback != null) { try { callback.updateEstimatedNetworkBytes(params.getJobId(), (JobWorkItem) args.arg2, args.argl1, args.argl2); } catch (RemoteException e) { Log.e(TAG, "Error updating estimated transfer size to system:" + " binder has gone away."); } } else { Log.e(TAG, "updateEstimatedNetworkBytes() called for a nonexistent job id."); } args.recycle(); break; } default: Log.e(TAG, "Unrecognised message received."); break; } } private void ackGetTransferredDownloadBytesMessage(@NonNull JobParameters params, @Nullable JobWorkItem item, long progress) { final IJobCallback callback = params.getCallback(); final int jobId = params.getJobId(); final int workId = item == null ? -1 : item.getWorkId(); if (callback != null) { try { callback.acknowledgeGetTransferredDownloadBytesMessage(jobId, workId, progress); } catch (RemoteException e) { Log.e(TAG, "System unreachable for returning progress."); } } else if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Attempting to ack a job that has already been processed."); } } private void ackGetTransferredUploadBytesMessage(@NonNull JobParameters params, @Nullable JobWorkItem item, long progress) { final IJobCallback callback = params.getCallback(); final int jobId = params.getJobId(); final int workId = item == null ? -1 : item.getWorkId(); if (callback != null) { try { callback.acknowledgeGetTransferredUploadBytesMessage(jobId, workId, progress); } catch (RemoteException e) { Log.e(TAG, "System unreachable for returning progress."); } } else if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Attempting to ack a job that has already been processed."); } } private void ackStartMessage(JobParameters params, boolean workOngoing) { final IJobCallback callback = params.getCallback(); final int jobId = params.getJobId(); Loading Loading @@ -213,4 +363,69 @@ public abstract class JobServiceEngine { m.arg2 = needsReschedule ? 1 : 0; m.sendToTarget(); } /** * Engine's request to get how much data has been downloaded. * * @see JobService#getTransferredDownloadBytes() */ @BytesLong public long getTransferredDownloadBytes(@NonNull JobParameters params, @Nullable JobWorkItem item) { if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Engine's request to get how much data has been uploaded. * * @see JobService#getTransferredUploadBytes() */ @BytesLong public long getTransferredUploadBytes(@NonNull JobParameters params, @Nullable JobWorkItem item) { if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Call in to engine to report data transfer progress. * * @see JobService#updateTransferredNetworkBytes(JobParameters, long, long) */ public void updateTransferredNetworkBytes(@NonNull JobParameters params, @Nullable JobWorkItem item, long downloadBytes, long uploadBytes) { if (params == null) { throw new NullPointerException("params"); } SomeArgs args = SomeArgs.obtain(); args.arg1 = params; args.arg2 = item; args.argl1 = downloadBytes; args.argl2 = uploadBytes; mHandler.obtainMessage(MSG_UPDATE_TRANSFERRED_NETWORK_BYTES, args).sendToTarget(); } /** * Call in to engine to report data transfer progress. * * @see JobService#updateEstimatedNetworkBytes(JobParameters, JobWorkItem, long, long) */ public void updateEstimatedNetworkBytes(@NonNull JobParameters params, @NonNull JobWorkItem item, @BytesLong long downloadBytes, @BytesLong long uploadBytes) { if (params == null) { throw new NullPointerException("params"); } SomeArgs args = SomeArgs.obtain(); args.arg1 = params; args.arg2 = item; args.argl1 = downloadBytes; args.argl2 = uploadBytes; mHandler.obtainMessage(MSG_UPDATE_ESTIMATED_NETWORK_BYTES, args).sendToTarget(); } } No newline at end of file Loading
apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl +39 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,25 @@ import android.app.job.JobWorkItem; * {@hide} */ interface IJobCallback { /** * Immediate callback to the system after sending a data transfer download progress request * signal; used to quickly detect ANR. * * @param jobId Unique integer used to identify this job. * @param workId Unique integer used to identify a specific work item. * @param transferredBytes How much data has been downloaded, in bytes. */ void acknowledgeGetTransferredDownloadBytesMessage(int jobId, int workId, long transferredBytes); /** * Immediate callback to the system after sending a data transfer upload progress request * signal; used to quickly detect ANR. * * @param jobId Unique integer used to identify this job. * @param workId Unique integer used to identify a specific work item. * @param transferredBytes How much data has been uploaded, in bytes. */ void acknowledgeGetTransferredUploadBytesMessage(int jobId, int workId, long transferredBytes); /** * Immediate callback to the system after sending a start signal, used to quickly detect ANR. * Loading Loading @@ -65,4 +84,24 @@ interface IJobCallback { */ @UnsupportedAppUsage void jobFinished(int jobId, boolean reschedule); /* * Inform JobScheduler of a change in the estimated transfer payload. * * @param jobId Unique integer used to identify this job. * @param item The particular JobWorkItem this progress is associated with, if any. * @param downloadBytes How many bytes the app expects to download. * @param uploadBytes How many bytes the app expects to upload. */ void updateEstimatedNetworkBytes(int jobId, in JobWorkItem item, long downloadBytes, long uploadBytes); /* * Update JobScheduler of how much data the job has successfully transferred. * * @param jobId Unique integer used to identify this job. * @param item The particular JobWorkItem this progress is associated with, if any. * @param transferredDownloadBytes The number of bytes that have successfully been downloaded. * @param transferredUploadBytes The number of bytes that have successfully been uploaded. */ void updateTransferredNetworkBytes(int jobId, in JobWorkItem item, long transferredDownloadBytes, long transferredUploadBytes); }
apex/jobscheduler/framework/java/android/app/job/IJobService.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.app.job; import android.app.job.JobParameters; import android.app.job.JobWorkItem; /** * Interface that the framework uses to communicate with application code that implements a Loading @@ -31,4 +32,8 @@ oneway interface IJobService { /** Stop execution of application's job. */ @UnsupportedAppUsage void stopJob(in JobParameters jobParams); /** Update JS of how much data has been downloaded. */ void getTransferredDownloadBytes(in JobParameters jobParams, in JobWorkItem jobWorkItem); /** Update JS of how much data has been uploaded. */ void getTransferredUploadBytes(in JobParameters jobParams, in JobWorkItem jobWorkItem); }
apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +13 −0 Original line number Diff line number Diff line Loading @@ -22,8 +22,11 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.ClipData; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; Loading Loading @@ -93,6 +96,16 @@ import java.util.List; */ @SystemService(Context.JOB_SCHEDULER_SERVICE) public abstract class JobScheduler { /** * Whether to throw an exception when an app doesn't properly implement all the necessary * data transfer APIs. * * @hide */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION = 255371817L; /** @hide */ @IntDef(prefix = { "RESULT_" }, value = { RESULT_FAILURE, Loading
apex/jobscheduler/framework/java/android/app/job/JobService.java +185 −0 Original line number Diff line number Diff line Loading @@ -16,7 +16,13 @@ package android.app.job; import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION; import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; import android.compat.Compatibility; import android.content.Intent; import android.os.IBinder; Loading Loading @@ -72,6 +78,28 @@ public abstract class JobService extends Service { public boolean onStopJob(JobParameters params) { return JobService.this.onStopJob(params); } @Override @BytesLong public long getTransferredDownloadBytes(@NonNull JobParameters params, @Nullable JobWorkItem item) { if (item == null) { return JobService.this.getTransferredDownloadBytes(); } else { return JobService.this.getTransferredDownloadBytes(item); } } @Override @BytesLong public long getTransferredUploadBytes(@NonNull JobParameters params, @Nullable JobWorkItem item) { if (item == null) { return JobService.this.getTransferredUploadBytes(); } else { return JobService.this.getTransferredUploadBytes(item); } } }; } return mEngine.getBinder(); Loading Loading @@ -171,4 +199,161 @@ public abstract class JobService extends Service { * to end the job entirely. Regardless of the value returned, your job must stop executing. */ public abstract boolean onStopJob(JobParameters params); /** * Update how much data this job will transfer. This method can * be called multiple times within the first 30 seconds after * {@link #onStartJob(JobParameters)} has been called. Only * one call will be heeded after that time has passed. * * This method (or an overload) must be called within the first * 30 seconds for a data transfer job if a payload size estimate * was not provided at the time of scheduling. * * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long) */ public final void updateEstimatedNetworkBytes(@NonNull JobParameters params, @BytesLong long downloadBytes, @BytesLong long uploadBytes) { mEngine.updateEstimatedNetworkBytes(params, null, downloadBytes, uploadBytes); } /** * Update how much data will transfer for the JobWorkItem. This * method can be called multiple times within the first 30 seconds * after {@link #onStartJob(JobParameters)} has been called. * Only one call will be heeded after that time has passed. * * This method (or an overload) must be called within the first * 30 seconds for a data transfer job if a payload size estimate * was not provided at the time of scheduling. * * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long) */ public final void updateEstimatedNetworkBytes(@NonNull JobParameters params, @NonNull JobWorkItem jobWorkItem, @BytesLong long downloadBytes, @BytesLong long uploadBytes) { mEngine.updateEstimatedNetworkBytes(params, jobWorkItem, downloadBytes, uploadBytes); } /** * Tell JobScheduler how much data has successfully been transferred for the data transfer job. */ public final void updateTransferredNetworkBytes(@NonNull JobParameters params, @BytesLong long transferredDownloadBytes, @BytesLong long transferredUploadBytes) { mEngine.updateTransferredNetworkBytes(params, null, transferredDownloadBytes, transferredUploadBytes); } /** * Tell JobScheduler how much data has been transferred for the data transfer * {@link JobWorkItem}. */ public final void updateTransferredNetworkBytes(@NonNull JobParameters params, @NonNull JobWorkItem item, @BytesLong long transferredDownloadBytes, @BytesLong long transferredUploadBytes) { mEngine.updateTransferredNetworkBytes(params, item, transferredDownloadBytes, transferredUploadBytes); } /** * Get the number of bytes the app has successfully downloaded for this job. JobScheduler * will call this if the job has specified positive estimated download bytes and * {@link #updateTransferredNetworkBytes(JobParameters, long, long)} * hasn't been called recently. * * <p> * This must be implemented for all data transfer jobs. * * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long) * @see JobInfo#NETWORK_BYTES_UNKNOWN */ // TODO(255371817): specify the actual time JS will wait for progress before requesting @BytesLong public long getTransferredDownloadBytes() { if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { // Regular jobs don't have to implement this and JobScheduler won't call this API for // non-data transfer jobs. throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Get the number of bytes the app has successfully downloaded for this job. JobScheduler * will call this if the job has specified positive estimated upload bytes and * {@link #updateTransferredNetworkBytes(JobParameters, long, long)} * hasn't been called recently. * * <p> * This must be implemented for all data transfer jobs. * * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long) * @see JobInfo#NETWORK_BYTES_UNKNOWN */ // TODO(255371817): specify the actual time JS will wait for progress before requesting @BytesLong public long getTransferredUploadBytes() { if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { // Regular jobs don't have to implement this and JobScheduler won't call this API for // non-data transfer jobs. throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Get the number of bytes the app has successfully downloaded for this job. JobScheduler * will call this if the job has specified positive estimated download bytes and * {@link #updateTransferredNetworkBytes(JobParameters, JobWorkItem, long, long)} * hasn't been called recently and the job has * {@link JobWorkItem JobWorkItems} that have been * {@link JobParameters#dequeueWork dequeued} but not * {@link JobParameters#completeWork(JobWorkItem) completed}. * * <p> * This must be implemented for all data transfer jobs. * * @see JobInfo#NETWORK_BYTES_UNKNOWN */ // TODO(255371817): specify the actual time JS will wait for progress before requesting @BytesLong public long getTransferredDownloadBytes(@NonNull JobWorkItem item) { if (item == null) { return getTransferredDownloadBytes(); } if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { // Regular jobs don't have to implement this and JobScheduler won't call this API for // non-data transfer jobs. throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Get the number of bytes the app has successfully downloaded for this job. JobScheduler * will call this if the job has specified positive estimated upload bytes and * {@link #updateTransferredNetworkBytes(JobParameters, JobWorkItem, long, long)} * hasn't been called recently and the job has * {@link JobWorkItem JobWorkItems} that have been * {@link JobParameters#dequeueWork dequeued} but not * {@link JobParameters#completeWork(JobWorkItem) completed}. * * <p> * This must be implemented for all data transfer jobs. * * @see JobInfo#NETWORK_BYTES_UNKNOWN */ // TODO(255371817): specify the actual time JS will wait for progress before requesting @BytesLong public long getTransferredUploadBytes(@NonNull JobWorkItem item) { if (item == null) { return getTransferredUploadBytes(); } if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { // Regular jobs don't have to implement this and JobScheduler won't call this API for // non-data transfer jobs. throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } }
apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java +220 −5 Original line number Diff line number Diff line Loading @@ -16,7 +16,13 @@ package android.app.job; import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION; import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; import android.compat.Compatibility; import android.content.Intent; import android.os.Handler; import android.os.IBinder; Loading @@ -25,6 +31,8 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; import com.android.internal.os.SomeArgs; import java.lang.ref.WeakReference; /** Loading @@ -51,6 +59,20 @@ public abstract class JobServiceEngine { * Message that the client has completed execution of this job. */ private static final int MSG_JOB_FINISHED = 2; /** * Message that will result in a call to * {@link #getTransferredDownloadBytes(JobParameters, JobWorkItem)}. */ private static final int MSG_GET_TRANSFERRED_DOWNLOAD_BYTES = 3; /** * Message that will result in a call to * {@link #getTransferredUploadBytes(JobParameters, JobWorkItem)}. */ private static final int MSG_GET_TRANSFERRED_UPLOAD_BYTES = 4; /** Message that the client wants to update JobScheduler of the data transfer progress. */ private static final int MSG_UPDATE_TRANSFERRED_NETWORK_BYTES = 5; /** Message that the client wants to update JobScheduler of the estimated transfer size. */ private static final int MSG_UPDATE_ESTIMATED_NETWORK_BYTES = 6; private final IJobService mBinder; Loading @@ -67,6 +89,32 @@ public abstract class JobServiceEngine { mService = new WeakReference<>(service); } @Override public void getTransferredDownloadBytes(@NonNull JobParameters jobParams, @Nullable JobWorkItem jobWorkItem) throws RemoteException { JobServiceEngine service = mService.get(); if (service != null) { SomeArgs args = SomeArgs.obtain(); args.arg1 = jobParams; args.arg2 = jobWorkItem; service.mHandler.obtainMessage(MSG_GET_TRANSFERRED_DOWNLOAD_BYTES, args) .sendToTarget(); } } @Override public void getTransferredUploadBytes(@NonNull JobParameters jobParams, @Nullable JobWorkItem jobWorkItem) throws RemoteException { JobServiceEngine service = mService.get(); if (service != null) { SomeArgs args = SomeArgs.obtain(); args.arg1 = jobParams; args.arg2 = jobWorkItem; service.mHandler.obtainMessage(MSG_GET_TRANSFERRED_UPLOAD_BYTES, args) .sendToTarget(); } } @Override public void startJob(JobParameters jobParams) throws RemoteException { JobServiceEngine service = mService.get(); Loading Loading @@ -98,9 +146,9 @@ public abstract class JobServiceEngine { @Override public void handleMessage(Message msg) { final JobParameters params = (JobParameters) msg.obj; switch (msg.what) { case MSG_EXECUTE_JOB: case MSG_EXECUTE_JOB: { final JobParameters params = (JobParameters) msg.obj; try { boolean workOngoing = JobServiceEngine.this.onStartJob(params); ackStartMessage(params, workOngoing); Loading @@ -109,7 +157,9 @@ public abstract class JobServiceEngine { throw new RuntimeException(e); } break; case MSG_STOP_JOB: } case MSG_STOP_JOB: { final JobParameters params = (JobParameters) msg.obj; try { boolean ret = JobServiceEngine.this.onStopJob(params); ackStopMessage(params, ret); Loading @@ -118,7 +168,9 @@ public abstract class JobServiceEngine { throw new RuntimeException(e); } break; case MSG_JOB_FINISHED: } case MSG_JOB_FINISHED: { final JobParameters params = (JobParameters) msg.obj; final boolean needsReschedule = (msg.arg2 == 1); IJobCallback callback = params.getCallback(); if (callback != null) { Loading @@ -132,12 +184,110 @@ public abstract class JobServiceEngine { Log.e(TAG, "finishJob() called for a nonexistent job id."); } break; } case MSG_GET_TRANSFERRED_DOWNLOAD_BYTES: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; final JobWorkItem item = (JobWorkItem) args.arg2; try { long ret = JobServiceEngine.this.getTransferredDownloadBytes(params, item); ackGetTransferredDownloadBytesMessage(params, item, ret); } catch (Exception e) { Log.e(TAG, "Application unable to handle getTransferredDownloadBytes.", e); throw new RuntimeException(e); } args.recycle(); break; } case MSG_GET_TRANSFERRED_UPLOAD_BYTES: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; final JobWorkItem item = (JobWorkItem) args.arg2; try { long ret = JobServiceEngine.this.getTransferredUploadBytes(params, item); ackGetTransferredUploadBytesMessage(params, item, ret); } catch (Exception e) { Log.e(TAG, "Application unable to handle getTransferredUploadBytes.", e); throw new RuntimeException(e); } args.recycle(); break; } case MSG_UPDATE_TRANSFERRED_NETWORK_BYTES: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; IJobCallback callback = params.getCallback(); if (callback != null) { try { callback.updateTransferredNetworkBytes(params.getJobId(), (JobWorkItem) args.arg2, args.argl1, args.argl2); } catch (RemoteException e) { Log.e(TAG, "Error updating data transfer progress to system:" + " binder has gone away."); } } else { Log.e(TAG, "updateDataTransferProgress() called for a nonexistent job id."); } args.recycle(); break; } case MSG_UPDATE_ESTIMATED_NETWORK_BYTES: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; IJobCallback callback = params.getCallback(); if (callback != null) { try { callback.updateEstimatedNetworkBytes(params.getJobId(), (JobWorkItem) args.arg2, args.argl1, args.argl2); } catch (RemoteException e) { Log.e(TAG, "Error updating estimated transfer size to system:" + " binder has gone away."); } } else { Log.e(TAG, "updateEstimatedNetworkBytes() called for a nonexistent job id."); } args.recycle(); break; } default: Log.e(TAG, "Unrecognised message received."); break; } } private void ackGetTransferredDownloadBytesMessage(@NonNull JobParameters params, @Nullable JobWorkItem item, long progress) { final IJobCallback callback = params.getCallback(); final int jobId = params.getJobId(); final int workId = item == null ? -1 : item.getWorkId(); if (callback != null) { try { callback.acknowledgeGetTransferredDownloadBytesMessage(jobId, workId, progress); } catch (RemoteException e) { Log.e(TAG, "System unreachable for returning progress."); } } else if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Attempting to ack a job that has already been processed."); } } private void ackGetTransferredUploadBytesMessage(@NonNull JobParameters params, @Nullable JobWorkItem item, long progress) { final IJobCallback callback = params.getCallback(); final int jobId = params.getJobId(); final int workId = item == null ? -1 : item.getWorkId(); if (callback != null) { try { callback.acknowledgeGetTransferredUploadBytesMessage(jobId, workId, progress); } catch (RemoteException e) { Log.e(TAG, "System unreachable for returning progress."); } } else if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Attempting to ack a job that has already been processed."); } } private void ackStartMessage(JobParameters params, boolean workOngoing) { final IJobCallback callback = params.getCallback(); final int jobId = params.getJobId(); Loading Loading @@ -213,4 +363,69 @@ public abstract class JobServiceEngine { m.arg2 = needsReschedule ? 1 : 0; m.sendToTarget(); } /** * Engine's request to get how much data has been downloaded. * * @see JobService#getTransferredDownloadBytes() */ @BytesLong public long getTransferredDownloadBytes(@NonNull JobParameters params, @Nullable JobWorkItem item) { if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Engine's request to get how much data has been uploaded. * * @see JobService#getTransferredUploadBytes() */ @BytesLong public long getTransferredUploadBytes(@NonNull JobParameters params, @Nullable JobWorkItem item) { if (Compatibility.isChangeEnabled(THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION)) { throw new RuntimeException("Not implemented. Must override in a subclass."); } return 0; } /** * Call in to engine to report data transfer progress. * * @see JobService#updateTransferredNetworkBytes(JobParameters, long, long) */ public void updateTransferredNetworkBytes(@NonNull JobParameters params, @Nullable JobWorkItem item, long downloadBytes, long uploadBytes) { if (params == null) { throw new NullPointerException("params"); } SomeArgs args = SomeArgs.obtain(); args.arg1 = params; args.arg2 = item; args.argl1 = downloadBytes; args.argl2 = uploadBytes; mHandler.obtainMessage(MSG_UPDATE_TRANSFERRED_NETWORK_BYTES, args).sendToTarget(); } /** * Call in to engine to report data transfer progress. * * @see JobService#updateEstimatedNetworkBytes(JobParameters, JobWorkItem, long, long) */ public void updateEstimatedNetworkBytes(@NonNull JobParameters params, @NonNull JobWorkItem item, @BytesLong long downloadBytes, @BytesLong long uploadBytes) { if (params == null) { throw new NullPointerException("params"); } SomeArgs args = SomeArgs.obtain(); args.arg1 = params; args.arg2 = item; args.argl1 = downloadBytes; args.argl2 = uploadBytes; mHandler.obtainMessage(MSG_UPDATE_ESTIMATED_NETWORK_BYTES, args).sendToTarget(); } } No newline at end of file