Add express logs for several JobScheduler events.

Add express logs to track:
1. How apps interact with the updateEstimatedNetworkBytes() API
2. How apps interact with the updateTransferredNetworkBytes() API
3. How apps interact with the setNotification() API
4. How apps interact with the setEstimatedNetworkBytes() API
5. How apps interact with the setMinimumChunkBytes() API
6. How often apps hit the max scheduled job limit
7. How often apps are too slow in responding to JobService API calls
8. How many times apps crash (or otherwise unexpectedly lose their
   connection) while running a JobService
9. How many JobWorkItems apps enqueue at a time
10. Job execution concurrency

Bug: 138239687
Bug: 279935506
Test: atest CtsJobSchedulerTestCases:ConnectivityConstraintTest
Test: atest CtsJobSchedulerTestCases:JobSchedulingTest
Test: atest CtsJobSchedulerTestCases:NotificationTest
Test: atest FrameworksMockingServicesTests:JobNotificationCoordinatorTest
Change-Id: I153eef1120fcdbf64c241707287233c422fa66db
This commit is contained in:
Kweku Adams
2023-04-28 20:40:07 +00:00
parent 0f59388c3a
commit 72a334cae0
6 changed files with 241 additions and 26 deletions

View File

@@ -62,6 +62,7 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.StatLogger;
import com.android.modules.expresslog.Histogram;
import com.android.server.AppSchedulingModuleThread;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
@@ -471,6 +472,13 @@ class JobConcurrencyManager {
private final Consumer<PackageStats> mPackageStatsStagingCountClearer =
PackageStats::resetStagedCount;
private static final Histogram sConcurrencyHistogramLogger = new Histogram(
"job_scheduler.value_hist_job_concurrency",
// Create a histogram that expects values in the range [0, 99].
// Include more buckets than MAX_CONCURRENCY_LIMIT to account for/identify the cases
// where we may create additional slots for TOP-started EJs and UIJs
new Histogram.UniformOptions(100, 0, 99));
private final StatLogger mStatLogger = new StatLogger(new String[]{
"assignJobsToContexts",
"refreshSystemState",
@@ -1433,6 +1441,7 @@ class JobConcurrencyManager {
mService.mJobPackageTracker.noteConcurrency(mRunningJobs.size(),
// TODO: log per type instead of only TOP
mWorkCountTracker.getRunningJobCount(WORK_TYPE_TOP));
sConcurrencyHistogramLogger.logSample(mActiveServices.size());
}
@GuardedBy("mLock")

View File

@@ -33,6 +33,7 @@ import android.util.SparseArrayMap;
import android.util.SparseSetArray;
import com.android.internal.annotations.GuardedBy;
import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
import com.android.server.notification.NotificationManagerInternal;
@@ -114,11 +115,39 @@ class JobNotificationCoordinator {
@JobService.JobEndNotificationPolicy int jobEndNotificationPolicy) {
validateNotification(packageName, callingUid, notification, jobEndNotificationPolicy);
final JobStatus jobStatus = hostingContext.getRunningJobLocked();
if (jobStatus == null) {
Slog.wtfStack(TAG, "enqueueNotification called with no running job");
return;
}
final NotificationDetails oldDetails = mNotificationDetails.get(hostingContext);
if (oldDetails != null && oldDetails.notificationId != notificationId) {
// App is switching notification IDs. Remove association with the old one.
removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED,
jobStatus);
if (oldDetails == null) {
if (jobStatus.startedAsUserInitiatedJob) {
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_initial_setNotification_call_required",
jobStatus.getUid());
} else {
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_initial_setNotification_call_optional",
jobStatus.getUid());
}
} else {
if (jobStatus.startedAsUserInitiatedJob) {
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_subsequent_setNotification_call_required",
jobStatus.getUid());
} else {
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_subsequent_setNotification_call_optional",
jobStatus.getUid());
}
if (oldDetails.notificationId != notificationId) {
// App is switching notification IDs. Remove association with the old one.
removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED,
jobStatus);
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_setNotification_changed_notification_ids",
jobStatus.getUid());
}
}
final int userId = UserHandle.getUserId(callingUid);
if (jobStatus != null && jobStatus.startedAsUserInitiatedJob) {

View File

@@ -106,6 +106,8 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.modules.expresslog.Counter;
import com.android.modules.expresslog.Histogram;
import com.android.server.AppSchedulingModuleThread;
import com.android.server.AppStateTracker;
import com.android.server.AppStateTrackerImpl;
@@ -378,6 +380,28 @@ public class JobSchedulerService extends com.android.server.SystemService
private final long[] mLastCancelledJobTimeElapsed =
new long[DEBUG ? NUM_COMPLETED_JOB_HISTORY : 0];
private static final Histogram sEnqueuedJwiHighWaterMarkLogger = new Histogram(
"job_scheduler.value_hist_w_uid_enqueued_work_items_high_water_mark",
new Histogram.ScaledRangeOptions(25, 0, 5, 1.4f));
private static final Histogram sInitialJobEstimatedNetworkDownloadKBLogger = new Histogram(
"job_scheduler.value_hist_initial_job_estimated_network_download_kilobytes",
new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
private static final Histogram sInitialJwiEstimatedNetworkDownloadKBLogger = new Histogram(
"job_scheduler.value_hist_initial_jwi_estimated_network_download_kilobytes",
new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
private static final Histogram sInitialJobEstimatedNetworkUploadKBLogger = new Histogram(
"job_scheduler.value_hist_initial_job_estimated_network_upload_kilobytes",
new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
private static final Histogram sInitialJwiEstimatedNetworkUploadKBLogger = new Histogram(
"job_scheduler.value_hist_initial_jwi_estimated_network_upload_kilobytes",
new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
private static final Histogram sJobMinimumChunkKBLogger = new Histogram(
"job_scheduler.value_hist_w_uid_job_minimum_chunk_kilobytes",
new Histogram.ScaledRangeOptions(25, 0, 5 /* 5 KB */, 1.76f));
private static final Histogram sJwiMinimumChunkKBLogger = new Histogram(
"job_scheduler.value_hist_w_uid_jwi_minimum_chunk_kilobytes",
new Histogram.ScaledRangeOptions(25, 0, 5 /* 5 KB */, 1.76f));
/**
* A mapping of which uids are currently in the foreground to their effective bias.
*/
@@ -1422,6 +1446,32 @@ public class JobSchedulerService extends com.android.server.SystemService
return JobScheduler.RESULT_FAILURE;
}
if (job.getRequiredNetwork() != null) {
sInitialJobEstimatedNetworkDownloadKBLogger.logSample(
safelyScaleBytesToKBForHistogram(
job.getEstimatedNetworkDownloadBytes()));
sInitialJobEstimatedNetworkUploadKBLogger.logSample(
safelyScaleBytesToKBForHistogram(job.getEstimatedNetworkUploadBytes()));
sJobMinimumChunkKBLogger.logSampleWithUid(uId,
safelyScaleBytesToKBForHistogram(job.getMinimumNetworkChunkBytes()));
if (work != null) {
sInitialJwiEstimatedNetworkDownloadKBLogger.logSample(
safelyScaleBytesToKBForHistogram(
work.getEstimatedNetworkDownloadBytes()));
sInitialJwiEstimatedNetworkUploadKBLogger.logSample(
safelyScaleBytesToKBForHistogram(
work.getEstimatedNetworkUploadBytes()));
sJwiMinimumChunkKBLogger.logSampleWithUid(uId,
safelyScaleBytesToKBForHistogram(
work.getMinimumNetworkChunkBytes()));
}
}
if (work != null) {
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_job_work_items_enqueued", uId);
}
synchronized (mLock) {
final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, namespace, job.getId());
@@ -1451,6 +1501,7 @@ public class JobSchedulerService extends com.android.server.SystemService
toCancel.enqueueWorkLocked(work);
mJobs.touchJob(toCancel);
sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, toCancel.getWorkCount());
// If any of work item is enqueued when the source is in the foreground,
// exempt the entire job.
@@ -1483,6 +1534,8 @@ public class JobSchedulerService extends com.android.server.SystemService
if (packageName == null) {
if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
Slog.w(TAG, "Too many jobs for uid " + uId);
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_max_scheduling_limit_hit", uId);
throw new IllegalStateException("Apps may not schedule more than "
+ MAX_JOBS_PER_APP + " distinct jobs");
}
@@ -1522,6 +1575,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (work != null) {
// If work has been supplied, enqueue it into the new job.
jobStatus.enqueueWorkLocked(work);
sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, jobStatus.getWorkCount());
}
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
@@ -3834,6 +3888,18 @@ public class JobSchedulerService extends com.android.server.SystemService
return bucket;
}
static int safelyScaleBytesToKBForHistogram(long bytes) {
long kilobytes = bytes / 1000;
// Anything over Integer.MAX_VALUE or under Integer.MIN_VALUE isn't expected and will
// be put into the overflow buckets.
if (kilobytes > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else if (kilobytes < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
return (int) kilobytes;
}
private class CloudProviderChangeListener implements
StorageManagerInternal.CloudProviderChangeListener {

View File

@@ -20,6 +20,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 static com.android.server.job.JobSchedulerService.safelyScaleBytesToKBForHistogram;
import android.Manifest;
import android.annotation.BytesLong;
@@ -66,6 +67,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.FrameworkStatsLog;
import com.android.modules.expresslog.Counter;
import com.android.modules.expresslog.Histogram;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
@@ -111,6 +114,22 @@ public final class JobServiceContext implements ServiceConnection {
private static final long NOTIFICATION_TIMEOUT_MILLIS = 10_000L * Build.HW_TIMEOUT_MULTIPLIER;
private static final long EXECUTION_DURATION_STAMP_PERIOD_MILLIS = 5 * 60_000L;
private static final Histogram sEnqueuedJwiAtJobStart = new Histogram(
"job_scheduler.value_hist_w_uid_enqueued_work_items_at_job_start",
new Histogram.ScaledRangeOptions(20, 1, 3, 1.4f));
private static final Histogram sTransferredNetworkDownloadKBHighWaterMarkLogger = new Histogram(
"job_scheduler.value_hist_transferred_network_download_kilobytes_high_water_mark",
new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
private static final Histogram sTransferredNetworkUploadKBHighWaterMarkLogger = new Histogram(
"job_scheduler.value_hist_transferred_network_upload_kilobytes_high_water_mark",
new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
private static final Histogram sUpdatedEstimatedNetworkDownloadKBLogger = new Histogram(
"job_scheduler.value_hist_updated_estimated_network_download_kilobytes",
new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
private static final Histogram sUpdatedEstimatedNetworkUploadKBLogger = new Histogram(
"job_scheduler.value_hist_updated_estimated_network_upload_kilobytes",
new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
private static final String[] VERB_STRINGS = {
"VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
};
@@ -479,6 +498,7 @@ public final class JobServiceContext implements ServiceConnection {
job.getEstimatedNetworkUploadBytes(),
job.getWorkCount(),
ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())));
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
final String componentPackage = job.getServiceComponent().getPackageName();
@@ -815,6 +835,41 @@ public final class JobServiceContext implements ServiceConnection {
if (!verifyCallerLocked(cb)) {
return;
}
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_estimated_network_bytes_updated",
mRunningJob.getUid());
sUpdatedEstimatedNetworkDownloadKBLogger.logSample(
safelyScaleBytesToKBForHistogram(downloadBytes));
sUpdatedEstimatedNetworkUploadKBLogger.logSample(
safelyScaleBytesToKBForHistogram(uploadBytes));
if (mEstimatedDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN
&& downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
if (mEstimatedDownloadBytes < downloadBytes) {
Counter.logIncrementWithUid(
"job_scheduler."
+ "value_cntr_w_uid_estimated_network_download_bytes_increased",
mRunningJob.getUid());
} else if (mEstimatedDownloadBytes > downloadBytes) {
Counter.logIncrementWithUid(
"job_scheduler."
+ "value_cntr_w_uid_estimated_network_download_bytes_decreased",
mRunningJob.getUid());
}
}
if (mEstimatedUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN
&& uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
if (mEstimatedUploadBytes < uploadBytes) {
Counter.logIncrementWithUid(
"job_scheduler"
+ ".value_cntr_w_uid_estimated_network_upload_bytes_increased",
mRunningJob.getUid());
} else if (mEstimatedUploadBytes > uploadBytes) {
Counter.logIncrementWithUid(
"job_scheduler"
+ ".value_cntr_w_uid_estimated_network_upload_bytes_decreased",
mRunningJob.getUid());
}
}
mEstimatedDownloadBytes = downloadBytes;
mEstimatedUploadBytes = uploadBytes;
}
@@ -827,6 +882,41 @@ public final class JobServiceContext implements ServiceConnection {
if (!verifyCallerLocked(cb)) {
return;
}
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_transferred_network_bytes_updated",
mRunningJob.getUid());
sTransferredNetworkDownloadKBHighWaterMarkLogger.logSample(
safelyScaleBytesToKBForHistogram(downloadBytes));
sTransferredNetworkUploadKBHighWaterMarkLogger.logSample(
safelyScaleBytesToKBForHistogram(uploadBytes));
if (mTransferredDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN
&& downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
if (mTransferredDownloadBytes < downloadBytes) {
Counter.logIncrementWithUid(
"job_scheduler."
+ "value_cntr_w_uid_transferred_network_download_bytes_increased",
mRunningJob.getUid());
} else if (mTransferredDownloadBytes > downloadBytes) {
Counter.logIncrementWithUid(
"job_scheduler."
+ "value_cntr_w_uid_transferred_network_download_bytes_decreased",
mRunningJob.getUid());
}
}
if (mTransferredUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN
&& uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
if (mTransferredUploadBytes < uploadBytes) {
Counter.logIncrementWithUid(
"job_scheduler."
+ "value_cntr_w_uid_transferred_network_upload_bytes_increased",
mRunningJob.getUid());
} else if (mTransferredUploadBytes > uploadBytes) {
Counter.logIncrementWithUid(
"job_scheduler."
+ "value_cntr_w_uid_transferred_network_upload_bytes_decreased",
mRunningJob.getUid());
}
}
mTransferredDownloadBytes = downloadBytes;
mTransferredUploadBytes = uploadBytes;
}
@@ -897,6 +987,11 @@ public final class JobServiceContext implements ServiceConnection {
// Use that as the stop reason for logging/debugging purposes.
mParams.setStopReason(
mDeathMarkStopReason, mDeathMarkInternalStopReason, mDeathMarkDebugReason);
} else if (mRunningJob != null) {
Counter.logIncrementWithUid(
"job_scheduler.value_cntr_w_uid_unexpected_service_disconnects",
// Use the calling UID since that's the one this context was connected to.
mRunningJob.getUid());
}
closeAndCleanupJobLocked(true /* needsReschedule */, "unexpectedly disconnected");
}
@@ -1223,6 +1318,8 @@ public final class JobServiceContext implements ServiceConnection {
switch (mVerb) {
case VERB_BINDING:
onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true,
/* texCounterMetricId */
"job_scheduler.value_cntr_w_uid_slow_app_response_binding",
/* debugReason */ "timed out while binding",
/* anrMessage */ "Timed out while trying to bind",
CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
@@ -1233,6 +1330,8 @@ public final class JobServiceContext implements ServiceConnection {
// know what happened so let's log it and notify the JobScheduler
// FINISHED/NO-RETRY.
onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true,
/* texCounterMetricId */
"job_scheduler.value_cntr_w_uid_slow_app_response_onStartJob",
/* debugReason */ "timed out while starting",
/* anrMessage */ "No response to onStartJob",
CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
@@ -1243,6 +1342,8 @@ public final class JobServiceContext implements ServiceConnection {
// Don't update the stop reasons since we were already stopping the job for some
// other reason.
onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ false,
/* texCounterMetricId */
"job_scheduler.value_cntr_w_uid_slow_app_response_onStopJob",
/* debugReason */ "timed out while stopping",
/* anrMessage */ "No response to onStopJob",
CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
@@ -1298,6 +1399,8 @@ public final class JobServiceContext implements ServiceConnection {
}
} else if (mAwaitingNotification) {
onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true,
/* texCounterMetricId */
"job_scheduler.value_cntr_w_uid_slow_app_response_setNotification",
/* debugReason */ "timed out while stopping",
/* anrMessage */ "required notification not provided",
/* triggerAnr */ true);
@@ -1347,8 +1450,11 @@ public final class JobServiceContext implements ServiceConnection {
@GuardedBy("mLock")
private void onSlowAppResponseLocked(boolean reschedule, boolean updateStopReasons,
@NonNull String texCounterMetricId,
@NonNull String debugReason, @NonNull String anrMessage, boolean triggerAnr) {
Slog.w(TAG, anrMessage + " for " + getRunningJobNameLocked());
// Use the calling UID since that's the one this context was connected to.
Counter.logIncrementWithUid(texCounterMetricId, mRunningJob.getUid());
if (updateStopReasons) {
mParams.setStopReason(
JobParameters.STOP_REASON_UNDEFINED,

View File

@@ -90,6 +90,7 @@ public final class TimeController extends StateController {
final long nowElapsedMillis = sElapsedRealtimeClock.millis();
if (job.hasDeadlineConstraint() && evaluateDeadlineConstraint(job, nowElapsedMillis)) {
// We're intentionally excluding jobs whose deadlines have passed
// from the job_scheduler.value_job_scheduler_job_deadline_expired_counter count
// (mostly like deadlines of 0) when the job was scheduled.
return;
} else if (job.hasTimingDelayConstraint() && evaluateTimingDelayConstraint(job,

View File

@@ -90,7 +90,7 @@ public class JobNotificationCoordinatorTest {
@Test
public void testParameterValidation() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
final JobServiceContext jsc = createJobServiceContext();
final int uid = 10123;
final int pid = 42;
final int notificationId = 23;
@@ -137,7 +137,7 @@ public class JobNotificationCoordinatorTest {
@Test
public void testSingleJob_DetachOnStop() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
final JobServiceContext jsc = createJobServiceContext();
final Notification notification = createValidNotification();
final int uid = 10123;
final int pid = 42;
@@ -159,7 +159,7 @@ public class JobNotificationCoordinatorTest {
@Test
public void testSingleJob_RemoveOnStop() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
final JobServiceContext jsc = createJobServiceContext();
final Notification notification = createValidNotification();
final int uid = 10123;
final int pid = 42;
@@ -181,7 +181,7 @@ public class JobNotificationCoordinatorTest {
@Test
public void testSingleJob_EnqueueDifferentNotificationId_DetachOnStop() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
final JobServiceContext jsc = createJobServiceContext();
final Notification notification1 = createValidNotification();
final Notification notification2 = createValidNotification();
final int uid = 10123;
@@ -210,7 +210,7 @@ public class JobNotificationCoordinatorTest {
@Test
public void testSingleJob_EnqueueDifferentNotificationId_RemoveOnStop() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
final JobServiceContext jsc = createJobServiceContext();
final Notification notification1 = createValidNotification();
final Notification notification2 = createValidNotification();
final int uid = 10123;
@@ -239,7 +239,7 @@ public class JobNotificationCoordinatorTest {
@Test
public void testSingleJob_EnqueueSameNotificationId() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
final JobServiceContext jsc = createJobServiceContext();
final Notification notification1 = createValidNotification();
final Notification notification2 = createValidNotification();
final int uid = 10123;
@@ -267,8 +267,8 @@ public class JobNotificationCoordinatorTest {
@Test
public void testMultipleJobs_sameApp_EnqueueDifferentNotificationId() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc1 = mock(JobServiceContext.class);
final JobServiceContext jsc2 = mock(JobServiceContext.class);
final JobServiceContext jsc1 = createJobServiceContext();
final JobServiceContext jsc2 = createJobServiceContext();
final Notification notification1 = createValidNotification();
final Notification notification2 = createValidNotification();
final int uid = 10123;
@@ -313,8 +313,8 @@ public class JobNotificationCoordinatorTest {
@Test
public void testMultipleJobs_sameApp_EnqueueSameNotificationId() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc1 = mock(JobServiceContext.class);
final JobServiceContext jsc2 = mock(JobServiceContext.class);
final JobServiceContext jsc1 = createJobServiceContext();
final JobServiceContext jsc2 = createJobServiceContext();
final Notification notification1 = createValidNotification();
final Notification notification2 = createValidNotification();
final int uid = 10123;
@@ -355,8 +355,8 @@ public class JobNotificationCoordinatorTest {
@Test
public void testMultipleJobs_sameApp_DifferentUsers() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc1 = mock(JobServiceContext.class);
final JobServiceContext jsc2 = mock(JobServiceContext.class);
final JobServiceContext jsc1 = createJobServiceContext();
final JobServiceContext jsc2 = createJobServiceContext();
final Notification notification1 = createValidNotification();
final Notification notification2 = createValidNotification();
final int uid1 = 10123;
@@ -403,8 +403,8 @@ public class JobNotificationCoordinatorTest {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final String pkg1 = "pkg1";
final String pkg2 = "pkg2";
final JobServiceContext jsc1 = mock(JobServiceContext.class);
final JobServiceContext jsc2 = mock(JobServiceContext.class);
final JobServiceContext jsc1 = createJobServiceContext();
final JobServiceContext jsc2 = createJobServiceContext();
final Notification notification1 = createValidNotification();
final Notification notification2 = createValidNotification();
final int uid = 10123;
@@ -448,7 +448,7 @@ public class JobNotificationCoordinatorTest {
@Test
public void testUserStop_SingleJob_DetachOnStop() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
final JobServiceContext jsc = createJobServiceContext();
final Notification notification = createValidNotification();
final int uid = 10123;
final int pid = 42;
@@ -470,8 +470,8 @@ public class JobNotificationCoordinatorTest {
@Test
public void testUserStop_MultipleJobs_sameApp_EnqueueSameNotificationId_DetachOnStop() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc1 = mock(JobServiceContext.class);
final JobServiceContext jsc2 = mock(JobServiceContext.class);
final JobServiceContext jsc1 = createJobServiceContext();
final JobServiceContext jsc2 = createJobServiceContext();
final Notification notification1 = createValidNotification();
final Notification notification2 = createValidNotification();
final int uid = 10123;
@@ -512,10 +512,9 @@ public class JobNotificationCoordinatorTest {
@Test
public void testUserInitiatedJob_hasNotificationFlag() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
final JobStatus js = mock(JobStatus.class);
final JobServiceContext jsc = createJobServiceContext();
final JobStatus js = jsc.getRunningJobLocked();
js.startedAsUserInitiatedJob = true;
doReturn(js).when(jsc).getRunningJobLocked();
final Notification notification = createValidNotification();
final int uid = 10123;
final int pid = 42;
@@ -532,8 +531,7 @@ public class JobNotificationCoordinatorTest {
@Test
public void testNonUserInitiatedJob_doesNotHaveNotificationFlag() {
final JobNotificationCoordinator coordinator = new JobNotificationCoordinator();
final JobServiceContext jsc = mock(JobServiceContext.class);
doReturn(mock(JobStatus.class)).when(jsc).getRunningJobLocked();
final JobServiceContext jsc = createJobServiceContext();
final Notification notification = createValidNotification();
final int uid = 10123;
final int pid = 42;
@@ -547,6 +545,12 @@ public class JobNotificationCoordinatorTest {
assertEquals(notification.flags & Notification.FLAG_USER_INITIATED_JOB, 0);
}
private JobServiceContext createJobServiceContext() {
final JobServiceContext jsc = mock(JobServiceContext.class);
doReturn(mock(JobStatus.class)).when(jsc).getRunningJobLocked();
return jsc;
}
private Notification createValidNotification() {
final Notification notification = mock(Notification.class);
doReturn(mock(Icon.class)).when(notification).getSmallIcon();