blob: 90edde907f6d06da03bffe0dc2de0533ee30051b [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;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070026import android.util.ArrayMap;
27import android.util.ArraySet;
28import android.util.Log;
Christopher Tate4b425712016-06-27 16:12:41 -070029import android.util.Slog;
Tony Make46393e2016-07-29 11:30:47 +010030import android.util.SparseArray;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070031import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080032import android.util.proto.ProtoOutputStream;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080033
34import com.android.internal.annotations.VisibleForTesting;
35import com.android.server.job.JobSchedulerService;
36import com.android.server.job.StateChangedListener;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080037import com.android.server.job.StateControllerProto;
38import com.android.server.job.StateControllerProto.ContentObserverController.Observer.TriggerContentData;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080039
40import java.io.PrintWriter;
41import java.util.ArrayList;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080042
43/**
44 * Controller for monitoring changes to content URIs through a ContentObserver.
45 */
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070046public final class ContentObserverController extends StateController {
Jeff Sharkey01bb5302018-02-21 20:12:40 -070047 private static final String TAG = "JobScheduler.ContentObserver";
48 private static final boolean DEBUG = JobSchedulerService.DEBUG
49 || Log.isLoggable(TAG, Log.DEBUG);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080050
51 /**
52 * Maximum number of changing URIs we will batch together to report.
53 * XXX Should be smarter about this, restricting it by the maximum number
54 * of characters we will retain.
55 */
56 private static final int MAX_URIS_REPORTED = 50;
57
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070058 /**
59 * At this point we consider it urgent to schedule the job ASAP.
60 */
61 private static final int URIS_URGENT_THRESHOLD = 40;
62
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080063 private static final Object sCreationLock = new Object();
64 private static volatile ContentObserverController sController;
65
Dianne Hackbornf9bac162017-04-20 17:17:48 -070066 final private ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
Tony Make46393e2016-07-29 11:30:47 +010067 /**
68 * Per-userid {@link JobInfo.TriggerContentUri} keyed ContentObserver cache.
69 */
Dianne Hackbornf9bac162017-04-20 17:17:48 -070070 final SparseArray<ArrayMap<JobInfo.TriggerContentUri, ObserverInstance>> mObservers =
Tony Make46393e2016-07-29 11:30:47 +010071 new SparseArray<>();
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070072 final Handler mHandler;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080073
74 public static ContentObserverController get(JobSchedulerService taskManagerService) {
75 synchronized (sCreationLock) {
76 if (sController == null) {
77 sController = new ContentObserverController(taskManagerService,
Dianne Hackborn33d31c52016-02-16 10:30:33 -080078 taskManagerService.getContext(), taskManagerService.getLock());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080079 }
80 }
81 return sController;
82 }
83
84 @VisibleForTesting
85 public static ContentObserverController getForTesting(StateChangedListener stateChangedListener,
86 Context context) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -080087 return new ContentObserverController(stateChangedListener, context, new Object());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080088 }
89
Dianne Hackborn33d31c52016-02-16 10:30:33 -080090 private ContentObserverController(StateChangedListener stateChangedListener, Context context,
91 Object lock) {
92 super(stateChangedListener, context, lock);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070093 mHandler = new Handler(context.getMainLooper());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080094 }
95
96 @Override
Dianne Hackbornb0001f62016-02-16 10:30:33 -080097 public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080098 if (taskStatus.hasContentTriggerConstraint()) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -080099 if (taskStatus.contentObserverJobInstance == null) {
100 taskStatus.contentObserverJobInstance = new JobInstance(taskStatus);
101 }
Christopher Tate4b425712016-06-27 16:12:41 -0700102 if (DEBUG) {
103 Slog.i(TAG, "Tracking content-trigger job " + taskStatus);
104 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800105 mTrackedTasks.add(taskStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700106 taskStatus.setTrackingController(JobStatus.TRACKING_CONTENT);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800107 boolean havePendingUris = false;
108 // If there is a previous job associated with the new job, propagate over
109 // any pending content URI trigger reports.
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700110 if (taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800111 havePendingUris = true;
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800112 }
113 // If we have previously reported changed authorities/uris, then we failed
114 // to complete the job with them so will re-record them to report again.
115 if (taskStatus.changedAuthorities != null) {
116 havePendingUris = true;
117 if (taskStatus.contentObserverJobInstance.mChangedAuthorities == null) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800118 taskStatus.contentObserverJobInstance.mChangedAuthorities
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800119 = new ArraySet<>();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800120 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800121 for (String auth : taskStatus.changedAuthorities) {
122 taskStatus.contentObserverJobInstance.mChangedAuthorities.add(auth);
123 }
124 if (taskStatus.changedUris != null) {
125 if (taskStatus.contentObserverJobInstance.mChangedUris == null) {
126 taskStatus.contentObserverJobInstance.mChangedUris = new ArraySet<>();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800127 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800128 for (Uri uri : taskStatus.changedUris) {
129 taskStatus.contentObserverJobInstance.mChangedUris.add(uri);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800130 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800131 }
132 taskStatus.changedAuthorities = null;
133 taskStatus.changedUris = null;
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800134 }
135 taskStatus.changedAuthorities = null;
136 taskStatus.changedUris = null;
137 taskStatus.setContentTriggerConstraintSatisfied(havePendingUris);
138 }
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700139 if (lastJob != null && lastJob.contentObserverJobInstance != null) {
140 // And now we can detach the instance state from the last job.
141 lastJob.contentObserverJobInstance.detachLocked();
142 lastJob.contentObserverJobInstance = null;
143 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800144 }
145
146 @Override
147 public void prepareForExecutionLocked(JobStatus taskStatus) {
148 if (taskStatus.hasContentTriggerConstraint()) {
149 if (taskStatus.contentObserverJobInstance != null) {
150 taskStatus.changedUris = taskStatus.contentObserverJobInstance.mChangedUris;
151 taskStatus.changedAuthorities
152 = taskStatus.contentObserverJobInstance.mChangedAuthorities;
153 taskStatus.contentObserverJobInstance.mChangedUris = null;
154 taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800155 }
156 }
157 }
158
159 @Override
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700160 public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
161 boolean forUpdate) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700162 if (taskStatus.clearTrackingController(JobStatus.TRACKING_CONTENT)) {
163 mTrackedTasks.remove(taskStatus);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700164 if (taskStatus.contentObserverJobInstance != null) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700165 taskStatus.contentObserverJobInstance.unscheduleLocked();
166 if (incomingJob != null) {
167 if (taskStatus.contentObserverJobInstance != null
168 && taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
169 // We are stopping this job, but it is going to be replaced by this given
170 // incoming job. We want to propagate our state over to it, so we don't
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700171 // lose any content changes that had happened since the last one started.
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700172 // If there is a previous job associated with the new job, propagate over
173 // any pending content URI trigger reports.
174 if (incomingJob.contentObserverJobInstance == null) {
175 incomingJob.contentObserverJobInstance = new JobInstance(incomingJob);
176 }
177 incomingJob.contentObserverJobInstance.mChangedAuthorities
178 = taskStatus.contentObserverJobInstance.mChangedAuthorities;
179 incomingJob.contentObserverJobInstance.mChangedUris
180 = taskStatus.contentObserverJobInstance.mChangedUris;
181 taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
182 taskStatus.contentObserverJobInstance.mChangedUris = null;
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700183 }
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700184 // We won't detach the content observers here, because we want to
185 // allow them to continue monitoring so we don't miss anything... and
186 // since we are giving an incomingJob here, we know this will be
187 // immediately followed by a start tracking of that job.
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700188 } else {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700189 // But here there is no incomingJob, so nothing coming up, so time to detach.
190 taskStatus.contentObserverJobInstance.detachLocked();
191 taskStatus.contentObserverJobInstance = null;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800192 }
193 }
Christopher Tate4b425712016-06-27 16:12:41 -0700194 if (DEBUG) {
195 Slog.i(TAG, "No longer tracking job " + taskStatus);
196 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800197 }
198 }
199
200 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700201 public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800202 if (failureToReschedule.hasContentTriggerConstraint()
203 && newJob.hasContentTriggerConstraint()) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700204 // Our job has failed, and we are scheduling a new job for it.
205 // Copy the last reported content changes in to the new job, so when
206 // we schedule the new one we will pick them up and report them again.
207 newJob.changedAuthorities = failureToReschedule.changedAuthorities;
208 newJob.changedUris = failureToReschedule.changedUris;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800209 }
210 }
211
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700212 final class ObserverInstance extends ContentObserver {
Christopher Tate4b425712016-06-27 16:12:41 -0700213 final JobInfo.TriggerContentUri mUri;
Tony Make46393e2016-07-29 11:30:47 +0100214 final @UserIdInt int mUserId;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700215 final ArraySet<JobInstance> mJobs = new ArraySet<>();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800216
Tony Make46393e2016-07-29 11:30:47 +0100217 public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri,
218 @UserIdInt int userId) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800219 super(handler);
220 mUri = uri;
Tony Make46393e2016-07-29 11:30:47 +0100221 mUserId = userId;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800222 }
223
224 @Override
225 public void onChange(boolean selfChange, Uri uri) {
Christopher Tate4b425712016-06-27 16:12:41 -0700226 if (DEBUG) {
227 Slog.i(TAG, "onChange(self=" + selfChange + ") for " + uri
Tony Make46393e2016-07-29 11:30:47 +0100228 + " when mUri=" + mUri + " mUserId=" + mUserId);
Christopher Tate4b425712016-06-27 16:12:41 -0700229 }
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800230 synchronized (mLock) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800231 final int N = mJobs.size();
232 for (int i=0; i<N; i++) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700233 JobInstance inst = mJobs.valueAt(i);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800234 if (inst.mChangedUris == null) {
235 inst.mChangedUris = new ArraySet<>();
236 }
237 if (inst.mChangedUris.size() < MAX_URIS_REPORTED) {
238 inst.mChangedUris.add(uri);
239 }
240 if (inst.mChangedAuthorities == null) {
241 inst.mChangedAuthorities = new ArraySet<>();
242 }
243 inst.mChangedAuthorities.add(uri.getAuthority());
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700244 inst.scheduleLocked();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800245 }
246 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800247 }
248 }
249
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700250 static final class TriggerRunnable implements Runnable {
251 final JobInstance mInstance;
252
253 TriggerRunnable(JobInstance instance) {
254 mInstance = instance;
255 }
256
257 @Override public void run() {
258 mInstance.trigger();
259 }
260 }
261
262 final class JobInstance {
263 final ArrayList<ObserverInstance> mMyObservers = new ArrayList<>();
264 final JobStatus mJobStatus;
265 final Runnable mExecuteRunner;
266 final Runnable mTimeoutRunner;
267 ArraySet<Uri> mChangedUris;
268 ArraySet<String> mChangedAuthorities;
269
270 boolean mTriggerPending;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800271
Tony Make46393e2016-07-29 11:30:47 +0100272 // This constructor must be called with the master job scheduler lock held.
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800273 JobInstance(JobStatus jobStatus) {
274 mJobStatus = jobStatus;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700275 mExecuteRunner = new TriggerRunnable(this);
276 mTimeoutRunner = new TriggerRunnable(this);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800277 final JobInfo.TriggerContentUri[] uris = jobStatus.getJob().getTriggerContentUris();
Tony Make46393e2016-07-29 11:30:47 +0100278 final int sourceUserId = jobStatus.getSourceUserId();
279 ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
280 mObservers.get(sourceUserId);
281 if (observersOfUser == null) {
282 observersOfUser = new ArrayMap<>();
283 mObservers.put(sourceUserId, observersOfUser);
284 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800285 if (uris != null) {
286 for (JobInfo.TriggerContentUri uri : uris) {
Tony Make46393e2016-07-29 11:30:47 +0100287 ObserverInstance obs = observersOfUser.get(uri);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800288 if (obs == null) {
Tony Make46393e2016-07-29 11:30:47 +0100289 obs = new ObserverInstance(mHandler, uri, jobStatus.getSourceUserId());
290 observersOfUser.put(uri, obs);
Christopher Tate4b425712016-06-27 16:12:41 -0700291 final boolean andDescendants = (uri.getFlags() &
292 JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
293 if (DEBUG) {
294 Slog.v(TAG, "New observer " + obs + " for " + uri.getUri()
Tony Make46393e2016-07-29 11:30:47 +0100295 + " andDescendants=" + andDescendants
296 + " sourceUserId=" + sourceUserId);
Christopher Tate4b425712016-06-27 16:12:41 -0700297 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800298 mContext.getContentResolver().registerContentObserver(
299 uri.getUri(),
Christopher Tate4b425712016-06-27 16:12:41 -0700300 andDescendants,
Tony Make46393e2016-07-29 11:30:47 +0100301 obs,
302 sourceUserId
303 );
Christopher Tate4b425712016-06-27 16:12:41 -0700304 } else {
305 if (DEBUG) {
306 final boolean andDescendants = (uri.getFlags() &
307 JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
308 Slog.v(TAG, "Reusing existing observer " + obs + " for " + uri.getUri()
309 + " andDescendants=" + andDescendants);
310 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800311 }
312 obs.mJobs.add(this);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700313 mMyObservers.add(obs);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800314 }
315 }
316 }
317
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700318 void trigger() {
319 boolean reportChange = false;
320 synchronized (mLock) {
321 if (mTriggerPending) {
322 if (mJobStatus.setContentTriggerConstraintSatisfied(true)) {
323 reportChange = true;
324 }
325 unscheduleLocked();
326 }
327 }
328 // Let the scheduler know that state has changed. This may or may not result in an
329 // execution.
330 if (reportChange) {
331 mStateChangedListener.onControllerStateChanged();
332 }
333 }
334
335 void scheduleLocked() {
336 if (!mTriggerPending) {
337 mTriggerPending = true;
338 mHandler.postDelayed(mTimeoutRunner, mJobStatus.getTriggerContentMaxDelay());
339 }
340 mHandler.removeCallbacks(mExecuteRunner);
341 if (mChangedUris.size() >= URIS_URGENT_THRESHOLD) {
342 // If we start getting near the limit, GO NOW!
343 mHandler.post(mExecuteRunner);
344 } else {
345 mHandler.postDelayed(mExecuteRunner, mJobStatus.getTriggerContentUpdateDelay());
346 }
347 }
348
349 void unscheduleLocked() {
350 if (mTriggerPending) {
351 mHandler.removeCallbacks(mExecuteRunner);
352 mHandler.removeCallbacks(mTimeoutRunner);
353 mTriggerPending = false;
354 }
355 }
356
357 void detachLocked() {
358 final int N = mMyObservers.size();
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800359 for (int i=0; i<N; i++) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700360 final ObserverInstance obs = mMyObservers.get(i);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800361 obs.mJobs.remove(this);
362 if (obs.mJobs.size() == 0) {
Christopher Tate4b425712016-06-27 16:12:41 -0700363 if (DEBUG) {
364 Slog.i(TAG, "Unregistering observer " + obs + " for " + obs.mUri.getUri());
365 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800366 mContext.getContentResolver().unregisterContentObserver(obs);
Tony Make46393e2016-07-29 11:30:47 +0100367 ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observerOfUser =
368 mObservers.get(obs.mUserId);
369 if (observerOfUser != null) {
370 observerOfUser.remove(obs.mUri);
371 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800372 }
373 }
374 }
375 }
376
377 @Override
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700378 public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700379 pw.println("Content:");
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700380 for (int i = 0; i < mTrackedTasks.size(); i++) {
381 JobStatus js = mTrackedTasks.valueAt(i);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700382 if (!js.shouldDump(filterUid)) {
383 continue;
384 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700385 pw.print(" #");
386 js.printUniqueId(pw);
387 pw.print(" from ");
388 UserHandle.formatUid(pw, js.getSourceUid());
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700389 pw.println();
390 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800391 int N = mObservers.size();
392 if (N > 0) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700393 pw.println(" Observers:");
Tony Make46393e2016-07-29 11:30:47 +0100394 for (int userIdx = 0; userIdx < N; userIdx++) {
395 final int userId = mObservers.keyAt(userIdx);
396 ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
397 mObservers.get(userId);
398 int numbOfObserversPerUser = observersOfUser.size();
399 for (int observerIdx = 0 ; observerIdx < numbOfObserversPerUser; observerIdx++) {
400 ObserverInstance obs = observersOfUser.valueAt(observerIdx);
401 int M = obs.mJobs.size();
402 boolean shouldDump = false;
403 for (int j = 0; j < M; j++) {
404 JobInstance inst = obs.mJobs.valueAt(j);
405 if (inst.mJobStatus.shouldDump(filterUid)) {
406 shouldDump = true;
407 break;
408 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700409 }
Tony Make46393e2016-07-29 11:30:47 +0100410 if (!shouldDump) {
411 continue;
412 }
413 pw.print(" ");
414 JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx);
415 pw.print(trigger.getUri());
416 pw.print(" 0x");
417 pw.print(Integer.toHexString(trigger.getFlags()));
418 pw.print(" (");
419 pw.print(System.identityHashCode(obs));
420 pw.println("):");
421 pw.println(" Jobs:");
422 for (int j = 0; j < M; j++) {
423 JobInstance inst = obs.mJobs.valueAt(j);
424 pw.print(" #");
425 inst.mJobStatus.printUniqueId(pw);
426 pw.print(" from ");
427 UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid());
428 if (inst.mChangedAuthorities != null) {
429 pw.println(":");
430 if (inst.mTriggerPending) {
431 pw.print(" Trigger pending: update=");
432 TimeUtils.formatDuration(
433 inst.mJobStatus.getTriggerContentUpdateDelay(), pw);
434 pw.print(", max=");
435 TimeUtils.formatDuration(
436 inst.mJobStatus.getTriggerContentMaxDelay(), pw);
437 pw.println();
438 }
439 pw.println(" Changed Authorities:");
440 for (int k = 0; k < inst.mChangedAuthorities.size(); k++) {
441 pw.print(" ");
442 pw.println(inst.mChangedAuthorities.valueAt(k));
443 }
444 if (inst.mChangedUris != null) {
445 pw.println(" Changed URIs:");
446 for (int k = 0; k < inst.mChangedUris.size(); k++) {
447 pw.print(" ");
448 pw.println(inst.mChangedUris.valueAt(k));
449 }
450 }
451 } else {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700452 pw.println();
453 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800454 }
455 }
456 }
457 }
458 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800459
460 @Override
461 public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
462 final long token = proto.start(fieldId);
463 final long mToken = proto.start(StateControllerProto.CONTENT_OBSERVER);
464
465 for (int i = 0; i < mTrackedTasks.size(); i++) {
466 JobStatus js = mTrackedTasks.valueAt(i);
467 if (!js.shouldDump(filterUid)) {
468 continue;
469 }
470 final long jsToken =
471 proto.start(StateControllerProto.ContentObserverController.TRACKED_JOBS);
472 js.writeToShortProto(proto,
473 StateControllerProto.ContentObserverController.TrackedJob.INFO);
474 proto.write(StateControllerProto.ContentObserverController.TrackedJob.SOURCE_UID,
475 js.getSourceUid());
476 proto.end(jsToken);
477 }
478
479 final int n = mObservers.size();
480 for (int userIdx = 0; userIdx < n; userIdx++) {
481 final long oToken =
482 proto.start(StateControllerProto.ContentObserverController.OBSERVERS);
483 final int userId = mObservers.keyAt(userIdx);
484
485 proto.write(StateControllerProto.ContentObserverController.Observer.USER_ID, userId);
486
487 ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
488 mObservers.get(userId);
489 int numbOfObserversPerUser = observersOfUser.size();
490 for (int observerIdx = 0 ; observerIdx < numbOfObserversPerUser; observerIdx++) {
491 ObserverInstance obs = observersOfUser.valueAt(observerIdx);
492 int m = obs.mJobs.size();
493 boolean shouldDump = false;
494 for (int j = 0; j < m; j++) {
495 JobInstance inst = obs.mJobs.valueAt(j);
496 if (inst.mJobStatus.shouldDump(filterUid)) {
497 shouldDump = true;
498 break;
499 }
500 }
501 if (!shouldDump) {
502 continue;
503 }
504 final long tToken = proto.start(
505 StateControllerProto.ContentObserverController.Observer.TRIGGERS);
506
507 JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx);
508 Uri u = trigger.getUri();
509 if (u != null) {
510 proto.write(TriggerContentData.URI, u.toString());
511 }
512 proto.write(TriggerContentData.FLAGS, trigger.getFlags());
513
514 for (int j = 0; j < m; j++) {
515 final long jToken = proto.start(TriggerContentData.JOBS);
516 JobInstance inst = obs.mJobs.valueAt(j);
517
518 inst.mJobStatus.writeToShortProto(proto, TriggerContentData.JobInstance.INFO);
519 proto.write(TriggerContentData.JobInstance.SOURCE_UID,
520 inst.mJobStatus.getSourceUid());
521
522 if (inst.mChangedAuthorities == null) {
523 proto.end(jToken);
524 continue;
525 }
526 if (inst.mTriggerPending) {
527 proto.write(TriggerContentData.JobInstance.TRIGGER_CONTENT_UPDATE_DELAY_MS,
528 inst.mJobStatus.getTriggerContentUpdateDelay());
529 proto.write(TriggerContentData.JobInstance.TRIGGER_CONTENT_MAX_DELAY_MS,
530 inst.mJobStatus.getTriggerContentMaxDelay());
531 }
532 for (int k = 0; k < inst.mChangedAuthorities.size(); k++) {
533 proto.write(TriggerContentData.JobInstance.CHANGED_AUTHORITIES,
534 inst.mChangedAuthorities.valueAt(k));
535 }
536 if (inst.mChangedUris != null) {
537 for (int k = 0; k < inst.mChangedUris.size(); k++) {
538 u = inst.mChangedUris.valueAt(k);
539 if (u != null) {
540 proto.write(TriggerContentData.JobInstance.CHANGED_URIS,
541 u.toString());
542 }
543 }
544 }
545
546 proto.end(jToken);
547 }
548
549 proto.end(tToken);
550 }
551
552 proto.end(oToken);
553 }
554
555 proto.end(mToken);
556 proto.end(token);
557 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800558}