Loading apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl +14 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app.job; import android.app.Notification; import android.app.job.JobWorkItem; /** Loading Loading @@ -104,4 +105,17 @@ interface IJobCallback { */ void updateTransferredNetworkBytes(int jobId, in JobWorkItem item, long transferredDownloadBytes, long transferredUploadBytes); /** * Provide JobScheduler with a notification to post and tie to this job's * lifecycle. * This is required for all user-initiated job and optional for other jobs. * * @param jobId Unique integer used to identify this job. * @param notificationId The ID for this notification, as per * {@link android.app.NotificationManager#notify(int, Notification)}. * @param notification The notification to be displayed. * @param jobEndNotificationPolicy The policy to apply to the notification when the job stops. */ void setNotification(int jobId, int notificationId, in Notification notification, int jobEndNotificationPolicy); } apex/jobscheduler/framework/java/android/app/job/JobService.java +64 −0 Original line number Diff line number Diff line Loading @@ -19,13 +19,18 @@ package android.app.job; import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION; import android.annotation.BytesLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.Service; import android.compat.Compatibility; import android.content.Intent; import android.os.IBinder; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p> * <p>This is the base class that handles asynchronous requests that were previously scheduled. You Loading Loading @@ -63,6 +68,32 @@ public abstract class JobService extends Service { public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE"; /** * Detach the notification supplied to * {@link #setNotification(JobParameters, int, Notification, int)} when the job ends. * The notification will remain shown even after JobScheduler stops the job. * * @hide */ public static final int JOB_END_NOTIFICATION_POLICY_DETACH = 0; /** * Cancel and remove the notification supplied to * {@link #setNotification(JobParameters, int, Notification, int)} when the job ends. * The notification will be removed from the notification shade. * * @hide */ public static final int JOB_END_NOTIFICATION_POLICY_REMOVE = 1; /** @hide */ @IntDef(prefix = {"JOB_END_NOTIFICATION_POLICY_"}, value = { JOB_END_NOTIFICATION_POLICY_DETACH, JOB_END_NOTIFICATION_POLICY_REMOVE, }) @Retention(RetentionPolicy.SOURCE) public @interface JobEndNotificationPolicy { } private JobServiceEngine mEngine; /** @hide */ Loading Loading @@ -364,4 +395,37 @@ public abstract class JobService extends Service { } return 0; } /** * Provide JobScheduler with a notification to post and tie to this job's lifecycle. * This is required for all user-initiated jobs * (scheduled via {link JobInfo.Builder#setUserInitiated(boolean)}) and optional for * other jobs. If the app does not call this method for a required notification within * 10 seconds after {@link #onStartJob(JobParameters)} is called, * the system will trigger an ANR and stop this job. * * <p> * Note that certain types of jobs * (e.g. {@link JobInfo.Builder#setDataTransfer data transfer jobs}) may require the * notification to have certain characteristics and their documentation will state * any such requirements. * * <p> * JobScheduler will not remember this notification after the job has finished running, * so apps must call this every time the job is started (if required or desired). * * @param params The parameters identifying this job, as supplied to * the job in the {@link #onStartJob(JobParameters)} callback. * @param notificationId The ID for this notification, as per * {@link android.app.NotificationManager#notify(int, * Notification)}. * @param notification The notification to be displayed. * @param jobEndNotificationPolicy The policy to apply to the notification when the job stops. * @hide */ public final void setNotification(@NonNull JobParameters params, int notificationId, @NonNull Notification notification, @JobEndNotificationPolicy int jobEndNotificationPolicy) { mEngine.setNotification(params, notificationId, notification, jobEndNotificationPolicy); } } apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java +45 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEM import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.Service; import android.compat.Compatibility; import android.content.Intent; Loading Loading @@ -73,6 +74,8 @@ public abstract class JobServiceEngine { 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; /** Message that the client wants to give JobScheduler a notification to tie to the job. */ private static final int MSG_SET_NOTIFICATION = 7; private final IJobService mBinder; Loading Loading @@ -250,6 +253,24 @@ public abstract class JobServiceEngine { args.recycle(); break; } case MSG_SET_NOTIFICATION: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; final Notification notification = (Notification) args.arg2; IJobCallback callback = params.getCallback(); if (callback != null) { try { callback.setNotification(params.getJobId(), args.argi1, notification, args.argi2); } catch (RemoteException e) { Log.e(TAG, "Error providing notification: binder has gone away."); } } else { Log.e(TAG, "setNotification() called for a nonexistent job."); } args.recycle(); break; } default: Log.e(TAG, "Unrecognised message received."); break; Loading Loading @@ -432,4 +453,27 @@ public abstract class JobServiceEngine { args.argl2 = uploadBytes; mHandler.obtainMessage(MSG_UPDATE_ESTIMATED_NETWORK_BYTES, args).sendToTarget(); } /** * Give JobScheduler a notification to tie to this job's lifecycle. * * @hide * @see JobService#setNotification(JobParameters, int, Notification, int) */ public void setNotification(@NonNull JobParameters params, int notificationId, @NonNull Notification notification, @JobService.JobEndNotificationPolicy int jobEndNotificationPolicy) { if (params == null) { throw new NullPointerException("params"); } if (notification == null) { throw new NullPointerException("notification"); } SomeArgs args = SomeArgs.obtain(); args.arg1 = params; args.arg2 = notification; args.argi1 = notificationId; args.argi2 = jobEndNotificationPolicy; mHandler.obtainMessage(MSG_SET_NOTIFICATION, args).sendToTarget(); } } apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +69 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.server.job; import static android.app.job.JobInfo.getPriorityString; import static android.app.job.JobService.JOB_END_NOTIFICATION_POLICY_DETACH; import static android.app.job.JobService.JOB_END_NOTIFICATION_POLICY_REMOVE; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; Loading @@ -24,6 +26,7 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.job.IJobCallback; import android.app.job.IJobService; import android.app.job.JobInfo; Loading Loading @@ -58,6 +61,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; import com.android.server.notification.NotificationManagerInternal; import com.android.server.tare.EconomicPolicy; import com.android.server.tare.EconomyManagerInternal; import com.android.server.tare.JobSchedulerEconomicPolicy; Loading Loading @@ -117,6 +121,7 @@ public final class JobServiceContext implements ServiceConnection { private final EconomyManagerInternal mEconomyManagerInternal; private final JobPackageTracker mJobPackageTracker; private final PowerManager mPowerManager; private final NotificationManagerInternal mNotificationManagerInternal; private PowerManager.WakeLock mWakeLock; // Execution state. Loading Loading @@ -169,6 +174,11 @@ public final class JobServiceContext implements ServiceConnection { /** The absolute maximum amount of time the job can run */ private long mMaxExecutionTimeMillis; private int mNotificationId; private Notification mNotification; private int mNotificationPid; private int mNotificationJobStopPolicy; /** * The stop reason for a pending cancel. If there's not pending cancel, then the value should be * {@link JobParameters#STOP_REASON_UNDEFINED}. Loading Loading @@ -235,6 +245,12 @@ public final class JobServiceContext implements ServiceConnection { long downloadBytes, long uploadBytes) { doUpdateTransferredNetworkBytes(this, jobId, item, downloadBytes, uploadBytes); } @Override public void setNotification(int jobId, int notificationId, Notification notification, int jobEndNotificationPolicy) { doSetNotification(this, jobId, notificationId, notification, jobEndNotificationPolicy); } } JobServiceContext(JobSchedulerService service, JobConcurrencyManager concurrencyManager, Loading @@ -244,6 +260,7 @@ public final class JobServiceContext implements ServiceConnection { mService = service; mBatteryStats = batteryStats; mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); mNotificationManagerInternal = LocalServices.getService(NotificationManagerInternal.class); mJobPackageTracker = tracker; mCallbackHandler = new JobServiceHandler(looper); mJobConcurrencyManager = concurrencyManager; Loading Loading @@ -593,6 +610,49 @@ public final class JobServiceContext implements ServiceConnection { } } private void doSetNotification(JobCallback cb, int jodId, int notificationId, Notification notification, int jobEndNotificationPolicy) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { if (!verifyCallerLocked(cb)) { return; } if (callingUid != mRunningJob.getUid()) { Slog.wtfStack(TAG, "Calling UID isn't the same as running job's UID..."); throw new SecurityException("Can't post notification on behalf of another app"); } if (notification == null) { throw new NullPointerException("notification"); } if (notification.getSmallIcon() == null) { throw new IllegalArgumentException("small icon required"); } final String callingPkgName = mRunningJob.getServiceComponent().getPackageName(); if (null == mNotificationManagerInternal.getNotificationChannel( callingPkgName, callingUid, notification.getChannelId())) { throw new IllegalArgumentException("invalid notification channel"); } if (jobEndNotificationPolicy != JOB_END_NOTIFICATION_POLICY_DETACH && jobEndNotificationPolicy != JOB_END_NOTIFICATION_POLICY_REMOVE) { throw new IllegalArgumentException("invalid job end notification policy"); } // TODO(260848384): ensure apps can't cancel the notification for user-initiated job mNotificationManagerInternal.enqueueNotification( callingPkgName, callingPkgName, callingUid, callingPid, /* tag */ null, notificationId, notification, UserHandle.getUserId(callingUid)); mNotificationId = notificationId; mNotification = notification; mNotificationPid = callingPid; mNotificationJobStopPolicy = jobEndNotificationPolicy; } } finally { Binder.restoreCallingIdentity(ident); } } 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 Loading Loading @@ -1116,6 +1176,13 @@ public final class JobServiceContext implements ServiceConnection { JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, String.valueOf(mRunningJob.getJobId())); } if (mNotification != null && mNotificationJobStopPolicy == JOB_END_NOTIFICATION_POLICY_REMOVE) { final String callingPkgName = completedJob.getServiceComponent().getPackageName(); mNotificationManagerInternal.cancelNotification( callingPkgName, callingPkgName, completedJob.getUid(), mNotificationPid, /* tag */ null, mNotificationId, UserHandle.getUserId(completedJob.getUid())); } if (mWakeLock != null) { mWakeLock.release(); } Loading @@ -1133,6 +1200,7 @@ public final class JobServiceContext implements ServiceConnection { mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; mPendingInternalStopReason = 0; mPendingDebugStopReason = null; mNotification = null; removeOpTimeOutLocked(); mCompletedListener.onJobCompletedLocked(completedJob, internalStopReason, reschedule); mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType); Loading @@ -1157,6 +1225,7 @@ public final class JobServiceContext implements ServiceConnection { private void scheduleOpTimeOutLocked() { removeOpTimeOutLocked(); // TODO(260848384): enforce setNotification timeout for user-initiated jobs final long timeoutMillis; switch (mVerb) { case VERB_EXECUTING: Loading Loading
apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl +14 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app.job; import android.app.Notification; import android.app.job.JobWorkItem; /** Loading Loading @@ -104,4 +105,17 @@ interface IJobCallback { */ void updateTransferredNetworkBytes(int jobId, in JobWorkItem item, long transferredDownloadBytes, long transferredUploadBytes); /** * Provide JobScheduler with a notification to post and tie to this job's * lifecycle. * This is required for all user-initiated job and optional for other jobs. * * @param jobId Unique integer used to identify this job. * @param notificationId The ID for this notification, as per * {@link android.app.NotificationManager#notify(int, Notification)}. * @param notification The notification to be displayed. * @param jobEndNotificationPolicy The policy to apply to the notification when the job stops. */ void setNotification(int jobId, int notificationId, in Notification notification, int jobEndNotificationPolicy); }
apex/jobscheduler/framework/java/android/app/job/JobService.java +64 −0 Original line number Diff line number Diff line Loading @@ -19,13 +19,18 @@ package android.app.job; import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEMENTATION; import android.annotation.BytesLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.Service; import android.compat.Compatibility; import android.content.Intent; import android.os.IBinder; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p> * <p>This is the base class that handles asynchronous requests that were previously scheduled. You Loading Loading @@ -63,6 +68,32 @@ public abstract class JobService extends Service { public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE"; /** * Detach the notification supplied to * {@link #setNotification(JobParameters, int, Notification, int)} when the job ends. * The notification will remain shown even after JobScheduler stops the job. * * @hide */ public static final int JOB_END_NOTIFICATION_POLICY_DETACH = 0; /** * Cancel and remove the notification supplied to * {@link #setNotification(JobParameters, int, Notification, int)} when the job ends. * The notification will be removed from the notification shade. * * @hide */ public static final int JOB_END_NOTIFICATION_POLICY_REMOVE = 1; /** @hide */ @IntDef(prefix = {"JOB_END_NOTIFICATION_POLICY_"}, value = { JOB_END_NOTIFICATION_POLICY_DETACH, JOB_END_NOTIFICATION_POLICY_REMOVE, }) @Retention(RetentionPolicy.SOURCE) public @interface JobEndNotificationPolicy { } private JobServiceEngine mEngine; /** @hide */ Loading Loading @@ -364,4 +395,37 @@ public abstract class JobService extends Service { } return 0; } /** * Provide JobScheduler with a notification to post and tie to this job's lifecycle. * This is required for all user-initiated jobs * (scheduled via {link JobInfo.Builder#setUserInitiated(boolean)}) and optional for * other jobs. If the app does not call this method for a required notification within * 10 seconds after {@link #onStartJob(JobParameters)} is called, * the system will trigger an ANR and stop this job. * * <p> * Note that certain types of jobs * (e.g. {@link JobInfo.Builder#setDataTransfer data transfer jobs}) may require the * notification to have certain characteristics and their documentation will state * any such requirements. * * <p> * JobScheduler will not remember this notification after the job has finished running, * so apps must call this every time the job is started (if required or desired). * * @param params The parameters identifying this job, as supplied to * the job in the {@link #onStartJob(JobParameters)} callback. * @param notificationId The ID for this notification, as per * {@link android.app.NotificationManager#notify(int, * Notification)}. * @param notification The notification to be displayed. * @param jobEndNotificationPolicy The policy to apply to the notification when the job stops. * @hide */ public final void setNotification(@NonNull JobParameters params, int notificationId, @NonNull Notification notification, @JobEndNotificationPolicy int jobEndNotificationPolicy) { mEngine.setNotification(params, notificationId, notification, jobEndNotificationPolicy); } }
apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java +45 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.job.JobScheduler.THROW_ON_INVALID_DATA_TRANSFER_IMPLEM import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.Service; import android.compat.Compatibility; import android.content.Intent; Loading Loading @@ -73,6 +74,8 @@ public abstract class JobServiceEngine { 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; /** Message that the client wants to give JobScheduler a notification to tie to the job. */ private static final int MSG_SET_NOTIFICATION = 7; private final IJobService mBinder; Loading Loading @@ -250,6 +253,24 @@ public abstract class JobServiceEngine { args.recycle(); break; } case MSG_SET_NOTIFICATION: { final SomeArgs args = (SomeArgs) msg.obj; final JobParameters params = (JobParameters) args.arg1; final Notification notification = (Notification) args.arg2; IJobCallback callback = params.getCallback(); if (callback != null) { try { callback.setNotification(params.getJobId(), args.argi1, notification, args.argi2); } catch (RemoteException e) { Log.e(TAG, "Error providing notification: binder has gone away."); } } else { Log.e(TAG, "setNotification() called for a nonexistent job."); } args.recycle(); break; } default: Log.e(TAG, "Unrecognised message received."); break; Loading Loading @@ -432,4 +453,27 @@ public abstract class JobServiceEngine { args.argl2 = uploadBytes; mHandler.obtainMessage(MSG_UPDATE_ESTIMATED_NETWORK_BYTES, args).sendToTarget(); } /** * Give JobScheduler a notification to tie to this job's lifecycle. * * @hide * @see JobService#setNotification(JobParameters, int, Notification, int) */ public void setNotification(@NonNull JobParameters params, int notificationId, @NonNull Notification notification, @JobService.JobEndNotificationPolicy int jobEndNotificationPolicy) { if (params == null) { throw new NullPointerException("params"); } if (notification == null) { throw new NullPointerException("notification"); } SomeArgs args = SomeArgs.obtain(); args.arg1 = params; args.arg2 = notification; args.argi1 = notificationId; args.argi2 = jobEndNotificationPolicy; mHandler.obtainMessage(MSG_SET_NOTIFICATION, args).sendToTarget(); } }
apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +69 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.server.job; import static android.app.job.JobInfo.getPriorityString; import static android.app.job.JobService.JOB_END_NOTIFICATION_POLICY_DETACH; import static android.app.job.JobService.JOB_END_NOTIFICATION_POLICY_REMOVE; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; Loading @@ -24,6 +26,7 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.BytesLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.job.IJobCallback; import android.app.job.IJobService; import android.app.job.JobInfo; Loading Loading @@ -58,6 +61,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; import com.android.server.notification.NotificationManagerInternal; import com.android.server.tare.EconomicPolicy; import com.android.server.tare.EconomyManagerInternal; import com.android.server.tare.JobSchedulerEconomicPolicy; Loading Loading @@ -117,6 +121,7 @@ public final class JobServiceContext implements ServiceConnection { private final EconomyManagerInternal mEconomyManagerInternal; private final JobPackageTracker mJobPackageTracker; private final PowerManager mPowerManager; private final NotificationManagerInternal mNotificationManagerInternal; private PowerManager.WakeLock mWakeLock; // Execution state. Loading Loading @@ -169,6 +174,11 @@ public final class JobServiceContext implements ServiceConnection { /** The absolute maximum amount of time the job can run */ private long mMaxExecutionTimeMillis; private int mNotificationId; private Notification mNotification; private int mNotificationPid; private int mNotificationJobStopPolicy; /** * The stop reason for a pending cancel. If there's not pending cancel, then the value should be * {@link JobParameters#STOP_REASON_UNDEFINED}. Loading Loading @@ -235,6 +245,12 @@ public final class JobServiceContext implements ServiceConnection { long downloadBytes, long uploadBytes) { doUpdateTransferredNetworkBytes(this, jobId, item, downloadBytes, uploadBytes); } @Override public void setNotification(int jobId, int notificationId, Notification notification, int jobEndNotificationPolicy) { doSetNotification(this, jobId, notificationId, notification, jobEndNotificationPolicy); } } JobServiceContext(JobSchedulerService service, JobConcurrencyManager concurrencyManager, Loading @@ -244,6 +260,7 @@ public final class JobServiceContext implements ServiceConnection { mService = service; mBatteryStats = batteryStats; mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); mNotificationManagerInternal = LocalServices.getService(NotificationManagerInternal.class); mJobPackageTracker = tracker; mCallbackHandler = new JobServiceHandler(looper); mJobConcurrencyManager = concurrencyManager; Loading Loading @@ -593,6 +610,49 @@ public final class JobServiceContext implements ServiceConnection { } } private void doSetNotification(JobCallback cb, int jodId, int notificationId, Notification notification, int jobEndNotificationPolicy) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { if (!verifyCallerLocked(cb)) { return; } if (callingUid != mRunningJob.getUid()) { Slog.wtfStack(TAG, "Calling UID isn't the same as running job's UID..."); throw new SecurityException("Can't post notification on behalf of another app"); } if (notification == null) { throw new NullPointerException("notification"); } if (notification.getSmallIcon() == null) { throw new IllegalArgumentException("small icon required"); } final String callingPkgName = mRunningJob.getServiceComponent().getPackageName(); if (null == mNotificationManagerInternal.getNotificationChannel( callingPkgName, callingUid, notification.getChannelId())) { throw new IllegalArgumentException("invalid notification channel"); } if (jobEndNotificationPolicy != JOB_END_NOTIFICATION_POLICY_DETACH && jobEndNotificationPolicy != JOB_END_NOTIFICATION_POLICY_REMOVE) { throw new IllegalArgumentException("invalid job end notification policy"); } // TODO(260848384): ensure apps can't cancel the notification for user-initiated job mNotificationManagerInternal.enqueueNotification( callingPkgName, callingPkgName, callingUid, callingPid, /* tag */ null, notificationId, notification, UserHandle.getUserId(callingUid)); mNotificationId = notificationId; mNotification = notification; mNotificationPid = callingPid; mNotificationJobStopPolicy = jobEndNotificationPolicy; } } finally { Binder.restoreCallingIdentity(ident); } } 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 Loading Loading @@ -1116,6 +1176,13 @@ public final class JobServiceContext implements ServiceConnection { JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, String.valueOf(mRunningJob.getJobId())); } if (mNotification != null && mNotificationJobStopPolicy == JOB_END_NOTIFICATION_POLICY_REMOVE) { final String callingPkgName = completedJob.getServiceComponent().getPackageName(); mNotificationManagerInternal.cancelNotification( callingPkgName, callingPkgName, completedJob.getUid(), mNotificationPid, /* tag */ null, mNotificationId, UserHandle.getUserId(completedJob.getUid())); } if (mWakeLock != null) { mWakeLock.release(); } Loading @@ -1133,6 +1200,7 @@ public final class JobServiceContext implements ServiceConnection { mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; mPendingInternalStopReason = 0; mPendingDebugStopReason = null; mNotification = null; removeOpTimeOutLocked(); mCompletedListener.onJobCompletedLocked(completedJob, internalStopReason, reschedule); mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType); Loading @@ -1157,6 +1225,7 @@ public final class JobServiceContext implements ServiceConnection { private void scheduleOpTimeOutLocked() { removeOpTimeOutLocked(); // TODO(260848384): enforce setNotification timeout for user-initiated jobs final long timeoutMillis; switch (mVerb) { case VERB_EXECUTING: Loading