summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Kweku Adams <kwekua@google.com> 2022-10-24 21:58:16 +0000
committer Kweku Adams <kwekua@google.com> 2022-11-10 18:03:39 +0000
commit7a5d66b557b9e8f930b4d38a0c46dab8725fb628 (patch)
tree53d7858f3f4a20f2b40a8ff6f4627e2739ef847f
parent2d4746d1aad3582470c3a5d71248e28ea59afc51 (diff)
Add data transfer JobService APIs.
Add new APIs to support the data transfer job project. These APIs allow the app to update JobScheduler on estimated transfer sizes and the progress of the transfer. Bug: 255371817 Test: Android builds Change-Id: I70c1020d8a8b0956aaff5e2a843cc338bdbe6bf5
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl39
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/IJobService.aidl5
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobScheduler.java13
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobService.java185
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java225
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java45
-rw-r--r--core/api/current.txt12
7 files changed, 519 insertions, 5 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
index d281da037fde..a3390b75b8bf 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
@@ -30,6 +30,25 @@ import android.app.job.JobWorkItem;
*/
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.
*
* @param jobId Unique integer used to identify this job.
@@ -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);
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobService.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobService.aidl
index 22ad252b9639..2bb82bd006de 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobService.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobService.aidl
@@ -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
@@ -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);
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index dfdb29091ad9..74486861956e 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -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;
@@ -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,
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index d184d44239ed..dabf7282a9e0 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -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;
@@ -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();
@@ -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;
+ }
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
index 3d43d20e7955..6c4b6863ae9e 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
@@ -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;
@@ -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;
/**
@@ -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;
@@ -68,6 +90,32 @@ public abstract class JobServiceEngine {
}
@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();
if (service != null) {
@@ -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);
@@ -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);
@@ -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) {
@@ -132,19 +184,117 @@ 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();
if (callback != null) {
try {
callback.acknowledgeStartMessage(jobId, workOngoing);
- } catch(RemoteException e) {
+ } catch (RemoteException e) {
Log.e(TAG, "System unreachable for starting job.");
}
} else {
@@ -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
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 7c61a354016f..1615122ea2b4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -21,6 +21,7 @@ import static android.app.job.JobInfo.getPriorityString;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.IJobCallback;
@@ -187,6 +188,18 @@ public final class JobServiceContext implements ServiceConnection {
public long mStoppedTime;
@Override
+ public void acknowledgeGetTransferredDownloadBytesMessage(int jobId, int workId,
+ @BytesLong long transferredBytes) {
+ doAcknowledgeGetTransferredDownloadBytesMessage(this, jobId, workId, transferredBytes);
+ }
+
+ @Override
+ public void acknowledgeGetTransferredUploadBytesMessage(int jobId, int workId,
+ @BytesLong long transferredBytes) {
+ doAcknowledgeGetTransferredUploadBytesMessage(this, jobId, workId, transferredBytes);
+ }
+
+ @Override
public void acknowledgeStartMessage(int jobId, boolean ongoing) {
doAcknowledgeStartMessage(this, jobId, ongoing);
}
@@ -210,6 +223,18 @@ public final class JobServiceContext implements ServiceConnection {
public void jobFinished(int jobId, boolean reschedule) {
doJobFinished(this, jobId, reschedule);
}
+
+ @Override
+ public void updateEstimatedNetworkBytes(int jobId, JobWorkItem item,
+ long downloadBytes, long uploadBytes) {
+ doUpdateEstimatedNetworkBytes(this, jobId, item, downloadBytes, uploadBytes);
+ }
+
+ @Override
+ public void updateTransferredNetworkBytes(int jobId, JobWorkItem item,
+ long downloadBytes, long uploadBytes) {
+ doUpdateTransferredNetworkBytes(this, jobId, item, downloadBytes, uploadBytes);
+ }
}
JobServiceContext(JobSchedulerService service, JobConcurrencyManager concurrencyManager,
@@ -497,6 +522,16 @@ public final class JobServiceContext implements ServiceConnection {
}
}
+ private void doAcknowledgeGetTransferredDownloadBytesMessage(JobCallback jobCallback, int jobId,
+ int workId, @BytesLong long transferredBytes) {
+ // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
+ }
+
+ private void doAcknowledgeGetTransferredUploadBytesMessage(JobCallback jobCallback, int jobId,
+ int workId, @BytesLong long transferredBytes) {
+ // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
+ }
+
void doAcknowledgeStopMessage(JobCallback cb, int jobId, boolean reschedule) {
doCallback(cb, reschedule, null);
}
@@ -549,6 +584,16 @@ public final class JobServiceContext implements ServiceConnection {
}
}
+ private void doUpdateTransferredNetworkBytes(JobCallback jobCallback, int jobId,
+ @Nullable JobWorkItem item, long downloadBytes, long uploadBytes) {
+ // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
+ }
+
+ private void doUpdateEstimatedNetworkBytes(JobCallback jobCallback, int jobId,
+ @Nullable JobWorkItem item, long downloadBytes, long uploadBytes) {
+ // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
+ }
+
/**
* We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
* we intend to send to the client - we stop sending work when the service is unbound so until
diff --git a/core/api/current.txt b/core/api/current.txt
index 05c9012f4613..b4a9d3f3635e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8448,19 +8448,31 @@ package android.app.job {
public abstract class JobService extends android.app.Service {
ctor public JobService();
+ method public long getTransferredDownloadBytes();
+ method public long getTransferredDownloadBytes(@NonNull android.app.job.JobWorkItem);
+ method public long getTransferredUploadBytes();
+ method public long getTransferredUploadBytes(@NonNull android.app.job.JobWorkItem);
method public final void jobFinished(android.app.job.JobParameters, boolean);
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract boolean onStartJob(android.app.job.JobParameters);
method public abstract boolean onStopJob(android.app.job.JobParameters);
+ method public final void updateEstimatedNetworkBytes(@NonNull android.app.job.JobParameters, long, long);
+ method public final void updateEstimatedNetworkBytes(@NonNull android.app.job.JobParameters, @NonNull android.app.job.JobWorkItem, long, long);
+ method public final void updateTransferredNetworkBytes(@NonNull android.app.job.JobParameters, long, long);
+ method public final void updateTransferredNetworkBytes(@NonNull android.app.job.JobParameters, @NonNull android.app.job.JobWorkItem, long, long);
field public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE";
}
public abstract class JobServiceEngine {
ctor public JobServiceEngine(android.app.Service);
method public final android.os.IBinder getBinder();
+ method public long getTransferredDownloadBytes(@NonNull android.app.job.JobParameters, @Nullable android.app.job.JobWorkItem);
+ method public long getTransferredUploadBytes(@NonNull android.app.job.JobParameters, @Nullable android.app.job.JobWorkItem);
method public void jobFinished(android.app.job.JobParameters, boolean);
method public abstract boolean onStartJob(android.app.job.JobParameters);
method public abstract boolean onStopJob(android.app.job.JobParameters);
+ method public void updateEstimatedNetworkBytes(@NonNull android.app.job.JobParameters, @NonNull android.app.job.JobWorkItem, long, long);
+ method public void updateTransferredNetworkBytes(@NonNull android.app.job.JobParameters, @Nullable android.app.job.JobWorkItem, long, long);
}
public final class JobWorkItem implements android.os.Parcelable {