blob: 7394e23f297ac42134f9a1d0278680443c7343f5 [file] [log] [blame]
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001/*
2 * Copyright (C) 2016 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
17package com.android.server.job.controllers;
18
Tony Make46393e2016-07-29 11:30:47 +010019import android.annotation.UserIdInt;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080020import android.app.job.JobInfo;
21import android.content.Context;
22import android.database.ContentObserver;
23import android.net.Uri;
24import android.os.Handler;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070025import android.os.UserHandle;
Christopher Tate4b425712016-06-27 16:12:41 -070026import android.util.Slog;
Tony Make46393e2016-07-29 11:30:47 +010027import android.util.SparseArray;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070028import android.util.TimeUtils;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080029import android.util.ArrayMap;
30import android.util.ArraySet;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080031import android.util.proto.ProtoOutputStream;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080032
33import com.android.internal.annotations.VisibleForTesting;
34import com.android.server.job.JobSchedulerService;
35import com.android.server.job.StateChangedListener;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080036import com.android.server.job.StateControllerProto;
37import com.android.server.job.StateControllerProto.ContentObserverController.Observer.TriggerContentData;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080038
39import java.io.PrintWriter;
40import java.util.ArrayList;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080041
42/**
43 * Controller for monitoring changes to content URIs through a ContentObserver.
44 */
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070045public final class ContentObserverController extends StateController {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080046 private static final String TAG = "JobScheduler.Content";
Christopher Tate4b425712016-06-27 16:12:41 -070047 private static final boolean DEBUG = false;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080048
49 /**
50 * Maximum number of changing URIs we will batch together to report.
51 * XXX Should be smarter about this, restricting it by the maximum number
52 * of characters we will retain.
53 */
54 private static final int MAX_URIS_REPORTED = 50;
55
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070056 /**
57 * At this point we consider it urgent to schedule the job ASAP.
58 */
59 private static final int URIS_URGENT_THRESHOLD = 40;
60
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080061 private static final Object sCreationLock = new Object();
62 private static volatile ContentObserverController sController;
63
Dianne Hackbornf9bac162017-04-20 17:17:48 -070064 final private ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
Tony Make46393e2016-07-29 11:30:47 +010065 /**
66 * Per-userid {@link JobInfo.TriggerContentUri} keyed ContentObserver cache.
67 */
Dianne Hackbornf9bac162017-04-20 17:17:48 -070068 final SparseArray<ArrayMap<JobInfo.TriggerContentUri, ObserverInstance>> mObservers =
Tony Make46393e2016-07-29 11:30:47 +010069 new SparseArray<>();
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070070 final Handler mHandler;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080071
72 public static ContentObserverController get(JobSchedulerService taskManagerService) {
73 synchronized (sCreationLock) {
74 if (sController == null) {
75 sController = new ContentObserverController(taskManagerService,
Dianne Hackborn33d31c52016-02-16 10:30:33 -080076 taskManagerService.getContext(), taskManagerService.getLock());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080077 }
78 }
79 return sController;
80 }
81
82 @VisibleForTesting
83 public static ContentObserverController getForTesting(StateChangedListener stateChangedListener,
84 Context context) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -080085 return new ContentObserverController(stateChangedListener, context, new Object());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080086 }
87
Dianne Hackborn33d31c52016-02-16 10:30:33 -080088 private ContentObserverController(StateChangedListener stateChangedListener, Context context,
89 Object lock) {
90 super(stateChangedListener, context, lock);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070091 mHandler = new Handler(context.getMainLooper());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080092 }
93
94 @Override
Dianne Hackbornb0001f62016-02-16 10:30:33 -080095 public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080096 if (taskStatus.hasContentTriggerConstraint()) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -080097 if (taskStatus.contentObserverJobInstance == null) {
98 taskStatus.contentObserverJobInstance = new JobInstance(taskStatus);
99 }
Christopher Tate4b425712016-06-27 16:12:41 -0700100 if (DEBUG) {
101 Slog.i(TAG, "Tracking content-trigger job " + taskStatus);
102 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800103 mTrackedTasks.add(taskStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700104 taskStatus.setTrackingController(JobStatus.TRACKING_CONTENT);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800105 boolean havePendingUris = false;
106 // If there is a previous job associated with the new job, propagate over
107 // any pending content URI trigger reports.
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700108 if (taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800109 havePendingUris = true;
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800110 }
111 // If we have previously reported changed authorities/uris, then we failed
112 // to complete the job with them so will re-record them to report again.
113 if (taskStatus.changedAuthorities != null) {
114 havePendingUris = true;
115 if (taskStatus.contentObserverJobInstance.mChangedAuthorities == null) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800116 taskStatus.contentObserverJobInstance.mChangedAuthorities
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800117 = new ArraySet<>();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800118 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800119 for (String auth : taskStatus.changedAuthorities) {
120 taskStatus.contentObserverJobInstance.mChangedAuthorities.add(auth);
121 }
122 if (taskStatus.changedUris != null) {
123 if (taskStatus.contentObserverJobInstance.mChangedUris == null) {
124 taskStatus.contentObserverJobInstance.mChangedUris = new ArraySet<>();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800125 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800126 for (Uri uri : taskStatus.changedUris) {
127 taskStatus.contentObserverJobInstance.mChangedUris.add(uri);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800128 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800129 }
130 taskStatus.changedAuthorities = null;
131 taskStatus.changedUris = null;
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800132 }
133 taskStatus.changedAuthorities = null;
134 taskStatus.changedUris = null;
135 taskStatus.setContentTriggerConstraintSatisfied(havePendingUris);
136 }
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700137 if (lastJob != null && lastJob.contentObserverJobInstance != null) {
138 // And now we can detach the instance state from the last job.
139 lastJob.contentObserverJobInstance.detachLocked();
140 lastJob.contentObserverJobInstance = null;
141 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800142 }
143
144 @Override
145 public void prepareForExecutionLocked(JobStatus taskStatus) {
146 if (taskStatus.hasContentTriggerConstraint()) {
147 if (taskStatus.contentObserverJobInstance != null) {
148 taskStatus.changedUris = taskStatus.contentObserverJobInstance.mChangedUris;
149 taskStatus.changedAuthorities
150 = taskStatus.contentObserverJobInstance.mChangedAuthorities;
151 taskStatus.contentObserverJobInstance.mChangedUris = null;
152 taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800153 }
154 }
155 }
156
157 @Override
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700158 public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
159 boolean forUpdate) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700160 if (taskStatus.clearTrackingController(JobStatus.TRACKING_CONTENT)) {
161 mTrackedTasks.remove(taskStatus);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700162 if (taskStatus.contentObserverJobInstance != null) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700163 taskStatus.contentObserverJobInstance.unscheduleLocked();
164 if (incomingJob != null) {
165 if (taskStatus.contentObserverJobInstance != null
166 && taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
167 // We are stopping this job, but it is going to be replaced by this given
168 // incoming job. We want to propagate our state over to it, so we don't
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700169 // lose any content changes that had happened since the last one started.
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700170 // If there is a previous job associated with the new job, propagate over
171 // any pending content URI trigger reports.
172 if (incomingJob.contentObserverJobInstance == null) {
173 incomingJob.contentObserverJobInstance = new JobInstance(incomingJob);
174 }
175 incomingJob.contentObserverJobInstance.mChangedAuthorities
176 = taskStatus.contentObserverJobInstance.mChangedAuthorities;
177 incomingJob.contentObserverJobInstance.mChangedUris
178 = taskStatus.contentObserverJobInstance.mChangedUris;
179 taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
180 taskStatus.contentObserverJobInstance.mChangedUris = null;
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700181 }
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700182 // We won't detach the content observers here, because we want to
183 // allow them to continue monitoring so we don't miss anything... and
184 // since we are giving an incomingJob here, we know this will be
185 // immediately followed by a start tracking of that job.
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700186 } else {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700187 // But here there is no incomingJob, so nothing coming up, so time to detach.
188 taskStatus.contentObserverJobInstance.detachLocked();
189 taskStatus.contentObserverJobInstance = null;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800190 }
191 }
Christopher Tate4b425712016-06-27 16:12:41 -0700192 if (DEBUG) {
193 Slog.i(TAG, "No longer tracking job " + taskStatus);
194 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800195 }
196 }
197
198 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700199 public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800200 if (failureToReschedule.hasContentTriggerConstraint()
201 && newJob.hasContentTriggerConstraint()) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700202 // Our job has failed, and we are scheduling a new job for it.
203 // Copy the last reported content changes in to the new job, so when
204 // we schedule the new one we will pick them up and report them again.
205 newJob.changedAuthorities = failureToReschedule.changedAuthorities;
206 newJob.changedUris = failureToReschedule.changedUris;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800207 }
208 }
209
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700210 final class ObserverInstance extends ContentObserver {
Christopher Tate4b425712016-06-27 16:12:41 -0700211 final JobInfo.TriggerContentUri mUri;
Tony Make46393e2016-07-29 11:30:47 +0100212 final @UserIdInt int mUserId;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700213 final ArraySet<JobInstance> mJobs = new ArraySet<>();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800214
Tony Make46393e2016-07-29 11:30:47 +0100215 public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri,
216 @UserIdInt int userId) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800217 super(handler);
218 mUri = uri;
Tony Make46393e2016-07-29 11:30:47 +0100219 mUserId = userId;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800220 }
221
222 @Override
223 public void onChange(boolean selfChange, Uri uri) {
Christopher Tate4b425712016-06-27 16:12:41 -0700224 if (DEBUG) {
225 Slog.i(TAG, "onChange(self=" + selfChange + ") for " + uri
Tony Make46393e2016-07-29 11:30:47 +0100226 + " when mUri=" + mUri + " mUserId=" + mUserId);
Christopher Tate4b425712016-06-27 16:12:41 -0700227 }
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800228 synchronized (mLock) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800229 final int N = mJobs.size();
230 for (int i=0; i<N; i++) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700231 JobInstance inst = mJobs.valueAt(i);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800232 if (inst.mChangedUris == null) {
233 inst.mChangedUris = new ArraySet<>();
234 }
235 if (inst.mChangedUris.size() < MAX_URIS_REPORTED) {
236 inst.mChangedUris.add(uri);
237 }
238 if (inst.mChangedAuthorities == null) {
239 inst.mChangedAuthorities = new ArraySet<>();
240 }
241 inst.mChangedAuthorities.add(uri.getAuthority());
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700242 inst.scheduleLocked();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800243 }
244 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800245 }
246 }
247
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700248 static final class TriggerRunnable implements Runnable {
249 final JobInstance mInstance;
250
251 TriggerRunnable(JobInstance instance) {
252 mInstance = instance;
253 }
254
255 @Override public void run() {
256 mInstance.trigger();
257 }
258 }
259
260 final class JobInstance {
261 final ArrayList<ObserverInstance> mMyObservers = new ArrayList<>();
262 final JobStatus mJobStatus;
263 final Runnable mExecuteRunner;
264 final Runnable mTimeoutRunner;
265 ArraySet<Uri> mChangedUris;
266 ArraySet<String> mChangedAuthorities;
267
268 boolean mTriggerPending;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800269
Tony Make46393e2016-07-29 11:30:47 +0100270 // This constructor must be called with the master job scheduler lock held.
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800271 JobInstance(JobStatus jobStatus) {
272 mJobStatus = jobStatus;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700273 mExecuteRunner = new TriggerRunnable(this);
274 mTimeoutRunner = new TriggerRunnable(this);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800275 final JobInfo.TriggerContentUri[] uris = jobStatus.getJob().getTriggerContentUris();
Tony Make46393e2016-07-29 11:30:47 +0100276 final int sourceUserId = jobStatus.getSourceUserId();
277 ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
278 mObservers.get(sourceUserId);
279 if (observersOfUser == null) {
280 observersOfUser = new ArrayMap<>();
281 mObservers.put(sourceUserId, observersOfUser);
282 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800283 if (uris != null) {
284 for (JobInfo.TriggerContentUri uri : uris) {
Tony Make46393e2016-07-29 11:30:47 +0100285 ObserverInstance obs = observersOfUser.get(uri);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800286 if (obs == null) {
Tony Make46393e2016-07-29 11:30:47 +0100287 obs = new ObserverInstance(mHandler, uri, jobStatus.getSourceUserId());
288 observersOfUser.put(uri, obs);
Christopher Tate4b425712016-06-27 16:12:41 -0700289 final boolean andDescendants = (uri.getFlags() &
290 JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
291 if (DEBUG) {
292 Slog.v(TAG, "New observer " + obs + " for " + uri.getUri()
Tony Make46393e2016-07-29 11:30:47 +0100293 + " andDescendants=" + andDescendants
294 + " sourceUserId=" + sourceUserId);
Christopher Tate4b425712016-06-27 16:12:41 -0700295 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800296 mContext.getContentResolver().registerContentObserver(
297 uri.getUri(),
Christopher Tate4b425712016-06-27 16:12:41 -0700298 andDescendants,
Tony Make46393e2016-07-29 11:30:47 +0100299 obs,
300 sourceUserId
301 );
Christopher Tate4b425712016-06-27 16:12:41 -0700302 } else {
303 if (DEBUG) {
304 final boolean andDescendants = (uri.getFlags() &
305 JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
306 Slog.v(TAG, "Reusing existing observer " + obs + " for " + uri.getUri()
307 + " andDescendants=" + andDescendants);
308 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800309 }
310 obs.mJobs.add(this);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700311 mMyObservers.add(obs);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800312 }
313 }
314 }
315
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700316 void trigger() {
317 boolean reportChange = false;
318 synchronized (mLock) {
319 if (mTriggerPending) {
320 if (mJobStatus.setContentTriggerConstraintSatisfied(true)) {
321 reportChange = true;
322 }
323 unscheduleLocked();
324 }
325 }
326 // Let the scheduler know that state has changed. This may or may not result in an
327 // execution.
328 if (reportChange) {
329 mStateChangedListener.onControllerStateChanged();
330 }
331 }
332
333 void scheduleLocked() {
334 if (!mTriggerPending) {
335 mTriggerPending = true;
336 mHandler.postDelayed(mTimeoutRunner, mJobStatus.getTriggerContentMaxDelay());
337 }
338 mHandler.removeCallbacks(mExecuteRunner);
339 if (mChangedUris.size() >= URIS_URGENT_THRESHOLD) {
340 // If we start getting near the limit, GO NOW!
341 mHandler.post(mExecuteRunner);
342 } else {
343 mHandler.postDelayed(mExecuteRunner, mJobStatus.getTriggerContentUpdateDelay());
344 }
345 }
346
347 void unscheduleLocked() {
348 if (mTriggerPending) {
349 mHandler.removeCallbacks(mExecuteRunner);
350 mHandler.removeCallbacks(mTimeoutRunner);
351 mTriggerPending = false;
352 }
353 }
354
355 void detachLocked() {
356 final int N = mMyObservers.size();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800357 for (int i=0; i<N; i++) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700358 final ObserverInstance obs = mMyObservers.get(i);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800359 obs.mJobs.remove(this);
360 if (obs.mJobs.size() == 0) {
Christopher Tate4b425712016-06-27 16:12:41 -0700361 if (DEBUG) {
362 Slog.i(TAG, "Unregistering observer " + obs + " for " + obs.mUri.getUri());
363 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800364 mContext.getContentResolver().unregisterContentObserver(obs);
Tony Make46393e2016-07-29 11:30:47 +0100365 ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observerOfUser =
366 mObservers.get(obs.mUserId);
367 if (observerOfUser != null) {
368 observerOfUser.remove(obs.mUri);
369 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800370 }
371 }
372 }
373 }
374
375 @Override
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700376 public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700377 pw.println("Content:");
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700378 for (int i = 0; i < mTrackedTasks.size(); i++) {
379 JobStatus js = mTrackedTasks.valueAt(i);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700380 if (!js.shouldDump(filterUid)) {
381 continue;
382 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700383 pw.print(" #");
384 js.printUniqueId(pw);
385 pw.print(" from ");
386 UserHandle.formatUid(pw, js.getSourceUid());
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700387 pw.println();
388 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800389 int N = mObservers.size();
390 if (N > 0) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700391 pw.println(" Observers:");
Tony Make46393e2016-07-29 11:30:47 +0100392 for (int userIdx = 0; userIdx < N; userIdx++) {
393 final int userId = mObservers.keyAt(userIdx);
394 ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
395 mObservers.get(userId);
396 int numbOfObserversPerUser = observersOfUser.size();
397 for (int observerIdx = 0 ; observerIdx < numbOfObserversPerUser; observerIdx++) {
398 ObserverInstance obs = observersOfUser.valueAt(observerIdx);
399 int M = obs.mJobs.size();
400 boolean shouldDump = false;
401 for (int j = 0; j < M; j++) {
402 JobInstance inst = obs.mJobs.valueAt(j);
403 if (inst.mJobStatus.shouldDump(filterUid)) {
404 shouldDump = true;
405 break;
406 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700407 }
Tony Make46393e2016-07-29 11:30:47 +0100408 if (!shouldDump) {
409 continue;
410 }
411 pw.print(" ");
412 JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx);
413 pw.print(trigger.getUri());
414 pw.print(" 0x");
415 pw.print(Integer.toHexString(trigger.getFlags()));
416 pw.print(" (");
417 pw.print(System.identityHashCode(obs));
418 pw.println("):");
419 pw.println(" Jobs:");
420 for (int j = 0; j < M; j++) {
421 JobInstance inst = obs.mJobs.valueAt(j);
422 pw.print(" #");
423 inst.mJobStatus.printUniqueId(pw);
424 pw.print(" from ");
425 UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid());
426 if (inst.mChangedAuthorities != null) {
427 pw.println(":");
428 if (inst.mTriggerPending) {
429 pw.print(" Trigger pending: update=");
430 TimeUtils.formatDuration(
431 inst.mJobStatus.getTriggerContentUpdateDelay(), pw);
432 pw.print(", max=");
433 TimeUtils.formatDuration(
434 inst.mJobStatus.getTriggerContentMaxDelay(), pw);
435 pw.println();
436 }
437 pw.println(" Changed Authorities:");
438 for (int k = 0; k < inst.mChangedAuthorities.size(); k++) {
439 pw.print(" ");
440 pw.println(inst.mChangedAuthorities.valueAt(k));
441 }
442 if (inst.mChangedUris != null) {
443 pw.println(" Changed URIs:");
444 for (int k = 0; k < inst.mChangedUris.size(); k++) {
445 pw.print(" ");
446 pw.println(inst.mChangedUris.valueAt(k));
447 }
448 }
449 } else {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700450 pw.println();
451 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800452 }
453 }
454 }
455 }
456 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800457
458 @Override
459 public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
460 final long token = proto.start(fieldId);
461 final long mToken = proto.start(StateControllerProto.CONTENT_OBSERVER);
462
463 for (int i = 0; i < mTrackedTasks.size(); i++) {
464 JobStatus js = mTrackedTasks.valueAt(i);
465 if (!js.shouldDump(filterUid)) {
466 continue;
467 }
468 final long jsToken =
469 proto.start(StateControllerProto.ContentObserverController.TRACKED_JOBS);
470 js.writeToShortProto(proto,
471 StateControllerProto.ContentObserverController.TrackedJob.INFO);
472 proto.write(StateControllerProto.ContentObserverController.TrackedJob.SOURCE_UID,
473 js.getSourceUid());
474 proto.end(jsToken);
475 }
476
477 final int n = mObservers.size();
478 for (int userIdx = 0; userIdx < n; userIdx++) {
479 final long oToken =
480 proto.start(StateControllerProto.ContentObserverController.OBSERVERS);
481 final int userId = mObservers.keyAt(userIdx);
482
483 proto.write(StateControllerProto.ContentObserverController.Observer.USER_ID, userId);
484
485 ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
486 mObservers.get(userId);
487 int numbOfObserversPerUser = observersOfUser.size();
488 for (int observerIdx = 0 ; observerIdx < numbOfObserversPerUser; observerIdx++) {
489 ObserverInstance obs = observersOfUser.valueAt(observerIdx);
490 int m = obs.mJobs.size();
491 boolean shouldDump = false;
492 for (int j = 0; j < m; j++) {
493 JobInstance inst = obs.mJobs.valueAt(j);
494 if (inst.mJobStatus.shouldDump(filterUid)) {
495 shouldDump = true;
496 break;
497 }
498 }
499 if (!shouldDump) {
500 continue;
501 }
502 final long tToken = proto.start(
503 StateControllerProto.ContentObserverController.Observer.TRIGGERS);
504
505 JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx);
506 Uri u = trigger.getUri();
507 if (u != null) {
508 proto.write(TriggerContentData.URI, u.toString());
509 }
510 proto.write(TriggerContentData.FLAGS, trigger.getFlags());
511
512 for (int j = 0; j < m; j++) {
513 final long jToken = proto.start(TriggerContentData.JOBS);
514 JobInstance inst = obs.mJobs.valueAt(j);
515
516 inst.mJobStatus.writeToShortProto(proto, TriggerContentData.JobInstance.INFO);
517 proto.write(TriggerContentData.JobInstance.SOURCE_UID,
518 inst.mJobStatus.getSourceUid());
519
520 if (inst.mChangedAuthorities == null) {
521 proto.end(jToken);
522 continue;
523 }
524 if (inst.mTriggerPending) {
525 proto.write(TriggerContentData.JobInstance.TRIGGER_CONTENT_UPDATE_DELAY_MS,
526 inst.mJobStatus.getTriggerContentUpdateDelay());
527 proto.write(TriggerContentData.JobInstance.TRIGGER_CONTENT_MAX_DELAY_MS,
528 inst.mJobStatus.getTriggerContentMaxDelay());
529 }
530 for (int k = 0; k < inst.mChangedAuthorities.size(); k++) {
531 proto.write(TriggerContentData.JobInstance.CHANGED_AUTHORITIES,
532 inst.mChangedAuthorities.valueAt(k));
533 }
534 if (inst.mChangedUris != null) {
535 for (int k = 0; k < inst.mChangedUris.size(); k++) {
536 u = inst.mChangedUris.valueAt(k);
537 if (u != null) {
538 proto.write(TriggerContentData.JobInstance.CHANGED_URIS,
539 u.toString());
540 }
541 }
542 }
543
544 proto.end(jToken);
545 }
546
547 proto.end(tToken);
548 }
549
550 proto.end(oToken);
551 }
552
553 proto.end(mToken);
554 proto.end(token);
555 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800556}