blob: 76fe560ed902304fb39901fbf8ecdb6df3b4ad3a [file] [log] [blame]
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001package android.os;
2
Narayan Kamathf0202a92017-12-07 15:45:33 +00003import android.annotation.Nullable;
Narayan Kamathdfcdcc92018-01-25 19:58:52 +00004import android.annotation.SystemApi;
Jeff Sharkeyc6091162018-06-29 17:15:40 -06005import android.annotation.TestApi;
Narayan Kamath8d828252018-01-11 15:22:37 +00006import android.content.Context;
Netta P958d0a52017-02-07 11:20:55 -08007import android.os.WorkSourceProto;
Narayan Kamath8d828252018-01-11 15:22:37 +00008import android.provider.Settings;
9import android.provider.Settings.Global;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080010import android.util.Log;
Netta P958d0a52017-02-07 11:20:55 -080011import android.util.proto.ProtoOutputStream;
Jeff Brown96307042012-07-27 15:51:34 -070012
Narayan Kamathdfcdcc92018-01-25 19:58:52 +000013import com.android.internal.annotations.VisibleForTesting;
14
Narayan Kamathf0202a92017-12-07 15:45:33 +000015import java.util.ArrayList;
Jeff Brown96307042012-07-27 15:51:34 -070016import java.util.Arrays;
17
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070018/**
19 * Describes the source of some work that may be done by someone else.
20 * Currently the public representation of what a work source is is not
21 * defined; this is an opaque container.
22 */
23public class WorkSource implements Parcelable {
Dianne Hackborn002a54e2013-01-10 17:34:55 -080024 static final String TAG = "WorkSource";
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080025 static final boolean DEBUG = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080026
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070027 int mNum;
28 int[] mUids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080029 String[] mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070030
Narayan Kamathf0202a92017-12-07 15:45:33 +000031 private ArrayList<WorkChain> mChains;
32
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070033 /**
34 * Internal statics to avoid object allocations in some operations.
35 * The WorkSource object itself is not thread safe, but we need to
36 * hold sTmpWorkSource lock while working with these statics.
37 */
38 static final WorkSource sTmpWorkSource = new WorkSource(0);
39 /**
40 * For returning newbie work from a modification operation.
41 */
42 static WorkSource sNewbWork;
43 /**
44 * For returning gone work form a modification operation.
45 */
46 static WorkSource sGoneWork;
47
48 /**
49 * Create an empty work source.
50 */
51 public WorkSource() {
52 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000053 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070054 }
55
56 /**
57 * Create a new WorkSource that is a copy of an existing one.
58 * If <var>orig</var> is null, an empty WorkSource is created.
59 */
60 public WorkSource(WorkSource orig) {
61 if (orig == null) {
62 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000063 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070064 return;
65 }
66 mNum = orig.mNum;
67 if (orig.mUids != null) {
68 mUids = orig.mUids.clone();
Dianne Hackborn002a54e2013-01-10 17:34:55 -080069 mNames = orig.mNames != null ? orig.mNames.clone() : null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070070 } else {
71 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080072 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070073 }
Narayan Kamathf0202a92017-12-07 15:45:33 +000074
75 if (orig.mChains != null) {
76 // Make a copy of all WorkChains that exist on |orig| since they are mutable.
77 mChains = new ArrayList<>(orig.mChains.size());
78 for (WorkChain chain : orig.mChains) {
79 mChains.add(new WorkChain(chain));
80 }
81 } else {
82 mChains = null;
83 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070084 }
85
86 /** @hide */
Jeff Sharkeyc6091162018-06-29 17:15:40 -060087 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070088 public WorkSource(int uid) {
89 mNum = 1;
90 mUids = new int[] { uid, 0 };
Dianne Hackborn002a54e2013-01-10 17:34:55 -080091 mNames = null;
Narayan Kamathf0202a92017-12-07 15:45:33 +000092 mChains = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080093 }
94
95 /** @hide */
96 public WorkSource(int uid, String name) {
97 if (name == null) {
98 throw new NullPointerException("Name can't be null");
99 }
100 mNum = 1;
101 mUids = new int[] { uid, 0 };
102 mNames = new String[] { name, null };
Narayan Kamathf0202a92017-12-07 15:45:33 +0000103 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700104 }
105
106 WorkSource(Parcel in) {
107 mNum = in.readInt();
108 mUids = in.createIntArray();
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800109 mNames = in.createStringArray();
Narayan Kamathf0202a92017-12-07 15:45:33 +0000110
111 int numChains = in.readInt();
112 if (numChains > 0) {
113 mChains = new ArrayList<>(numChains);
114 in.readParcelableList(mChains, WorkChain.class.getClassLoader());
115 } else {
116 mChains = null;
117 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700118 }
119
Narayan Kamath8d828252018-01-11 15:22:37 +0000120 /**
121 * Whether system services should create {@code WorkChains} (wherever possible) in the place
122 * of flat UID lists.
123 *
124 * @hide
125 */
126 public static boolean isChainedBatteryAttributionEnabled(Context context) {
127 return Settings.Global.getInt(context.getContentResolver(),
128 Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, 0) == 1;
129 }
130
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700131 /** @hide */
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600132 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700133 public int size() {
134 return mNum;
135 }
136
137 /** @hide */
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600138 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700139 public int get(int index) {
140 return mUids[index];
141 }
142
Marcin Oczeretko53295402018-12-17 12:44:35 +0000143 /**
144 * Return the UID to which this WorkSource should be attributed to, i.e, the UID that
145 * initiated the work and not the UID performing it. If the WorkSource has no UIDs, returns -1
146 * instead.
147 *
148 * @hide
149 */
150 public int getAttributionUid() {
151 if (isEmpty()) {
152 return -1;
153 }
154
155 return mNum > 0 ? mUids[0] : mChains.get(0).getAttributionUid();
156 }
157
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800158 /** @hide */
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600159 @TestApi
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800160 public String getName(int index) {
161 return mNames != null ? mNames[index] : null;
162 }
163
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700164 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000165 * Clear names from this WorkSource. Uids are left intact. WorkChains if any, are left
166 * intact.
David Christie6ab22842013-09-13 17:11:53 -0700167 *
168 * <p>Useful when combining with another WorkSource that doesn't have names.
169 * @hide
170 */
171 public void clearNames() {
David Christiea31510e2013-09-20 10:44:01 -0700172 if (mNames != null) {
173 mNames = null;
174 // Clear out any duplicate uids now that we don't have names to disambiguate them.
175 int destIndex = 1;
176 int newNum = mNum;
177 for (int sourceIndex = 1; sourceIndex < mNum; sourceIndex++) {
178 if (mUids[sourceIndex] == mUids[sourceIndex - 1]) {
179 newNum--;
180 } else {
181 mUids[destIndex] = mUids[sourceIndex];
182 destIndex++;
183 }
184 }
185 mNum = newNum;
186 }
David Christie6ab22842013-09-13 17:11:53 -0700187 }
188
189 /**
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700190 * Clear this WorkSource to be empty.
191 */
192 public void clear() {
193 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000194 if (mChains != null) {
195 mChains.clear();
196 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700197 }
198
Jeff Brown94838912012-07-27 12:04:37 -0700199 @Override
200 public boolean equals(Object o) {
Narayan Kamatheb279462018-01-09 16:09:35 +0000201 if (o instanceof WorkSource) {
202 WorkSource other = (WorkSource) o;
203
204 if (diff(other)) {
205 return false;
206 }
207
208 if (mChains != null && !mChains.isEmpty()) {
209 return mChains.equals(other.mChains);
210 } else {
211 return other.mChains == null || other.mChains.isEmpty();
212 }
213 }
214
215 return false;
Jeff Brown94838912012-07-27 12:04:37 -0700216 }
217
218 @Override
219 public int hashCode() {
220 int result = 0;
221 for (int i = 0; i < mNum; i++) {
222 result = ((result << 4) | (result >>> 28)) ^ mUids[i];
223 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800224 if (mNames != null) {
225 for (int i = 0; i < mNum; i++) {
226 result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
227 }
228 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000229
230 if (mChains != null) {
231 result = ((result << 4) | (result >>> 28)) ^ mChains.hashCode();
232 }
233
Jeff Brown94838912012-07-27 12:04:37 -0700234 return result;
235 }
236
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700237 /**
238 * Compare this WorkSource with another.
239 * @param other The WorkSource to compare against.
240 * @return If there is a difference, true is returned.
241 */
Narayan Kamathf0202a92017-12-07 15:45:33 +0000242 // TODO: This is a public API so it cannot be renamed. Because it is used in several places,
243 // we keep its semantics the same and ignore any differences in WorkChains (if any).
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700244 public boolean diff(WorkSource other) {
245 int N = mNum;
246 if (N != other.mNum) {
247 return true;
248 }
249 final int[] uids1 = mUids;
250 final int[] uids2 = other.mUids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800251 final String[] names1 = mNames;
252 final String[] names2 = other.mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700253 for (int i=0; i<N; i++) {
254 if (uids1[i] != uids2[i]) {
255 return true;
256 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800257 if (names1 != null && names2 != null && !names1[i].equals(names2[i])) {
258 return true;
259 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700260 }
261 return false;
262 }
263
264 /**
265 * Replace the current contents of this work source with the given
Narayan Kamathf0202a92017-12-07 15:45:33 +0000266 * work source. If {@code other} is null, the current work source
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700267 * will be made empty.
268 */
269 public void set(WorkSource other) {
270 if (other == null) {
271 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000272 if (mChains != null) {
273 mChains.clear();
274 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700275 return;
276 }
277 mNum = other.mNum;
278 if (other.mUids != null) {
279 if (mUids != null && mUids.length >= mNum) {
280 System.arraycopy(other.mUids, 0, mUids, 0, mNum);
281 } else {
282 mUids = other.mUids.clone();
283 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800284 if (other.mNames != null) {
285 if (mNames != null && mNames.length >= mNum) {
286 System.arraycopy(other.mNames, 0, mNames, 0, mNum);
287 } else {
288 mNames = other.mNames.clone();
289 }
290 } else {
291 mNames = null;
292 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700293 } else {
294 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800295 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700296 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000297
298 if (other.mChains != null) {
299 if (mChains != null) {
300 mChains.clear();
301 } else {
302 mChains = new ArrayList<>(other.mChains.size());
303 }
304
305 for (WorkChain chain : other.mChains) {
306 mChains.add(new WorkChain(chain));
307 }
308 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700309 }
310
311 /** @hide */
312 public void set(int uid) {
313 mNum = 1;
314 if (mUids == null) mUids = new int[2];
315 mUids[0] = uid;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800316 mNames = null;
Narayan Kamath6192f732017-12-21 09:43:38 +0000317 if (mChains != null) {
318 mChains.clear();
319 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800320 }
321
322 /** @hide */
323 public void set(int uid, String name) {
324 if (name == null) {
325 throw new NullPointerException("Name can't be null");
326 }
327 mNum = 1;
328 if (mUids == null) {
329 mUids = new int[2];
330 mNames = new String[2];
331 }
332 mUids[0] = uid;
333 mNames[0] = name;
Narayan Kamath6192f732017-12-21 09:43:38 +0000334 if (mChains != null) {
335 mChains.clear();
336 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700337 }
338
Narayan Kamathf0202a92017-12-07 15:45:33 +0000339 /**
340 * Legacy API, DO NOT USE: Only deals with flat UIDs and tags. No chains are transferred, and no
341 * differences in chains are returned. This will be removed once its callers have been
342 * rewritten.
343 *
344 * NOTE: This is currently only used in GnssLocationProvider.
345 *
346 * @hide
347 * @deprecated for internal use only. WorkSources are opaque and no external callers should need
348 * to be aware of internal differences.
349 */
350 @Deprecated
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600351 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700352 public WorkSource[] setReturningDiffs(WorkSource other) {
353 synchronized (sTmpWorkSource) {
354 sNewbWork = null;
355 sGoneWork = null;
356 updateLocked(other, true, true);
357 if (sNewbWork != null || sGoneWork != null) {
358 WorkSource[] diffs = new WorkSource[2];
359 diffs[0] = sNewbWork;
360 diffs[1] = sGoneWork;
361 return diffs;
362 }
363 return null;
364 }
365 }
366
367 /**
368 * Merge the contents of <var>other</var> WorkSource in to this one.
369 *
370 * @param other The other WorkSource whose contents are to be merged.
371 * @return Returns true if any new sources were added.
372 */
373 public boolean add(WorkSource other) {
374 synchronized (sTmpWorkSource) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000375 boolean uidAdded = updateLocked(other, false, false);
376
377 boolean chainAdded = false;
378 if (other.mChains != null) {
379 // NOTE: This is quite an expensive operation, especially if the number of chains
380 // is large. We could look into optimizing it if it proves problematic.
381 if (mChains == null) {
382 mChains = new ArrayList<>(other.mChains.size());
383 }
384
385 for (WorkChain wc : other.mChains) {
386 if (!mChains.contains(wc)) {
387 mChains.add(new WorkChain(wc));
388 }
389 }
390 }
391
392 return uidAdded || chainAdded;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700393 }
394 }
395
Narayan Kamathf0202a92017-12-07 15:45:33 +0000396 /**
397 * Legacy API: DO NOT USE. Only in use from unit tests.
398 *
399 * @hide
400 * @deprecated meant for unit testing use only. Will be removed in a future API revision.
401 */
402 @Deprecated
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600403 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700404 public WorkSource addReturningNewbs(WorkSource other) {
405 synchronized (sTmpWorkSource) {
406 sNewbWork = null;
407 updateLocked(other, false, true);
408 return sNewbWork;
409 }
410 }
411
412 /** @hide */
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600413 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700414 public boolean add(int uid) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800415 if (mNum <= 0) {
416 mNames = null;
417 insert(0, uid);
418 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700419 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800420 if (mNames != null) {
421 throw new IllegalArgumentException("Adding without name to named " + this);
422 }
423 int i = Arrays.binarySearch(mUids, 0, mNum, uid);
424 if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i);
425 if (i >= 0) {
426 return false;
427 }
428 insert(-i-1, uid);
429 return true;
430 }
431
432 /** @hide */
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600433 @TestApi
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800434 public boolean add(int uid, String name) {
435 if (mNum <= 0) {
436 insert(0, uid, name);
437 return true;
438 }
439 if (mNames == null) {
440 throw new IllegalArgumentException("Adding name to unnamed " + this);
441 }
442 int i;
443 for (i=0; i<mNum; i++) {
444 if (mUids[i] > uid) {
445 break;
446 }
447 if (mUids[i] == uid) {
Netta P958d0a52017-02-07 11:20:55 -0800448 int diff = mNames[i].compareTo(name);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800449 if (diff > 0) {
450 break;
451 }
452 if (diff == 0) {
453 return false;
454 }
455 }
456 }
457 insert(i, uid, name);
458 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700459 }
460
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700461 public boolean remove(WorkSource other) {
Narayan Kamathee07f622018-01-08 12:20:28 +0000462 if (isEmpty() || other.isEmpty()) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800463 return false;
464 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000465
Narayan Kamathee07f622018-01-08 12:20:28 +0000466 boolean uidRemoved;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800467 if (mNames == null && other.mNames == null) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000468 uidRemoved = removeUids(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800469 } else {
470 if (mNames == null) {
471 throw new IllegalArgumentException("Other " + other + " has names, but target "
472 + this + " does not");
473 }
474 if (other.mNames == null) {
475 throw new IllegalArgumentException("Target " + this + " has names, but other "
476 + other + " does not");
477 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000478 uidRemoved = removeUidsAndNames(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800479 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000480
481 boolean chainRemoved = false;
Narayan Kamathee07f622018-01-08 12:20:28 +0000482 if (other.mChains != null && mChains != null) {
483 chainRemoved = mChains.removeAll(other.mChains);
Narayan Kamathf0202a92017-12-07 15:45:33 +0000484 }
485
486 return uidRemoved || chainRemoved;
487 }
488
489 /**
490 * Create a new {@code WorkChain} associated with this WorkSource and return it.
491 *
492 * @hide
493 */
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000494 @SystemApi
Narayan Kamathf0202a92017-12-07 15:45:33 +0000495 public WorkChain createWorkChain() {
496 if (mChains == null) {
497 mChains = new ArrayList<>(4);
498 }
499
500 final WorkChain wc = new WorkChain();
501 mChains.add(wc);
502
503 return wc;
504 }
505
506 /**
Narayan Kamath81822022017-12-08 11:56:01 +0000507 * Returns {@code true} iff. this work source contains zero UIDs and zero WorkChains to
508 * attribute usage to.
509 *
510 * @hide for internal use only.
511 */
512 public boolean isEmpty() {
513 return mNum == 0 && (mChains == null || mChains.isEmpty());
514 }
515
516 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000517 * @return the list of {@code WorkChains} associated with this {@code WorkSource}.
518 * @hide
519 */
520 public ArrayList<WorkChain> getWorkChains() {
521 return mChains;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800522 }
523
Narayan Kamath32684dd2018-01-08 17:32:51 +0000524 /**
525 * DO NOT USE: Hacky API provided solely for {@code GnssLocationProvider}. See
526 * {@code setReturningDiffs} as well.
527 *
528 * @hide
529 */
530 public void transferWorkChains(WorkSource other) {
531 if (mChains != null) {
532 mChains.clear();
533 }
534
535 if (other.mChains == null || other.mChains.isEmpty()) {
536 return;
537 }
538
539 if (mChains == null) {
540 mChains = new ArrayList<>(4);
541 }
542
543 mChains.addAll(other.mChains);
544 other.mChains.clear();
545 }
546
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800547 private boolean removeUids(WorkSource other) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700548 int N1 = mNum;
549 final int[] uids1 = mUids;
550 final int N2 = other.mNum;
551 final int[] uids2 = other.mUids;
552 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800553 int i1 = 0, i2 = 0;
554 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
555 while (i1 < N1 && i2 < N2) {
556 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
557 + " of " + N2);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700558 if (uids2[i2] == uids1[i1]) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800559 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
560 + ": remove " + uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700561 N1--;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800562 changed = true;
Dianne Hackborn83770282010-09-14 11:45:44 -0700563 if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800564 i2++;
565 } else if (uids2[i2] > uids1[i1]) {
566 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700567 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800568 } else {
569 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
570 i2++;
571 }
572 }
573
574 mNum = N1;
575
576 return changed;
577 }
578
579 private boolean removeUidsAndNames(WorkSource other) {
580 int N1 = mNum;
581 final int[] uids1 = mUids;
582 final String[] names1 = mNames;
583 final int N2 = other.mNum;
584 final int[] uids2 = other.mUids;
585 final String[] names2 = other.mNames;
586 boolean changed = false;
587 int i1 = 0, i2 = 0;
588 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
589 while (i1 < N1 && i2 < N2) {
590 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
591 + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]);
592 if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) {
593 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
594 + ": remove " + uids1[i1] + " " + names1[i1]);
595 N1--;
596 changed = true;
597 if (i1 < N1) {
598 System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
599 System.arraycopy(names1, i1+1, names1, i1, N1-i1);
600 }
601 i2++;
602 } else if (uids2[i2] > uids1[i1]
603 || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) {
604 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
605 i1++;
606 } else {
607 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
608 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700609 }
610 }
611
612 mNum = N1;
613
614 return changed;
615 }
616
617 private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800618 if (mNames == null && other.mNames == null) {
619 return updateUidsLocked(other, set, returnNewbs);
620 } else {
621 if (mNum > 0 && mNames == null) {
622 throw new IllegalArgumentException("Other " + other + " has names, but target "
623 + this + " does not");
624 }
625 if (other.mNum > 0 && other.mNames == null) {
626 throw new IllegalArgumentException("Target " + this + " has names, but other "
627 + other + " does not");
628 }
629 return updateUidsAndNamesLocked(other, set, returnNewbs);
630 }
631 }
632
633 private static WorkSource addWork(WorkSource cur, int newUid) {
634 if (cur == null) {
635 return new WorkSource(newUid);
636 }
637 cur.insert(cur.mNum, newUid);
638 return cur;
639 }
640
641 private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700642 int N1 = mNum;
643 int[] uids1 = mUids;
644 final int N2 = other.mNum;
645 final int[] uids2 = other.mUids;
646 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800647 int i1 = 0, i2 = 0;
648 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
649 + " returnNewbs=" + returnNewbs);
650 while (i1 < N1 || i2 < N2) {
651 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
652 + " of " + N2);
653 if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700654 // Need to insert a new uid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800655 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
656 + ": insert " + uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700657 changed = true;
658 if (uids1 == null) {
659 uids1 = new int[4];
660 uids1[0] = uids2[i2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800661 } else if (N1 >= uids1.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700662 int[] newuids = new int[(uids1.length*3)/2];
663 if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
664 if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
665 uids1 = newuids;
666 uids1[i1] = uids2[i2];
667 } else {
668 if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
669 uids1[i1] = uids2[i2];
670 }
671 if (returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800672 sNewbWork = addWork(sNewbWork, uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700673 }
674 N1++;
675 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800676 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700677 } else {
678 if (!set) {
679 // Skip uids that already exist or are not in 'other'.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800680 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
681 if (i2 < N2 && uids2[i2] == uids1[i1]) {
682 i2++;
683 }
684 i1++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700685 } else {
686 // Remove any uids that don't exist in 'other'.
687 int start = i1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800688 while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) {
689 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]);
690 sGoneWork = addWork(sGoneWork, uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700691 i1++;
692 }
693 if (start < i1) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800694 System.arraycopy(uids1, i1, uids1, start, N1-i1);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700695 N1 -= i1-start;
696 i1 = start;
697 }
698 // If there is a matching uid, skip it.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800699 if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) {
700 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700701 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800702 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700703 }
704 }
705 }
706 }
707
708 mNum = N1;
709 mUids = uids1;
710
711 return changed;
712 }
713
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800714 /**
715 * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'.
716 */
717 private int compare(WorkSource other, int i1, int i2) {
718 final int diff = mUids[i1] - other.mUids[i2];
719 if (diff != 0) {
720 return diff;
721 }
722 return mNames[i1].compareTo(other.mNames[i2]);
723 }
724
725 private static WorkSource addWork(WorkSource cur, int newUid, String newName) {
726 if (cur == null) {
727 return new WorkSource(newUid, newName);
728 }
729 cur.insert(cur.mNum, newUid, newName);
730 return cur;
731 }
732
733 private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) {
734 final int N2 = other.mNum;
735 final int[] uids2 = other.mUids;
736 String[] names2 = other.mNames;
737 boolean changed = false;
738 int i1 = 0, i2 = 0;
739 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
740 + " returnNewbs=" + returnNewbs);
741 while (i1 < mNum || i2 < N2) {
742 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2
743 + " of " + N2);
744 int diff = -1;
745 if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) {
746 // Need to insert a new uid.
747 changed = true;
748 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum
749 + ": insert " + uids2[i2] + " " + names2[i2]);
750 insert(i1, uids2[i2], names2[i2]);
751 if (returnNewbs) {
752 sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]);
753 }
754 i1++;
755 i2++;
756 } else {
757 if (!set) {
758 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
759 if (i2 < N2 && diff == 0) {
760 i2++;
761 }
762 i1++;
763 } else {
764 // Remove any uids that don't exist in 'other'.
765 int start = i1;
766 while (diff < 0) {
767 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1]
768 + " " + mNames[i1]);
769 sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]);
770 i1++;
771 if (i1 >= mNum) {
772 break;
773 }
774 diff = i2 < N2 ? compare(other, i1, i2) : -1;
775 }
776 if (start < i1) {
777 System.arraycopy(mUids, i1, mUids, start, mNum-i1);
778 System.arraycopy(mNames, i1, mNames, start, mNum-i1);
779 mNum -= i1-start;
780 i1 = start;
781 }
782 // If there is a matching uid, skip it.
783 if (i1 < mNum && diff == 0) {
784 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
785 i1++;
786 i2++;
787 }
788 }
789 }
790 }
791
792 return changed;
793 }
794
795 private void insert(int index, int uid) {
796 if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700797 if (mUids == null) {
798 mUids = new int[4];
799 mUids[0] = uid;
800 mNum = 1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800801 } else if (mNum >= mUids.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700802 int[] newuids = new int[(mNum*3)/2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800803 if (index > 0) {
804 System.arraycopy(mUids, 0, newuids, 0, index);
805 }
806 if (index < mNum) {
807 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
808 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700809 mUids = newuids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800810 mUids[index] = uid;
811 mNum++;
812 } else {
813 if (index < mNum) {
814 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
815 }
816 mUids[index] = uid;
817 mNum++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700818 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800819 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700820
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800821 private void insert(int index, int uid, String name) {
822 if (mUids == null) {
823 mUids = new int[4];
824 mUids[0] = uid;
825 mNames = new String[4];
826 mNames[0] = name;
827 mNum = 1;
828 } else if (mNum >= mUids.length) {
829 int[] newuids = new int[(mNum*3)/2];
830 String[] newnames = new String[(mNum*3)/2];
831 if (index > 0) {
832 System.arraycopy(mUids, 0, newuids, 0, index);
833 System.arraycopy(mNames, 0, newnames, 0, index);
834 }
835 if (index < mNum) {
836 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
837 System.arraycopy(mNames, index, newnames, index+1, mNum-index);
838 }
839 mUids = newuids;
840 mNames = newnames;
841 mUids[index] = uid;
842 mNames[index] = name;
843 mNum++;
844 } else {
845 if (index < mNum) {
846 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
847 System.arraycopy(mNames, index, mNames, index+1, mNum-index);
848 }
849 mUids[index] = uid;
850 mNames[index] = name;
851 mNum++;
852 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700853 }
854
Narayan Kamathf0202a92017-12-07 15:45:33 +0000855 /**
856 * Represents an attribution chain for an item of work being performed. An attribution chain is
857 * an indexed list of {code (uid, tag)} nodes. The node at {@code index == 0} is the initiator
858 * of the work, and the node at the highest index performs the work. Nodes at other indices
859 * are intermediaries that facilitate the work. Examples :
860 *
861 * (1) Work being performed by uid=2456 (no chaining):
862 * <pre>
863 * WorkChain {
864 * mUids = { 2456 }
865 * mTags = { null }
866 * mSize = 1;
867 * }
868 * </pre>
869 *
870 * (2) Work being performed by uid=2456 (from component "c1") on behalf of uid=5678:
871 *
872 * <pre>
873 * WorkChain {
874 * mUids = { 5678, 2456 }
875 * mTags = { null, "c1" }
876 * mSize = 1
877 * }
878 * </pre>
879 *
880 * Attribution chains are mutable, though the only operation that can be performed on them
881 * is the addition of a new node at the end of the attribution chain to represent
882 *
883 * @hide
884 */
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000885 @SystemApi
886 public static final class WorkChain implements Parcelable {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000887 private int mSize;
888 private int[] mUids;
889 private String[] mTags;
890
891 // @VisibleForTesting
892 public WorkChain() {
893 mSize = 0;
894 mUids = new int[4];
895 mTags = new String[4];
896 }
897
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000898 /** @hide */
899 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000900 public WorkChain(WorkChain other) {
901 mSize = other.mSize;
902 mUids = other.mUids.clone();
903 mTags = other.mTags.clone();
904 }
905
906 private WorkChain(Parcel in) {
907 mSize = in.readInt();
908 mUids = in.createIntArray();
909 mTags = in.createStringArray();
910 }
911
912 /**
913 * Append a node whose uid is {@code uid} and whose optional tag is {@code tag} to this
914 * {@code WorkChain}.
915 */
916 public WorkChain addNode(int uid, @Nullable String tag) {
917 if (mSize == mUids.length) {
918 resizeArrays();
919 }
920
921 mUids[mSize] = uid;
922 mTags[mSize] = tag;
923 mSize++;
924
925 return this;
926 }
927
Narayan Kamath81822022017-12-08 11:56:01 +0000928 /**
Narayan Kamathcbe06772017-12-27 14:22:47 +0000929 * Return the UID to which this WorkChain should be attributed to, i.e, the UID that
Marcin Oczeretko53295402018-12-17 12:44:35 +0000930 * initiated the work and not the UID performing it. Returns -1 if the chain is empty.
Narayan Kamath81822022017-12-08 11:56:01 +0000931 */
932 public int getAttributionUid() {
Marcin Oczeretko53295402018-12-17 12:44:35 +0000933 return mSize > 0 ? mUids[0] : -1;
Narayan Kamath81822022017-12-08 11:56:01 +0000934 }
935
Narayan Kamath32684dd2018-01-08 17:32:51 +0000936 /**
937 * Return the tag associated with the attribution UID. See (@link #getAttributionUid}.
Marcin Oczeretko53295402018-12-17 12:44:35 +0000938 * Returns null if the chain is empty.
Narayan Kamath32684dd2018-01-08 17:32:51 +0000939 */
940 public String getAttributionTag() {
Marcin Oczeretko53295402018-12-17 12:44:35 +0000941 return mTags.length > 0 ? mTags[0] : null;
Narayan Kamath32684dd2018-01-08 17:32:51 +0000942 }
943
Narayan Kamathf0202a92017-12-07 15:45:33 +0000944 // TODO: The following three trivial getters are purely for testing and will be removed
945 // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
946 // diffing it etc.
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000947
948
949 /** @hide */
950 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000951 public int[] getUids() {
Yangster-mac5fa895e2018-04-09 14:45:28 -0700952 int[] uids = new int[mSize];
953 System.arraycopy(mUids, 0, uids, 0, mSize);
954 return uids;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000955 }
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000956
957 /** @hide */
958 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000959 public String[] getTags() {
Yangster-mac5fa895e2018-04-09 14:45:28 -0700960 String[] tags = new String[mSize];
961 System.arraycopy(mTags, 0, tags, 0, mSize);
962 return tags;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000963 }
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000964
965 /** @hide */
966 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000967 public int getSize() {
968 return mSize;
969 }
970
971 private void resizeArrays() {
972 final int newSize = mSize * 2;
973 int[] uids = new int[newSize];
974 String[] tags = new String[newSize];
975
976 System.arraycopy(mUids, 0, uids, 0, mSize);
977 System.arraycopy(mTags, 0, tags, 0, mSize);
978
979 mUids = uids;
980 mTags = tags;
981 }
982
983 @Override
984 public String toString() {
985 StringBuilder result = new StringBuilder("WorkChain{");
986 for (int i = 0; i < mSize; ++i) {
987 if (i != 0) {
988 result.append(", ");
989 }
990 result.append("(");
991 result.append(mUids[i]);
992 if (mTags[i] != null) {
993 result.append(", ");
994 result.append(mTags[i]);
995 }
996 result.append(")");
997 }
998
999 result.append("}");
1000 return result.toString();
1001 }
1002
1003 @Override
1004 public int hashCode() {
1005 return (mSize + 31 * Arrays.hashCode(mUids)) * 31 + Arrays.hashCode(mTags);
1006 }
1007
1008 @Override
1009 public boolean equals(Object o) {
1010 if (o instanceof WorkChain) {
1011 WorkChain other = (WorkChain) o;
1012
1013 return mSize == other.mSize
1014 && Arrays.equals(mUids, other.mUids)
1015 && Arrays.equals(mTags, other.mTags);
1016 }
1017
1018 return false;
1019 }
1020
1021 @Override
1022 public int describeContents() {
1023 return 0;
1024 }
1025
1026 @Override
1027 public void writeToParcel(Parcel dest, int flags) {
1028 dest.writeInt(mSize);
1029 dest.writeIntArray(mUids);
1030 dest.writeStringArray(mTags);
1031 }
1032
1033 public static final Parcelable.Creator<WorkChain> CREATOR =
1034 new Parcelable.Creator<WorkChain>() {
1035 public WorkChain createFromParcel(Parcel in) {
1036 return new WorkChain(in);
1037 }
1038 public WorkChain[] newArray(int size) {
1039 return new WorkChain[size];
1040 }
1041 };
1042 }
1043
Narayan Kamath81822022017-12-08 11:56:01 +00001044 /**
1045 * Computes the differences in WorkChains contained between {@code oldWs} and {@code newWs}.
1046 *
1047 * Returns {@code null} if no differences exist, otherwise returns a two element array. The
1048 * first element is a list of "new" chains, i.e WorkChains present in {@code newWs} but not in
1049 * {@code oldWs}. The second element is a list of "gone" chains, i.e WorkChains present in
1050 * {@code oldWs} but not in {@code newWs}.
1051 *
1052 * @hide
1053 */
1054 public static ArrayList<WorkChain>[] diffChains(WorkSource oldWs, WorkSource newWs) {
1055 ArrayList<WorkChain> newChains = null;
1056 ArrayList<WorkChain> goneChains = null;
1057
1058 // TODO(narayan): This is a dumb O(M*N) algorithm that determines what has changed across
1059 // WorkSource objects. We can replace this with something smarter, for e.g by defining
1060 // a Comparator between WorkChains. It's unclear whether that will be more efficient if
1061 // the number of chains associated with a WorkSource is expected to be small
1062 if (oldWs.mChains != null) {
1063 for (int i = 0; i < oldWs.mChains.size(); ++i) {
1064 final WorkChain wc = oldWs.mChains.get(i);
1065 if (newWs.mChains == null || !newWs.mChains.contains(wc)) {
1066 if (goneChains == null) {
1067 goneChains = new ArrayList<>(oldWs.mChains.size());
1068 }
1069 goneChains.add(wc);
1070 }
1071 }
1072 }
1073
1074 if (newWs.mChains != null) {
1075 for (int i = 0; i < newWs.mChains.size(); ++i) {
1076 final WorkChain wc = newWs.mChains.get(i);
1077 if (oldWs.mChains == null || !oldWs.mChains.contains(wc)) {
1078 if (newChains == null) {
1079 newChains = new ArrayList<>(newWs.mChains.size());
1080 }
1081 newChains.add(wc);
1082 }
1083 }
1084 }
1085
1086 if (newChains != null || goneChains != null) {
1087 return new ArrayList[] { newChains, goneChains };
1088 }
1089
1090 return null;
1091 }
1092
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001093 @Override
1094 public int describeContents() {
1095 return 0;
1096 }
1097
1098 @Override
1099 public void writeToParcel(Parcel dest, int flags) {
1100 dest.writeInt(mNum);
1101 dest.writeIntArray(mUids);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001102 dest.writeStringArray(mNames);
Narayan Kamathf0202a92017-12-07 15:45:33 +00001103
1104 if (mChains == null) {
1105 dest.writeInt(-1);
1106 } else {
1107 dest.writeInt(mChains.size());
1108 dest.writeParcelableList(mChains, flags);
1109 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001110 }
1111
Jeff Brown96307042012-07-27 15:51:34 -07001112 @Override
1113 public String toString() {
1114 StringBuilder result = new StringBuilder();
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001115 result.append("WorkSource{");
Jeff Brown96307042012-07-27 15:51:34 -07001116 for (int i = 0; i < mNum; i++) {
1117 if (i != 0) {
1118 result.append(", ");
1119 }
1120 result.append(mUids[i]);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001121 if (mNames != null) {
1122 result.append(" ");
1123 result.append(mNames[i]);
1124 }
Jeff Brown96307042012-07-27 15:51:34 -07001125 }
Narayan Kamathf0202a92017-12-07 15:45:33 +00001126
1127 if (mChains != null) {
1128 result.append(" chains=");
1129 for (int i = 0; i < mChains.size(); ++i) {
1130 if (i != 0) {
1131 result.append(", ");
1132 }
1133 result.append(mChains.get(i));
1134 }
1135 }
1136
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001137 result.append("}");
Jeff Brown96307042012-07-27 15:51:34 -07001138 return result.toString();
1139 }
1140
Netta P958d0a52017-02-07 11:20:55 -08001141 /** @hide */
1142 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1143 final long workSourceToken = proto.start(fieldId);
1144 for (int i = 0; i < mNum; i++) {
1145 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1146 proto.write(WorkSourceProto.WorkSourceContentProto.UID, mUids[i]);
1147 if (mNames != null) {
1148 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, mNames[i]);
1149 }
1150 proto.end(contentProto);
1151 }
Narayan Kamath80434a72017-12-27 15:44:17 +00001152
1153 if (mChains != null) {
1154 for (int i = 0; i < mChains.size(); i++) {
1155 final WorkChain wc = mChains.get(i);
1156 final long workChain = proto.start(WorkSourceProto.WORK_CHAINS);
1157
1158 final String[] tags = wc.getTags();
1159 final int[] uids = wc.getUids();
1160 for (int j = 0; j < tags.length; j++) {
1161 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1162 proto.write(WorkSourceProto.WorkSourceContentProto.UID, uids[j]);
1163 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, tags[j]);
1164 proto.end(contentProto);
1165 }
1166
1167 proto.end(workChain);
1168 }
1169 }
1170
Netta P958d0a52017-02-07 11:20:55 -08001171 proto.end(workSourceToken);
1172 }
1173
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001174 public static final Parcelable.Creator<WorkSource> CREATOR
1175 = new Parcelable.Creator<WorkSource>() {
1176 public WorkSource createFromParcel(Parcel in) {
1177 return new WorkSource(in);
1178 }
1179 public WorkSource[] newArray(int size) {
1180 return new WorkSource[size];
1181 }
1182 };
1183}