Let prefetch jobs run for apps with active widgets.
We currently can't tell if a widget is displaying data that is updated periodically. If a prefetch job is scheduled for such a widget, it should get to run since it'll be helpful for the user. Given what we know right now, we will mark the prefetch bit as satisfied for apps with active widgets. We won't relax the force-batching requirement unless we also determine that the app will be launched soon. Bug: 194532703 Test: atest FrameworksMockingServicesTests:PrefetchControllerTest Change-Id: I6d1c17c386a27e70525f72824710739b78e9ad5e
This commit is contained in:
@@ -28,6 +28,7 @@ import android.annotation.NonNull;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -63,6 +64,13 @@ public class PrefetchController extends StateController {
|
||||
private final PcConstants mPcConstants;
|
||||
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;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@@ -117,6 +125,11 @@ public class PrefetchController extends StateController {
|
||||
.registerLaunchTimeChangedListener(mEstimatedLaunchTimeChangedListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSystemServicesReady() {
|
||||
mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@GuardedBy("mLock")
|
||||
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
|
||||
@@ -298,11 +311,23 @@ public class PrefetchController extends StateController {
|
||||
// Mark a prefetch constraint as satisfied in the following scenarios:
|
||||
// 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)
|
||||
// 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 satisfied;
|
||||
if (!appIsOpen) {
|
||||
satisfied = willBeLaunchedSoonLocked(
|
||||
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), now);
|
||||
final int userId = jobStatus.getSourceUserId();
|
||||
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 {
|
||||
satisfied = mService.isCurrentlyRunningLocked(jobStatus);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.app.AlarmManager;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
@@ -168,12 +169,15 @@ public class PrefetchControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
private JobStatus createJobStatus(String testTag, int jobId) {
|
||||
JobInfo jobInfo = new JobInfo.Builder(jobId,
|
||||
private JobInfo createJobInfo(int jobId) {
|
||||
return new JobInfo.Builder(jobId,
|
||||
new ComponentName(mContext, "TestPrefetchJobService"))
|
||||
.setPrefetch(true)
|
||||
.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,
|
||||
@@ -330,6 +334,32 @@ public class PrefetchControllerTest {
|
||||
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
|
||||
public void testEstimatedLaunchTimeChangedToLate() {
|
||||
setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
|
||||
|
||||
Reference in New Issue
Block a user