Loading apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java +4 −4 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ package com.android.server.job; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.util.proto.ProtoOutputStream; Loading Loading @@ -47,9 +47,9 @@ public interface JobSchedulerInternal { void removeBackingUpUid(int uid); void clearAllBackingUpUids(); /** Returns the package responsible for backing up media on the device. */ @NonNull String getMediaBackupPackage(); /** Returns the package responsible for providing media from the cloud to the device. */ @Nullable String getCloudMediaProviderPackage(int userId); /** * The user has started interacting with the app. Take any appropriate action. Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +74 −8 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.BatteryManager; Loading @@ -70,6 +71,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.os.storage.StorageManagerInternal; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.format.DateUtils; Loading @@ -86,10 +88,10 @@ import android.util.SparseSetArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; Loading Loading @@ -237,6 +239,7 @@ public class JobSchedulerService extends com.android.server.SystemService static final int MSG_UID_ACTIVE = 6; static final int MSG_UID_IDLE = 7; static final int MSG_CHECK_CHANGED_JOB_LIST = 8; static final int MSG_CHECK_MEDIA_EXEMPTION = 9; /** * Track Services that have currently active or pending jobs. The index is provided by Loading Loading @@ -271,8 +274,8 @@ public class JobSchedulerService extends com.android.server.SystemService @GuardedBy("mLock") private final BatteryStateTracker mBatteryStateTracker; @NonNull private final String mSystemGalleryPackage; @GuardedBy("mLock") private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>(); private final CountQuotaTracker mQuotaTracker; private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()"; Loading Loading @@ -1783,9 +1786,6 @@ public class JobSchedulerService extends com.android.server.SystemService mJobRestrictions = new ArrayList<>(); mJobRestrictions.add(new ThermalStatusRestriction(this)); mSystemGalleryPackage = Objects.requireNonNull( context.getString(R.string.config_systemGallery)); // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. if (!mJobs.jobTimesInflatedValid()) { Loading Loading @@ -1855,6 +1855,9 @@ public class JobSchedulerService extends com.android.server.SystemService mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull( LocalServices.getService(AppStateTracker.class)); LocalServices.getService(StorageManagerInternal.class) .registerCloudProviderChangeListener(new CloudProviderChangeListener()); // Register br for package removals and user removals. final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); Loading Loading @@ -2353,6 +2356,15 @@ public class JobSchedulerService extends com.android.server.SystemService break; } case MSG_CHECK_MEDIA_EXEMPTION: { final SomeArgs args = (SomeArgs) message.obj; synchronized (mLock) { updateMediaBackupExemptionLocked( args.argi1, (String) args.arg1, (String) args.arg2); } args.recycle(); break; } } maybeRunPendingJobsLocked(); } Loading Loading @@ -2649,12 +2661,31 @@ public class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "Check changed jobs..."); } if (mChangedJobList.size() == 0) { return; } mChangedJobList.forEach(mMaybeQueueFunctor); mMaybeQueueFunctor.postProcessLocked(); mChangedJobList.clear(); } @GuardedBy("mLock") private void updateMediaBackupExemptionLocked(int userId, @Nullable String oldPkg, @Nullable String newPkg) { final Predicate<JobStatus> shouldProcessJob = (job) -> job.getSourceUserId() == userId && (job.getSourcePackageName().equals(oldPkg) || job.getSourcePackageName().equals(newPkg)); mJobs.forEachJob(shouldProcessJob, (job) -> { if (job.updateMediaBackupExemptionStatus()) { mChangedJobList.add(job); } }); mHandler.sendEmptyMessage(MSG_CHECK_CHANGED_JOB_LIST); } /** Returns true if both the calling and source users for the job are started. */ @GuardedBy("mLock") public boolean areUsersStartedLocked(final JobStatus job) { Loading Loading @@ -3050,8 +3081,8 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override public String getMediaBackupPackage() { return mSystemGalleryPackage; public String getCloudMediaProviderPackage(int userId) { return mCloudMediaProviderPackages.get(userId); } @Override Loading Loading @@ -3159,6 +3190,35 @@ public class JobSchedulerService extends com.android.server.SystemService return bucket; } private class CloudProviderChangeListener implements StorageManagerInternal.CloudProviderChangeListener { @Override public void onCloudProviderChanged(int userId, @Nullable String authority) { final PackageManager pm = getContext() .createContextAsUser(UserHandle.of(userId), 0) .getPackageManager(); final ProviderInfo pi = pm.resolveContentProvider( authority, PackageManager.ComponentInfoFlags.of(0)); final String newPkg = (pi == null) ? null : pi.packageName; synchronized (mLock) { final String oldPkg = mCloudMediaProviderPackages.get(userId); if (!Objects.equals(oldPkg, newPkg)) { if (DEBUG) { Slog.d(TAG, "Cloud provider of user " + userId + " changed from " + oldPkg + " to " + newPkg); } mCloudMediaProviderPackages.put(userId, newPkg); SomeArgs args = SomeArgs.obtain(); args.argi1 = userId; args.arg1 = oldPkg; args.arg2 = newPkg; mHandler.obtainMessage(MSG_CHECK_MEDIA_EXEMPTION, args).sendToTarget(); } } } } /** * Binder stub trampoline implementation */ Loading Loading @@ -3799,6 +3859,12 @@ public class JobSchedulerService extends com.android.server.SystemService pw.println(); pw.println("Started users: " + Arrays.toString(mStartedUsers)); pw.println(); pw.print("Media Cloud Providers: "); pw.println(mCloudMediaProviderPackages); pw.println(); pw.print("Registered "); pw.print(mJobs.size()); pw.println(" jobs:"); Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +24 −9 Original line number Diff line number Diff line Loading @@ -261,11 +261,9 @@ public final class JobStatus { * * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or * network changes, in which case this exemption does not make sense. * * TODO(b/149519887): Use a more explicit signal, maybe an API flag, that the scheduling package * needs to provide at the time of scheduling a job. */ private final boolean mHasMediaBackupExemption; private boolean mHasMediaBackupExemption; private final boolean mHasExemptedMediaUrisOnly; // Set to true if doze constraint was satisfied due to app being whitelisted. boolean appHasDozeExemption; Loading Loading @@ -508,11 +506,9 @@ public final class JobStatus { this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.numFailures = numFailures; boolean requiresNetwork = false; int requiredConstraints = job.getConstraintFlags(); if (job.getRequiredNetwork() != null) { requiredConstraints |= CONSTRAINT_CONNECTIVITY; requiresNetwork = true; } if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) { requiredConstraints |= CONSTRAINT_TIMING_DELAY; Loading @@ -531,6 +527,7 @@ public final class JobStatus { } } } mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly; this.requiredConstraints = requiredConstraints; mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST; addDynamicConstraints(dynamicConstraints); Loading Loading @@ -563,9 +560,7 @@ public final class JobStatus { job = builder.build(false); } final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); mHasMediaBackupExemption = !job.hasLateConstraint() && exemptedMediaUrisOnly && requiresNetwork && this.sourcePackageName.equals(jsi.getMediaBackupPackage()); updateMediaBackupExemptionStatus(); } /** Copy constructor: used specifically when cloning JobStatus objects for persistence, Loading Loading @@ -914,6 +909,25 @@ public final class JobStatus { mFirstForceBatchedTimeElapsed = now; } /** * Re-evaluates the media backup exemption status. * * @return true if the exemption status changed */ public boolean updateMediaBackupExemptionStatus() { final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); boolean hasMediaExemption = mHasExemptedMediaUrisOnly && !job.hasLateConstraint() && job.getRequiredNetwork() != null && getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId)); if (mHasMediaBackupExemption == hasMediaExemption) { return false; } mHasMediaBackupExemption = hasMediaExemption; return true; } public String getSourceTag() { return sourceTag; } Loading Loading @@ -2027,6 +2041,7 @@ public final class JobStatus { TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw); pw.println(); } pw.print("Has media backup exemption", mHasMediaBackupExemption).println(); } if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) { pw.print("Extras: "); Loading core/api/module-lib-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -415,6 +415,7 @@ package android.os.storage { method public long computeStorageCacheBytes(@NonNull java.io.File); method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int); method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int); method public void setCloudMediaProvider(@Nullable String); field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1 field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0 } Loading core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -1967,6 +1967,7 @@ package android.os.storage { method public long computeStorageCacheBytes(@NonNull java.io.File); method @NonNull public static java.util.UUID convert(@NonNull String); method @NonNull public static String convert(@NonNull java.util.UUID); method @Nullable public String getCloudMediaProvider(); method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int); method public static boolean isUserKeyUnlocked(int); field public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high"; Loading Loading
apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java +4 −4 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ package com.android.server.job; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.util.proto.ProtoOutputStream; Loading Loading @@ -47,9 +47,9 @@ public interface JobSchedulerInternal { void removeBackingUpUid(int uid); void clearAllBackingUpUids(); /** Returns the package responsible for backing up media on the device. */ @NonNull String getMediaBackupPackage(); /** Returns the package responsible for providing media from the cloud to the device. */ @Nullable String getCloudMediaProviderPackage(int userId); /** * The user has started interacting with the app. Take any appropriate action. Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +74 −8 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.BatteryManager; Loading @@ -70,6 +71,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.os.storage.StorageManagerInternal; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.format.DateUtils; Loading @@ -86,10 +88,10 @@ import android.util.SparseSetArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; Loading Loading @@ -237,6 +239,7 @@ public class JobSchedulerService extends com.android.server.SystemService static final int MSG_UID_ACTIVE = 6; static final int MSG_UID_IDLE = 7; static final int MSG_CHECK_CHANGED_JOB_LIST = 8; static final int MSG_CHECK_MEDIA_EXEMPTION = 9; /** * Track Services that have currently active or pending jobs. The index is provided by Loading Loading @@ -271,8 +274,8 @@ public class JobSchedulerService extends com.android.server.SystemService @GuardedBy("mLock") private final BatteryStateTracker mBatteryStateTracker; @NonNull private final String mSystemGalleryPackage; @GuardedBy("mLock") private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>(); private final CountQuotaTracker mQuotaTracker; private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()"; Loading Loading @@ -1783,9 +1786,6 @@ public class JobSchedulerService extends com.android.server.SystemService mJobRestrictions = new ArrayList<>(); mJobRestrictions.add(new ThermalStatusRestriction(this)); mSystemGalleryPackage = Objects.requireNonNull( context.getString(R.string.config_systemGallery)); // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. if (!mJobs.jobTimesInflatedValid()) { Loading Loading @@ -1855,6 +1855,9 @@ public class JobSchedulerService extends com.android.server.SystemService mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull( LocalServices.getService(AppStateTracker.class)); LocalServices.getService(StorageManagerInternal.class) .registerCloudProviderChangeListener(new CloudProviderChangeListener()); // Register br for package removals and user removals. final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); Loading Loading @@ -2353,6 +2356,15 @@ public class JobSchedulerService extends com.android.server.SystemService break; } case MSG_CHECK_MEDIA_EXEMPTION: { final SomeArgs args = (SomeArgs) message.obj; synchronized (mLock) { updateMediaBackupExemptionLocked( args.argi1, (String) args.arg1, (String) args.arg2); } args.recycle(); break; } } maybeRunPendingJobsLocked(); } Loading Loading @@ -2649,12 +2661,31 @@ public class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "Check changed jobs..."); } if (mChangedJobList.size() == 0) { return; } mChangedJobList.forEach(mMaybeQueueFunctor); mMaybeQueueFunctor.postProcessLocked(); mChangedJobList.clear(); } @GuardedBy("mLock") private void updateMediaBackupExemptionLocked(int userId, @Nullable String oldPkg, @Nullable String newPkg) { final Predicate<JobStatus> shouldProcessJob = (job) -> job.getSourceUserId() == userId && (job.getSourcePackageName().equals(oldPkg) || job.getSourcePackageName().equals(newPkg)); mJobs.forEachJob(shouldProcessJob, (job) -> { if (job.updateMediaBackupExemptionStatus()) { mChangedJobList.add(job); } }); mHandler.sendEmptyMessage(MSG_CHECK_CHANGED_JOB_LIST); } /** Returns true if both the calling and source users for the job are started. */ @GuardedBy("mLock") public boolean areUsersStartedLocked(final JobStatus job) { Loading Loading @@ -3050,8 +3081,8 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override public String getMediaBackupPackage() { return mSystemGalleryPackage; public String getCloudMediaProviderPackage(int userId) { return mCloudMediaProviderPackages.get(userId); } @Override Loading Loading @@ -3159,6 +3190,35 @@ public class JobSchedulerService extends com.android.server.SystemService return bucket; } private class CloudProviderChangeListener implements StorageManagerInternal.CloudProviderChangeListener { @Override public void onCloudProviderChanged(int userId, @Nullable String authority) { final PackageManager pm = getContext() .createContextAsUser(UserHandle.of(userId), 0) .getPackageManager(); final ProviderInfo pi = pm.resolveContentProvider( authority, PackageManager.ComponentInfoFlags.of(0)); final String newPkg = (pi == null) ? null : pi.packageName; synchronized (mLock) { final String oldPkg = mCloudMediaProviderPackages.get(userId); if (!Objects.equals(oldPkg, newPkg)) { if (DEBUG) { Slog.d(TAG, "Cloud provider of user " + userId + " changed from " + oldPkg + " to " + newPkg); } mCloudMediaProviderPackages.put(userId, newPkg); SomeArgs args = SomeArgs.obtain(); args.argi1 = userId; args.arg1 = oldPkg; args.arg2 = newPkg; mHandler.obtainMessage(MSG_CHECK_MEDIA_EXEMPTION, args).sendToTarget(); } } } } /** * Binder stub trampoline implementation */ Loading Loading @@ -3799,6 +3859,12 @@ public class JobSchedulerService extends com.android.server.SystemService pw.println(); pw.println("Started users: " + Arrays.toString(mStartedUsers)); pw.println(); pw.print("Media Cloud Providers: "); pw.println(mCloudMediaProviderPackages); pw.println(); pw.print("Registered "); pw.print(mJobs.size()); pw.println(" jobs:"); Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +24 −9 Original line number Diff line number Diff line Loading @@ -261,11 +261,9 @@ public final class JobStatus { * * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or * network changes, in which case this exemption does not make sense. * * TODO(b/149519887): Use a more explicit signal, maybe an API flag, that the scheduling package * needs to provide at the time of scheduling a job. */ private final boolean mHasMediaBackupExemption; private boolean mHasMediaBackupExemption; private final boolean mHasExemptedMediaUrisOnly; // Set to true if doze constraint was satisfied due to app being whitelisted. boolean appHasDozeExemption; Loading Loading @@ -508,11 +506,9 @@ public final class JobStatus { this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.numFailures = numFailures; boolean requiresNetwork = false; int requiredConstraints = job.getConstraintFlags(); if (job.getRequiredNetwork() != null) { requiredConstraints |= CONSTRAINT_CONNECTIVITY; requiresNetwork = true; } if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) { requiredConstraints |= CONSTRAINT_TIMING_DELAY; Loading @@ -531,6 +527,7 @@ public final class JobStatus { } } } mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly; this.requiredConstraints = requiredConstraints; mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST; addDynamicConstraints(dynamicConstraints); Loading Loading @@ -563,9 +560,7 @@ public final class JobStatus { job = builder.build(false); } final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); mHasMediaBackupExemption = !job.hasLateConstraint() && exemptedMediaUrisOnly && requiresNetwork && this.sourcePackageName.equals(jsi.getMediaBackupPackage()); updateMediaBackupExemptionStatus(); } /** Copy constructor: used specifically when cloning JobStatus objects for persistence, Loading Loading @@ -914,6 +909,25 @@ public final class JobStatus { mFirstForceBatchedTimeElapsed = now; } /** * Re-evaluates the media backup exemption status. * * @return true if the exemption status changed */ public boolean updateMediaBackupExemptionStatus() { final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); boolean hasMediaExemption = mHasExemptedMediaUrisOnly && !job.hasLateConstraint() && job.getRequiredNetwork() != null && getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId)); if (mHasMediaBackupExemption == hasMediaExemption) { return false; } mHasMediaBackupExemption = hasMediaExemption; return true; } public String getSourceTag() { return sourceTag; } Loading Loading @@ -2027,6 +2041,7 @@ public final class JobStatus { TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw); pw.println(); } pw.print("Has media backup exemption", mHasMediaBackupExemption).println(); } if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) { pw.print("Extras: "); Loading
core/api/module-lib-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -415,6 +415,7 @@ package android.os.storage { method public long computeStorageCacheBytes(@NonNull java.io.File); method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int); method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int); method public void setCloudMediaProvider(@Nullable String); field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1 field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0 } Loading
core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -1967,6 +1967,7 @@ package android.os.storage { method public long computeStorageCacheBytes(@NonNull java.io.File); method @NonNull public static java.util.UUID convert(@NonNull String); method @NonNull public static String convert(@NonNull java.util.UUID); method @Nullable public String getCloudMediaProvider(); method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int); method public static boolean isUserKeyUnlocked(int); field public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high"; Loading