Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License |
| 15 | */ |
| 16 | |
| 17 | package com.android.server.job.controllers; |
| 18 | |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 19 | import android.content.Context; |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 20 | import android.os.SystemClock; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 21 | import android.os.UserHandle; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 22 | import android.util.Slog; |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 23 | import android.util.proto.ProtoOutputStream; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 24 | |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 25 | import com.android.internal.util.Preconditions; |
| 26 | import com.android.server.AppStateTracker; |
| 27 | import com.android.server.AppStateTracker.Listener; |
| 28 | import com.android.server.LocalServices; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 29 | import com.android.server.job.JobSchedulerService; |
| 30 | import com.android.server.job.JobStore; |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 31 | import com.android.server.job.StateControllerProto; |
| 32 | import com.android.server.job.StateControllerProto.BackgroundJobsController.TrackedJob; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 33 | |
| 34 | import java.io.PrintWriter; |
| 35 | |
| 36 | public final class BackgroundJobsController extends StateController { |
| 37 | |
| 38 | private static final String LOG_TAG = "BackgroundJobsController"; |
| 39 | private static final boolean DEBUG = JobSchedulerService.DEBUG; |
| 40 | |
| 41 | // Singleton factory |
| 42 | private static final Object sCreationLock = new Object(); |
| 43 | private static volatile BackgroundJobsController sController; |
| 44 | |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 45 | private final JobSchedulerService mJobSchedulerService; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 46 | |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 47 | private final AppStateTracker mAppStateTracker; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 48 | |
| 49 | public static BackgroundJobsController get(JobSchedulerService service) { |
| 50 | synchronized (sCreationLock) { |
| 51 | if (sController == null) { |
| 52 | sController = new BackgroundJobsController(service, service.getContext(), |
| 53 | service.getLock()); |
| 54 | } |
| 55 | return sController; |
| 56 | } |
| 57 | } |
| 58 | |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 59 | private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) { |
| 60 | super(service, context, lock); |
| 61 | mJobSchedulerService = service; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 62 | |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 63 | mAppStateTracker = Preconditions.checkNotNull( |
| 64 | LocalServices.getService(AppStateTracker.class)); |
| 65 | mAppStateTracker.addListener(mForceAppStandbyListener); |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 66 | } |
| 67 | |
| 68 | @Override |
| 69 | public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 70 | updateSingleJobRestrictionLocked(jobStatus); |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 71 | } |
| 72 | |
| 73 | @Override |
| 74 | public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, |
| 75 | boolean forUpdate) { |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | @Override |
| 79 | public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) { |
Suprabh Shukla | a78acfd | 2017-10-13 19:29:36 -0700 | [diff] [blame] | 80 | pw.println("BackgroundJobsController"); |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 81 | |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 82 | mAppStateTracker.dump(pw, ""); |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 83 | |
| 84 | pw.println("Job state:"); |
| 85 | mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> { |
| 86 | if (!jobStatus.shouldDump(filterUid)) { |
| 87 | return; |
| 88 | } |
| 89 | final int uid = jobStatus.getSourceUid(); |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 90 | final String sourcePkg = jobStatus.getSourcePackageName(); |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 91 | pw.print(" #"); |
| 92 | jobStatus.printUniqueId(pw); |
| 93 | pw.print(" from "); |
| 94 | UserHandle.formatUid(pw, uid); |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 95 | pw.print(mAppStateTracker.isUidActive(uid) ? " active" : " idle"); |
| 96 | if (mAppStateTracker.isUidPowerSaveWhitelisted(uid) || |
| 97 | mAppStateTracker.isUidTempPowerSaveWhitelisted(uid)) { |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 98 | pw.print(", whitelisted"); |
| 99 | } |
| 100 | pw.print(": "); |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 101 | pw.print(sourcePkg); |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 102 | |
Makoto Onuki | 2206af39 | 2017-11-21 16:25:35 -0800 | [diff] [blame] | 103 | pw.print(" [RUN_ANY_IN_BACKGROUND "); |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 104 | pw.print(mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, sourcePkg) |
Makoto Onuki | 2206af39 | 2017-11-21 16:25:35 -0800 | [diff] [blame] | 105 | ? "allowed]" : "disallowed]"); |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 106 | |
| 107 | if ((jobStatus.satisfiedConstraints |
| 108 | & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) { |
| 109 | pw.println(" RUNNABLE"); |
| 110 | } else { |
| 111 | pw.println(" WAITING"); |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 112 | } |
| 113 | }); |
| 114 | } |
| 115 | |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 116 | @Override |
| 117 | public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { |
| 118 | final long token = proto.start(fieldId); |
| 119 | final long mToken = proto.start(StateControllerProto.BACKGROUND); |
| 120 | |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 121 | mAppStateTracker.dumpProto(proto, |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 122 | StateControllerProto.BackgroundJobsController.FORCE_APP_STANDBY_TRACKER); |
| 123 | |
| 124 | mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> { |
| 125 | if (!jobStatus.shouldDump(filterUid)) { |
| 126 | return; |
| 127 | } |
| 128 | final long jsToken = |
| 129 | proto.start(StateControllerProto.BackgroundJobsController.TRACKED_JOBS); |
| 130 | |
| 131 | jobStatus.writeToShortProto(proto, |
| 132 | TrackedJob.INFO); |
| 133 | final int sourceUid = jobStatus.getSourceUid(); |
| 134 | proto.write(TrackedJob.SOURCE_UID, sourceUid); |
| 135 | final String sourcePkg = jobStatus.getSourcePackageName(); |
| 136 | proto.write(TrackedJob.SOURCE_PACKAGE_NAME, sourcePkg); |
| 137 | |
| 138 | proto.write(TrackedJob.IS_IN_FOREGROUND, |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 139 | mAppStateTracker.isUidActive(sourceUid)); |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 140 | proto.write(TrackedJob.IS_WHITELISTED, |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 141 | mAppStateTracker.isUidPowerSaveWhitelisted(sourceUid) || |
| 142 | mAppStateTracker.isUidTempPowerSaveWhitelisted(sourceUid)); |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 143 | |
| 144 | proto.write( |
| 145 | TrackedJob.CAN_RUN_ANY_IN_BACKGROUND, |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 146 | mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed( |
Kweku Adams | 85f2fbc | 2017-12-18 12:04:12 -0800 | [diff] [blame] | 147 | sourceUid, sourcePkg)); |
| 148 | |
| 149 | proto.write( |
| 150 | TrackedJob.ARE_CONSTRAINTS_SATISFIED, |
| 151 | (jobStatus.satisfiedConstraints & |
| 152 | JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0); |
| 153 | |
| 154 | proto.end(jsToken); |
| 155 | }); |
| 156 | |
| 157 | proto.end(mToken); |
| 158 | proto.end(token); |
| 159 | } |
| 160 | |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 161 | private void updateAllJobRestrictionsLocked() { |
| 162 | updateJobRestrictionsLocked(/*filterUid=*/ -1); |
| 163 | } |
| 164 | |
| 165 | private void updateJobRestrictionsForUidLocked(int uid) { |
| 166 | |
| 167 | // TODO Use forEachJobForSourceUid() once we have it. |
| 168 | |
| 169 | updateJobRestrictionsLocked(/*filterUid=*/ uid); |
| 170 | } |
| 171 | |
| 172 | private void updateJobRestrictionsLocked(int filterUid) { |
| 173 | final UpdateJobFunctor updateTrackedJobs = |
| 174 | new UpdateJobFunctor(filterUid); |
| 175 | |
| 176 | final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0; |
| 177 | |
| 178 | mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs); |
| 179 | |
| 180 | final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0; |
| 181 | if (DEBUG) { |
| 182 | Slog.d(LOG_TAG, String.format( |
| 183 | "Job status updated: %d/%d checked/total jobs, %d us", |
| 184 | updateTrackedJobs.mCheckedCount, |
| 185 | updateTrackedJobs.mTotalCount, |
| 186 | (time / 1000) |
| 187 | )); |
| 188 | } |
| 189 | |
| 190 | if (updateTrackedJobs.mChanged) { |
| 191 | mStateChangedListener.onControllerStateChanged(); |
| 192 | } |
| 193 | } |
| 194 | |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 195 | boolean updateSingleJobRestrictionLocked(JobStatus jobStatus) { |
| 196 | |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 197 | final int uid = jobStatus.getSourceUid(); |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 198 | final String packageName = jobStatus.getSourcePackageName(); |
| 199 | |
Makoto Onuki | e491821 | 2018-02-06 11:30:15 -0800 | [diff] [blame] | 200 | final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName, |
Makoto Onuki | 1540784 | 2018-01-19 14:23:11 -0800 | [diff] [blame] | 201 | (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) |
| 202 | != 0); |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 203 | |
| 204 | return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun); |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 205 | } |
| 206 | |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 207 | private final class UpdateJobFunctor implements JobStore.JobStatusFunctor { |
| 208 | private final int mFilterUid; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 209 | |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 210 | boolean mChanged = false; |
| 211 | int mTotalCount = 0; |
| 212 | int mCheckedCount = 0; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 213 | |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 214 | UpdateJobFunctor(int filterUid) { |
| 215 | mFilterUid = filterUid; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | @Override |
| 219 | public void process(JobStatus jobStatus) { |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 220 | mTotalCount++; |
| 221 | if ((mFilterUid > 0) && (mFilterUid != jobStatus.getSourceUid())) { |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 222 | return; |
| 223 | } |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 224 | mCheckedCount++; |
| 225 | if (updateSingleJobRestrictionLocked(jobStatus)) { |
| 226 | mChanged = true; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 227 | } |
| 228 | } |
| 229 | } |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 230 | |
| 231 | private final Listener mForceAppStandbyListener = new Listener() { |
| 232 | @Override |
Makoto Onuki | 2206af39 | 2017-11-21 16:25:35 -0800 | [diff] [blame] | 233 | public void updateAllJobs() { |
Christopher Tate | 998fb4e | 2018-01-17 14:40:38 -0800 | [diff] [blame] | 234 | synchronized (mLock) { |
| 235 | updateAllJobRestrictionsLocked(); |
| 236 | } |
Makoto Onuki | 2206af39 | 2017-11-21 16:25:35 -0800 | [diff] [blame] | 237 | } |
| 238 | |
| 239 | @Override |
| 240 | public void updateJobsForUid(int uid) { |
Christopher Tate | 998fb4e | 2018-01-17 14:40:38 -0800 | [diff] [blame] | 241 | synchronized (mLock) { |
| 242 | updateJobRestrictionsForUidLocked(uid); |
| 243 | } |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | @Override |
Makoto Onuki | 2206af39 | 2017-11-21 16:25:35 -0800 | [diff] [blame] | 247 | public void updateJobsForUidPackage(int uid, String packageName) { |
Christopher Tate | 998fb4e | 2018-01-17 14:40:38 -0800 | [diff] [blame] | 248 | synchronized (mLock) { |
| 249 | updateJobRestrictionsForUidLocked(uid); |
| 250 | } |
Makoto Onuki | 9be0140 | 2017-11-10 13:22:26 -0800 | [diff] [blame] | 251 | } |
| 252 | }; |
Suprabh Shukla | 3ac1daa | 2017-07-14 12:15:27 -0700 | [diff] [blame] | 253 | } |