Loading apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +27 −2 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.annotation.NonNull; import android.app.job.JobInfo; import android.app.job.JobInfo; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Context; import android.os.Handler; import android.os.Handler; import android.os.Looper; import android.os.Looper; Loading Loading @@ -63,6 +64,13 @@ public class PrefetchController extends StateController { private final PcConstants mPcConstants; private final PcConstants mPcConstants; private final PcHandler mHandler; private final PcHandler mHandler; // Note: when determining prefetch bit satisfaction, we mark the bit as satisfied for apps with // active widgets assuming that any prefetch jobs are being used for the widget. However, we // don't have a callback telling us when widget status changes, which is incongruent with the // aforementioned assumption. This inconsistency _should_ be fine since any jobs scheduled // before the widget is activated are definitely not for the widget and don't have to be updated // to "satisfied=true". private AppWidgetManager mAppWidgetManager; private final UsageStatsManagerInternal mUsageStatsManagerInternal; private final UsageStatsManagerInternal mUsageStatsManagerInternal; @GuardedBy("mLock") @GuardedBy("mLock") Loading Loading @@ -117,6 +125,11 @@ public class PrefetchController extends StateController { .registerLaunchTimeChangedListener(mEstimatedLaunchTimeChangedListener); .registerLaunchTimeChangedListener(mEstimatedLaunchTimeChangedListener); } } @Override public void onSystemServicesReady() { mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class); } @Override @Override @GuardedBy("mLock") @GuardedBy("mLock") public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { Loading Loading @@ -298,11 +311,23 @@ public class PrefetchController extends StateController { // Mark a prefetch constraint as satisfied in the following scenarios: // Mark a prefetch constraint as satisfied in the following scenarios: // 1. The app is not open but it will be launched soon // 1. The app is not open but it will be launched soon // 2. The app is open and the job is already running (so we let it finish) // 2. The app is open and the job is already running (so we let it finish) // 3. The app is not open but has an active widget (we can't tell if a widget displays // status/data, so this assumes the prefetch job is to update the data displayed on // the widget). final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid()); final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid()); final boolean satisfied; final boolean satisfied; if (!appIsOpen) { if (!appIsOpen) { satisfied = willBeLaunchedSoonLocked( final int userId = jobStatus.getSourceUserId(); jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), now); final String pkgName = jobStatus.getSourcePackageName(); satisfied = willBeLaunchedSoonLocked(userId, pkgName, now) // At the time of implementation, isBoundWidgetPackage() results in a process ID // check and then a lookup into a map. Calling the method here every time // is based on the assumption that widgets won't change often and // AppWidgetManager won't be a bottleneck, so having a local cache won't provide // huge performance gains. If anything changes, we should reconsider having a // local cache. || (mAppWidgetManager != null && mAppWidgetManager.isBoundWidgetPackage(pkgName, userId)); } else { } else { satisfied = mService.isCurrentlyRunningLocked(jobStatus); satisfied = mService.isCurrentlyRunningLocked(jobStatus); } } Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java +33 −3 Original line number Original line Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.app.AlarmManager; import android.app.job.JobInfo; import android.app.job.JobInfo; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ComponentName; import android.content.Context; import android.content.Context; import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo; Loading Loading @@ -168,12 +169,15 @@ public class PrefetchControllerTest { } } } } private JobStatus createJobStatus(String testTag, int jobId) { private JobInfo createJobInfo(int jobId) { JobInfo jobInfo = new JobInfo.Builder(jobId, return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestPrefetchJobService")) new ComponentName(mContext, "TestPrefetchJobService")) .setPrefetch(true) .setPrefetch(true) .build(); .build(); return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); } private JobStatus createJobStatus(String testTag, int jobId) { return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, createJobInfo(jobId)); } } private static JobStatus createJobStatus(String testTag, String packageName, int callingUid, private static JobStatus createJobStatus(String testTag, String packageName, int callingUid, Loading Loading @@ -330,6 +334,32 @@ public class PrefetchControllerTest { assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); } } @Test public void testConstraintSatisfiedWhenWidget() { final JobStatus jobNonWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 1); final JobStatus jobWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 2); when(mUsageStatsManagerInternal .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(sSystemClock.millis() + 100 * HOUR_IN_MILLIS); final AppWidgetManager appWidgetManager = mock(AppWidgetManager.class); when(mContext.getSystemService(AppWidgetManager.class)).thenReturn(appWidgetManager); mPrefetchController.onSystemServicesReady(); when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(false); trackJobs(jobNonWidget); verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); assertFalse(jobNonWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(true); trackJobs(jobWidget); assertTrue(jobWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); } @Test @Test public void testEstimatedLaunchTimeChangedToLate() { public void testEstimatedLaunchTimeChangedToLate() { setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +27 −2 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.annotation.NonNull; import android.app.job.JobInfo; import android.app.job.JobInfo; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Context; import android.os.Handler; import android.os.Handler; import android.os.Looper; import android.os.Looper; Loading Loading @@ -63,6 +64,13 @@ public class PrefetchController extends StateController { private final PcConstants mPcConstants; private final PcConstants mPcConstants; private final PcHandler mHandler; private final PcHandler mHandler; // Note: when determining prefetch bit satisfaction, we mark the bit as satisfied for apps with // active widgets assuming that any prefetch jobs are being used for the widget. However, we // don't have a callback telling us when widget status changes, which is incongruent with the // aforementioned assumption. This inconsistency _should_ be fine since any jobs scheduled // before the widget is activated are definitely not for the widget and don't have to be updated // to "satisfied=true". private AppWidgetManager mAppWidgetManager; private final UsageStatsManagerInternal mUsageStatsManagerInternal; private final UsageStatsManagerInternal mUsageStatsManagerInternal; @GuardedBy("mLock") @GuardedBy("mLock") Loading Loading @@ -117,6 +125,11 @@ public class PrefetchController extends StateController { .registerLaunchTimeChangedListener(mEstimatedLaunchTimeChangedListener); .registerLaunchTimeChangedListener(mEstimatedLaunchTimeChangedListener); } } @Override public void onSystemServicesReady() { mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class); } @Override @Override @GuardedBy("mLock") @GuardedBy("mLock") public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { Loading Loading @@ -298,11 +311,23 @@ public class PrefetchController extends StateController { // Mark a prefetch constraint as satisfied in the following scenarios: // Mark a prefetch constraint as satisfied in the following scenarios: // 1. The app is not open but it will be launched soon // 1. The app is not open but it will be launched soon // 2. The app is open and the job is already running (so we let it finish) // 2. The app is open and the job is already running (so we let it finish) // 3. The app is not open but has an active widget (we can't tell if a widget displays // status/data, so this assumes the prefetch job is to update the data displayed on // the widget). final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid()); final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid()); final boolean satisfied; final boolean satisfied; if (!appIsOpen) { if (!appIsOpen) { satisfied = willBeLaunchedSoonLocked( final int userId = jobStatus.getSourceUserId(); jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), now); final String pkgName = jobStatus.getSourcePackageName(); satisfied = willBeLaunchedSoonLocked(userId, pkgName, now) // At the time of implementation, isBoundWidgetPackage() results in a process ID // check and then a lookup into a map. Calling the method here every time // is based on the assumption that widgets won't change often and // AppWidgetManager won't be a bottleneck, so having a local cache won't provide // huge performance gains. If anything changes, we should reconsider having a // local cache. || (mAppWidgetManager != null && mAppWidgetManager.isBoundWidgetPackage(pkgName, userId)); } else { } else { satisfied = mService.isCurrentlyRunningLocked(jobStatus); satisfied = mService.isCurrentlyRunningLocked(jobStatus); } } Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java +33 −3 Original line number Original line Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.app.AlarmManager; import android.app.job.JobInfo; import android.app.job.JobInfo; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ComponentName; import android.content.Context; import android.content.Context; import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo; Loading Loading @@ -168,12 +169,15 @@ public class PrefetchControllerTest { } } } } private JobStatus createJobStatus(String testTag, int jobId) { private JobInfo createJobInfo(int jobId) { JobInfo jobInfo = new JobInfo.Builder(jobId, return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestPrefetchJobService")) new ComponentName(mContext, "TestPrefetchJobService")) .setPrefetch(true) .setPrefetch(true) .build(); .build(); return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); } private JobStatus createJobStatus(String testTag, int jobId) { return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, createJobInfo(jobId)); } } private static JobStatus createJobStatus(String testTag, String packageName, int callingUid, private static JobStatus createJobStatus(String testTag, String packageName, int callingUid, Loading Loading @@ -330,6 +334,32 @@ public class PrefetchControllerTest { assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); } } @Test public void testConstraintSatisfiedWhenWidget() { final JobStatus jobNonWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 1); final JobStatus jobWidget = createJobStatus("testConstraintSatisfiedWhenWidget", 2); when(mUsageStatsManagerInternal .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(sSystemClock.millis() + 100 * HOUR_IN_MILLIS); final AppWidgetManager appWidgetManager = mock(AppWidgetManager.class); when(mContext.getSystemService(AppWidgetManager.class)).thenReturn(appWidgetManager); mPrefetchController.onSystemServicesReady(); when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(false); trackJobs(jobNonWidget); verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS)) .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID); assertFalse(jobNonWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID)) .thenReturn(true); trackJobs(jobWidget); assertTrue(jobWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH)); } @Test @Test public void testEstimatedLaunchTimeChangedToLate() { public void testEstimatedLaunchTimeChangedToLate() { setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS); Loading