blob: 401b4a36a7438f4e165dca73d40ef60b6fdaeffd [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;
Netta P958d0a52017-02-07 11:20:55 -08004import android.os.WorkSourceProto;
Dianne Hackborn002a54e2013-01-10 17:34:55 -08005import android.util.Log;
Netta P958d0a52017-02-07 11:20:55 -08006import android.util.proto.ProtoOutputStream;
Jeff Brown96307042012-07-27 15:51:34 -07007
Narayan Kamathf0202a92017-12-07 15:45:33 +00008import java.util.ArrayList;
Jeff Brown96307042012-07-27 15:51:34 -07009import java.util.Arrays;
Narayan Kamathf0202a92017-12-07 15:45:33 +000010import java.util.Objects;
Jeff Brown96307042012-07-27 15:51:34 -070011
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070012/**
13 * Describes the source of some work that may be done by someone else.
14 * Currently the public representation of what a work source is is not
15 * defined; this is an opaque container.
16 */
17public class WorkSource implements Parcelable {
Dianne Hackborn002a54e2013-01-10 17:34:55 -080018 static final String TAG = "WorkSource";
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080019 static final boolean DEBUG = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080020
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070021 int mNum;
22 int[] mUids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080023 String[] mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070024
Narayan Kamathf0202a92017-12-07 15:45:33 +000025 private ArrayList<WorkChain> mChains;
26
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070027 /**
28 * Internal statics to avoid object allocations in some operations.
29 * The WorkSource object itself is not thread safe, but we need to
30 * hold sTmpWorkSource lock while working with these statics.
31 */
32 static final WorkSource sTmpWorkSource = new WorkSource(0);
33 /**
34 * For returning newbie work from a modification operation.
35 */
36 static WorkSource sNewbWork;
37 /**
38 * For returning gone work form a modification operation.
39 */
40 static WorkSource sGoneWork;
41
42 /**
43 * Create an empty work source.
44 */
45 public WorkSource() {
46 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000047 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070048 }
49
50 /**
51 * Create a new WorkSource that is a copy of an existing one.
52 * If <var>orig</var> is null, an empty WorkSource is created.
53 */
54 public WorkSource(WorkSource orig) {
55 if (orig == null) {
56 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000057 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070058 return;
59 }
60 mNum = orig.mNum;
61 if (orig.mUids != null) {
62 mUids = orig.mUids.clone();
Dianne Hackborn002a54e2013-01-10 17:34:55 -080063 mNames = orig.mNames != null ? orig.mNames.clone() : null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070064 } else {
65 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080066 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070067 }
Narayan Kamathf0202a92017-12-07 15:45:33 +000068
69 if (orig.mChains != null) {
70 // Make a copy of all WorkChains that exist on |orig| since they are mutable.
71 mChains = new ArrayList<>(orig.mChains.size());
72 for (WorkChain chain : orig.mChains) {
73 mChains.add(new WorkChain(chain));
74 }
75 } else {
76 mChains = null;
77 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070078 }
79
80 /** @hide */
81 public WorkSource(int uid) {
82 mNum = 1;
83 mUids = new int[] { uid, 0 };
Dianne Hackborn002a54e2013-01-10 17:34:55 -080084 mNames = null;
Narayan Kamathf0202a92017-12-07 15:45:33 +000085 mChains = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080086 }
87
88 /** @hide */
89 public WorkSource(int uid, String name) {
90 if (name == null) {
91 throw new NullPointerException("Name can't be null");
92 }
93 mNum = 1;
94 mUids = new int[] { uid, 0 };
95 mNames = new String[] { name, null };
Narayan Kamathf0202a92017-12-07 15:45:33 +000096 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070097 }
98
99 WorkSource(Parcel in) {
100 mNum = in.readInt();
101 mUids = in.createIntArray();
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800102 mNames = in.createStringArray();
Narayan Kamathf0202a92017-12-07 15:45:33 +0000103
104 int numChains = in.readInt();
105 if (numChains > 0) {
106 mChains = new ArrayList<>(numChains);
107 in.readParcelableList(mChains, WorkChain.class.getClassLoader());
108 } else {
109 mChains = null;
110 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700111 }
112
113 /** @hide */
114 public int size() {
115 return mNum;
116 }
117
118 /** @hide */
119 public int get(int index) {
120 return mUids[index];
121 }
122
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800123 /** @hide */
124 public String getName(int index) {
125 return mNames != null ? mNames[index] : null;
126 }
127
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700128 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000129 * Clear names from this WorkSource. Uids are left intact. WorkChains if any, are left
130 * intact.
David Christie6ab22842013-09-13 17:11:53 -0700131 *
132 * <p>Useful when combining with another WorkSource that doesn't have names.
133 * @hide
134 */
135 public void clearNames() {
David Christiea31510e2013-09-20 10:44:01 -0700136 if (mNames != null) {
137 mNames = null;
138 // Clear out any duplicate uids now that we don't have names to disambiguate them.
139 int destIndex = 1;
140 int newNum = mNum;
141 for (int sourceIndex = 1; sourceIndex < mNum; sourceIndex++) {
142 if (mUids[sourceIndex] == mUids[sourceIndex - 1]) {
143 newNum--;
144 } else {
145 mUids[destIndex] = mUids[sourceIndex];
146 destIndex++;
147 }
148 }
149 mNum = newNum;
150 }
David Christie6ab22842013-09-13 17:11:53 -0700151 }
152
153 /**
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700154 * Clear this WorkSource to be empty.
155 */
156 public void clear() {
157 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000158 if (mChains != null) {
159 mChains.clear();
160 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700161 }
162
Jeff Brown94838912012-07-27 12:04:37 -0700163 @Override
164 public boolean equals(Object o) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000165 return o instanceof WorkSource
166 && !diff((WorkSource) o)
167 && Objects.equals(mChains, ((WorkSource) o).mChains);
Jeff Brown94838912012-07-27 12:04:37 -0700168 }
169
170 @Override
171 public int hashCode() {
172 int result = 0;
173 for (int i = 0; i < mNum; i++) {
174 result = ((result << 4) | (result >>> 28)) ^ mUids[i];
175 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800176 if (mNames != null) {
177 for (int i = 0; i < mNum; i++) {
178 result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
179 }
180 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000181
182 if (mChains != null) {
183 result = ((result << 4) | (result >>> 28)) ^ mChains.hashCode();
184 }
185
Jeff Brown94838912012-07-27 12:04:37 -0700186 return result;
187 }
188
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700189 /**
190 * Compare this WorkSource with another.
191 * @param other The WorkSource to compare against.
192 * @return If there is a difference, true is returned.
193 */
Narayan Kamathf0202a92017-12-07 15:45:33 +0000194 // TODO: This is a public API so it cannot be renamed. Because it is used in several places,
195 // we keep its semantics the same and ignore any differences in WorkChains (if any).
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700196 public boolean diff(WorkSource other) {
197 int N = mNum;
198 if (N != other.mNum) {
199 return true;
200 }
201 final int[] uids1 = mUids;
202 final int[] uids2 = other.mUids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800203 final String[] names1 = mNames;
204 final String[] names2 = other.mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700205 for (int i=0; i<N; i++) {
206 if (uids1[i] != uids2[i]) {
207 return true;
208 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800209 if (names1 != null && names2 != null && !names1[i].equals(names2[i])) {
210 return true;
211 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700212 }
213 return false;
214 }
215
216 /**
217 * Replace the current contents of this work source with the given
Narayan Kamathf0202a92017-12-07 15:45:33 +0000218 * work source. If {@code other} is null, the current work source
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700219 * will be made empty.
220 */
221 public void set(WorkSource other) {
222 if (other == null) {
223 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000224 if (mChains != null) {
225 mChains.clear();
226 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700227 return;
228 }
229 mNum = other.mNum;
230 if (other.mUids != null) {
231 if (mUids != null && mUids.length >= mNum) {
232 System.arraycopy(other.mUids, 0, mUids, 0, mNum);
233 } else {
234 mUids = other.mUids.clone();
235 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800236 if (other.mNames != null) {
237 if (mNames != null && mNames.length >= mNum) {
238 System.arraycopy(other.mNames, 0, mNames, 0, mNum);
239 } else {
240 mNames = other.mNames.clone();
241 }
242 } else {
243 mNames = null;
244 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700245 } else {
246 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800247 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700248 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000249
250 if (other.mChains != null) {
251 if (mChains != null) {
252 mChains.clear();
253 } else {
254 mChains = new ArrayList<>(other.mChains.size());
255 }
256
257 for (WorkChain chain : other.mChains) {
258 mChains.add(new WorkChain(chain));
259 }
260 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700261 }
262
263 /** @hide */
264 public void set(int uid) {
265 mNum = 1;
266 if (mUids == null) mUids = new int[2];
267 mUids[0] = uid;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800268 mNames = null;
Narayan Kamath6192f732017-12-21 09:43:38 +0000269 if (mChains != null) {
270 mChains.clear();
271 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800272 }
273
274 /** @hide */
275 public void set(int uid, String name) {
276 if (name == null) {
277 throw new NullPointerException("Name can't be null");
278 }
279 mNum = 1;
280 if (mUids == null) {
281 mUids = new int[2];
282 mNames = new String[2];
283 }
284 mUids[0] = uid;
285 mNames[0] = name;
Narayan Kamath6192f732017-12-21 09:43:38 +0000286 if (mChains != null) {
287 mChains.clear();
288 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700289 }
290
Narayan Kamathf0202a92017-12-07 15:45:33 +0000291 /**
292 * Legacy API, DO NOT USE: Only deals with flat UIDs and tags. No chains are transferred, and no
293 * differences in chains are returned. This will be removed once its callers have been
294 * rewritten.
295 *
296 * NOTE: This is currently only used in GnssLocationProvider.
297 *
298 * @hide
299 * @deprecated for internal use only. WorkSources are opaque and no external callers should need
300 * to be aware of internal differences.
301 */
302 @Deprecated
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700303 public WorkSource[] setReturningDiffs(WorkSource other) {
304 synchronized (sTmpWorkSource) {
305 sNewbWork = null;
306 sGoneWork = null;
307 updateLocked(other, true, true);
308 if (sNewbWork != null || sGoneWork != null) {
309 WorkSource[] diffs = new WorkSource[2];
310 diffs[0] = sNewbWork;
311 diffs[1] = sGoneWork;
312 return diffs;
313 }
314 return null;
315 }
316 }
317
318 /**
319 * Merge the contents of <var>other</var> WorkSource in to this one.
320 *
321 * @param other The other WorkSource whose contents are to be merged.
322 * @return Returns true if any new sources were added.
323 */
324 public boolean add(WorkSource other) {
325 synchronized (sTmpWorkSource) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000326 boolean uidAdded = updateLocked(other, false, false);
327
328 boolean chainAdded = false;
329 if (other.mChains != null) {
330 // NOTE: This is quite an expensive operation, especially if the number of chains
331 // is large. We could look into optimizing it if it proves problematic.
332 if (mChains == null) {
333 mChains = new ArrayList<>(other.mChains.size());
334 }
335
336 for (WorkChain wc : other.mChains) {
337 if (!mChains.contains(wc)) {
338 mChains.add(new WorkChain(wc));
339 }
340 }
341 }
342
343 return uidAdded || chainAdded;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700344 }
345 }
346
Narayan Kamathf0202a92017-12-07 15:45:33 +0000347 /**
348 * Legacy API: DO NOT USE. Only in use from unit tests.
349 *
350 * @hide
351 * @deprecated meant for unit testing use only. Will be removed in a future API revision.
352 */
353 @Deprecated
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700354 public WorkSource addReturningNewbs(WorkSource other) {
355 synchronized (sTmpWorkSource) {
356 sNewbWork = null;
357 updateLocked(other, false, true);
358 return sNewbWork;
359 }
360 }
361
362 /** @hide */
363 public boolean add(int uid) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800364 if (mNum <= 0) {
365 mNames = null;
366 insert(0, uid);
367 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700368 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800369 if (mNames != null) {
370 throw new IllegalArgumentException("Adding without name to named " + this);
371 }
372 int i = Arrays.binarySearch(mUids, 0, mNum, uid);
373 if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i);
374 if (i >= 0) {
375 return false;
376 }
377 insert(-i-1, uid);
378 return true;
379 }
380
381 /** @hide */
382 public boolean add(int uid, String name) {
383 if (mNum <= 0) {
384 insert(0, uid, name);
385 return true;
386 }
387 if (mNames == null) {
388 throw new IllegalArgumentException("Adding name to unnamed " + this);
389 }
390 int i;
391 for (i=0; i<mNum; i++) {
392 if (mUids[i] > uid) {
393 break;
394 }
395 if (mUids[i] == uid) {
Netta P958d0a52017-02-07 11:20:55 -0800396 int diff = mNames[i].compareTo(name);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800397 if (diff > 0) {
398 break;
399 }
400 if (diff == 0) {
401 return false;
402 }
403 }
404 }
405 insert(i, uid, name);
406 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700407 }
408
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700409 public boolean remove(WorkSource other) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800410 if (mNum <= 0 || other.mNum <= 0) {
411 return false;
412 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000413
414 boolean uidRemoved = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800415 if (mNames == null && other.mNames == null) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000416 uidRemoved = removeUids(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800417 } else {
418 if (mNames == null) {
419 throw new IllegalArgumentException("Other " + other + " has names, but target "
420 + this + " does not");
421 }
422 if (other.mNames == null) {
423 throw new IllegalArgumentException("Target " + this + " has names, but other "
424 + other + " does not");
425 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000426 uidRemoved = removeUidsAndNames(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800427 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000428
429 boolean chainRemoved = false;
430 if (other.mChains != null) {
431 if (mChains != null) {
432 chainRemoved = mChains.removeAll(other.mChains);
433 }
434 } else if (mChains != null) {
435 mChains.clear();
436 chainRemoved = true;
437 }
438
439 return uidRemoved || chainRemoved;
440 }
441
442 /**
443 * Create a new {@code WorkChain} associated with this WorkSource and return it.
444 *
445 * @hide
446 */
447 public WorkChain createWorkChain() {
448 if (mChains == null) {
449 mChains = new ArrayList<>(4);
450 }
451
452 final WorkChain wc = new WorkChain();
453 mChains.add(wc);
454
455 return wc;
456 }
457
458 /**
Narayan Kamath81822022017-12-08 11:56:01 +0000459 * Returns {@code true} iff. this work source contains zero UIDs and zero WorkChains to
460 * attribute usage to.
461 *
462 * @hide for internal use only.
463 */
464 public boolean isEmpty() {
465 return mNum == 0 && (mChains == null || mChains.isEmpty());
466 }
467
468 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000469 * @return the list of {@code WorkChains} associated with this {@code WorkSource}.
470 * @hide
471 */
472 public ArrayList<WorkChain> getWorkChains() {
473 return mChains;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800474 }
475
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800476 private boolean removeUids(WorkSource other) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700477 int N1 = mNum;
478 final int[] uids1 = mUids;
479 final int N2 = other.mNum;
480 final int[] uids2 = other.mUids;
481 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800482 int i1 = 0, i2 = 0;
483 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
484 while (i1 < N1 && i2 < N2) {
485 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
486 + " of " + N2);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700487 if (uids2[i2] == uids1[i1]) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800488 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
489 + ": remove " + uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700490 N1--;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800491 changed = true;
Dianne Hackborn83770282010-09-14 11:45:44 -0700492 if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800493 i2++;
494 } else if (uids2[i2] > uids1[i1]) {
495 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700496 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800497 } else {
498 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
499 i2++;
500 }
501 }
502
503 mNum = N1;
504
505 return changed;
506 }
507
508 private boolean removeUidsAndNames(WorkSource other) {
509 int N1 = mNum;
510 final int[] uids1 = mUids;
511 final String[] names1 = mNames;
512 final int N2 = other.mNum;
513 final int[] uids2 = other.mUids;
514 final String[] names2 = other.mNames;
515 boolean changed = false;
516 int i1 = 0, i2 = 0;
517 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
518 while (i1 < N1 && i2 < N2) {
519 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
520 + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]);
521 if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) {
522 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
523 + ": remove " + uids1[i1] + " " + names1[i1]);
524 N1--;
525 changed = true;
526 if (i1 < N1) {
527 System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
528 System.arraycopy(names1, i1+1, names1, i1, N1-i1);
529 }
530 i2++;
531 } else if (uids2[i2] > uids1[i1]
532 || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) {
533 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
534 i1++;
535 } else {
536 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
537 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700538 }
539 }
540
541 mNum = N1;
542
543 return changed;
544 }
545
546 private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800547 if (mNames == null && other.mNames == null) {
548 return updateUidsLocked(other, set, returnNewbs);
549 } else {
550 if (mNum > 0 && mNames == null) {
551 throw new IllegalArgumentException("Other " + other + " has names, but target "
552 + this + " does not");
553 }
554 if (other.mNum > 0 && other.mNames == null) {
555 throw new IllegalArgumentException("Target " + this + " has names, but other "
556 + other + " does not");
557 }
558 return updateUidsAndNamesLocked(other, set, returnNewbs);
559 }
560 }
561
562 private static WorkSource addWork(WorkSource cur, int newUid) {
563 if (cur == null) {
564 return new WorkSource(newUid);
565 }
566 cur.insert(cur.mNum, newUid);
567 return cur;
568 }
569
570 private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700571 int N1 = mNum;
572 int[] uids1 = mUids;
573 final int N2 = other.mNum;
574 final int[] uids2 = other.mUids;
575 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800576 int i1 = 0, i2 = 0;
577 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
578 + " returnNewbs=" + returnNewbs);
579 while (i1 < N1 || i2 < N2) {
580 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
581 + " of " + N2);
582 if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700583 // Need to insert a new uid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800584 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
585 + ": insert " + uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700586 changed = true;
587 if (uids1 == null) {
588 uids1 = new int[4];
589 uids1[0] = uids2[i2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800590 } else if (N1 >= uids1.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700591 int[] newuids = new int[(uids1.length*3)/2];
592 if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
593 if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
594 uids1 = newuids;
595 uids1[i1] = uids2[i2];
596 } else {
597 if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
598 uids1[i1] = uids2[i2];
599 }
600 if (returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800601 sNewbWork = addWork(sNewbWork, uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700602 }
603 N1++;
604 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800605 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700606 } else {
607 if (!set) {
608 // Skip uids that already exist or are not in 'other'.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800609 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
610 if (i2 < N2 && uids2[i2] == uids1[i1]) {
611 i2++;
612 }
613 i1++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700614 } else {
615 // Remove any uids that don't exist in 'other'.
616 int start = i1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800617 while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) {
618 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]);
619 sGoneWork = addWork(sGoneWork, uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700620 i1++;
621 }
622 if (start < i1) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800623 System.arraycopy(uids1, i1, uids1, start, N1-i1);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700624 N1 -= i1-start;
625 i1 = start;
626 }
627 // If there is a matching uid, skip it.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800628 if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) {
629 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700630 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800631 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700632 }
633 }
634 }
635 }
636
637 mNum = N1;
638 mUids = uids1;
639
640 return changed;
641 }
642
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800643 /**
644 * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'.
645 */
646 private int compare(WorkSource other, int i1, int i2) {
647 final int diff = mUids[i1] - other.mUids[i2];
648 if (diff != 0) {
649 return diff;
650 }
651 return mNames[i1].compareTo(other.mNames[i2]);
652 }
653
654 private static WorkSource addWork(WorkSource cur, int newUid, String newName) {
655 if (cur == null) {
656 return new WorkSource(newUid, newName);
657 }
658 cur.insert(cur.mNum, newUid, newName);
659 return cur;
660 }
661
662 private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) {
663 final int N2 = other.mNum;
664 final int[] uids2 = other.mUids;
665 String[] names2 = other.mNames;
666 boolean changed = false;
667 int i1 = 0, i2 = 0;
668 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
669 + " returnNewbs=" + returnNewbs);
670 while (i1 < mNum || i2 < N2) {
671 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2
672 + " of " + N2);
673 int diff = -1;
674 if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) {
675 // Need to insert a new uid.
676 changed = true;
677 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum
678 + ": insert " + uids2[i2] + " " + names2[i2]);
679 insert(i1, uids2[i2], names2[i2]);
680 if (returnNewbs) {
681 sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]);
682 }
683 i1++;
684 i2++;
685 } else {
686 if (!set) {
687 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
688 if (i2 < N2 && diff == 0) {
689 i2++;
690 }
691 i1++;
692 } else {
693 // Remove any uids that don't exist in 'other'.
694 int start = i1;
695 while (diff < 0) {
696 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1]
697 + " " + mNames[i1]);
698 sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]);
699 i1++;
700 if (i1 >= mNum) {
701 break;
702 }
703 diff = i2 < N2 ? compare(other, i1, i2) : -1;
704 }
705 if (start < i1) {
706 System.arraycopy(mUids, i1, mUids, start, mNum-i1);
707 System.arraycopy(mNames, i1, mNames, start, mNum-i1);
708 mNum -= i1-start;
709 i1 = start;
710 }
711 // If there is a matching uid, skip it.
712 if (i1 < mNum && diff == 0) {
713 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
714 i1++;
715 i2++;
716 }
717 }
718 }
719 }
720
721 return changed;
722 }
723
724 private void insert(int index, int uid) {
725 if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700726 if (mUids == null) {
727 mUids = new int[4];
728 mUids[0] = uid;
729 mNum = 1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800730 } else if (mNum >= mUids.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700731 int[] newuids = new int[(mNum*3)/2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800732 if (index > 0) {
733 System.arraycopy(mUids, 0, newuids, 0, index);
734 }
735 if (index < mNum) {
736 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
737 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700738 mUids = newuids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800739 mUids[index] = uid;
740 mNum++;
741 } else {
742 if (index < mNum) {
743 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
744 }
745 mUids[index] = uid;
746 mNum++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700747 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800748 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700749
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800750 private void insert(int index, int uid, String name) {
751 if (mUids == null) {
752 mUids = new int[4];
753 mUids[0] = uid;
754 mNames = new String[4];
755 mNames[0] = name;
756 mNum = 1;
757 } else if (mNum >= mUids.length) {
758 int[] newuids = new int[(mNum*3)/2];
759 String[] newnames = new String[(mNum*3)/2];
760 if (index > 0) {
761 System.arraycopy(mUids, 0, newuids, 0, index);
762 System.arraycopy(mNames, 0, newnames, 0, index);
763 }
764 if (index < mNum) {
765 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
766 System.arraycopy(mNames, index, newnames, index+1, mNum-index);
767 }
768 mUids = newuids;
769 mNames = newnames;
770 mUids[index] = uid;
771 mNames[index] = name;
772 mNum++;
773 } else {
774 if (index < mNum) {
775 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
776 System.arraycopy(mNames, index, mNames, index+1, mNum-index);
777 }
778 mUids[index] = uid;
779 mNames[index] = name;
780 mNum++;
781 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700782 }
783
Narayan Kamathf0202a92017-12-07 15:45:33 +0000784 /**
785 * Represents an attribution chain for an item of work being performed. An attribution chain is
786 * an indexed list of {code (uid, tag)} nodes. The node at {@code index == 0} is the initiator
787 * of the work, and the node at the highest index performs the work. Nodes at other indices
788 * are intermediaries that facilitate the work. Examples :
789 *
790 * (1) Work being performed by uid=2456 (no chaining):
791 * <pre>
792 * WorkChain {
793 * mUids = { 2456 }
794 * mTags = { null }
795 * mSize = 1;
796 * }
797 * </pre>
798 *
799 * (2) Work being performed by uid=2456 (from component "c1") on behalf of uid=5678:
800 *
801 * <pre>
802 * WorkChain {
803 * mUids = { 5678, 2456 }
804 * mTags = { null, "c1" }
805 * mSize = 1
806 * }
807 * </pre>
808 *
809 * Attribution chains are mutable, though the only operation that can be performed on them
810 * is the addition of a new node at the end of the attribution chain to represent
811 *
812 * @hide
813 */
814 public static class WorkChain implements Parcelable {
815 private int mSize;
816 private int[] mUids;
817 private String[] mTags;
818
819 // @VisibleForTesting
820 public WorkChain() {
821 mSize = 0;
822 mUids = new int[4];
823 mTags = new String[4];
824 }
825
826 // @VisibleForTesting
827 public WorkChain(WorkChain other) {
828 mSize = other.mSize;
829 mUids = other.mUids.clone();
830 mTags = other.mTags.clone();
831 }
832
833 private WorkChain(Parcel in) {
834 mSize = in.readInt();
835 mUids = in.createIntArray();
836 mTags = in.createStringArray();
837 }
838
839 /**
840 * Append a node whose uid is {@code uid} and whose optional tag is {@code tag} to this
841 * {@code WorkChain}.
842 */
843 public WorkChain addNode(int uid, @Nullable String tag) {
844 if (mSize == mUids.length) {
845 resizeArrays();
846 }
847
848 mUids[mSize] = uid;
849 mTags[mSize] = tag;
850 mSize++;
851
852 return this;
853 }
854
Narayan Kamath81822022017-12-08 11:56:01 +0000855 /**
Narayan Kamathcbe06772017-12-27 14:22:47 +0000856 * Return the UID to which this WorkChain should be attributed to, i.e, the UID that
857 * initiated the work and not the UID performing it.
Narayan Kamath81822022017-12-08 11:56:01 +0000858 */
859 public int getAttributionUid() {
Narayan Kamathcbe06772017-12-27 14:22:47 +0000860 return mUids[0];
Narayan Kamath81822022017-12-08 11:56:01 +0000861 }
862
Narayan Kamathf0202a92017-12-07 15:45:33 +0000863 // TODO: The following three trivial getters are purely for testing and will be removed
864 // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
865 // diffing it etc.
866 //
867 // @VisibleForTesting
868 public int[] getUids() {
869 return mUids;
870 }
871 // @VisibleForTesting
872 public String[] getTags() {
873 return mTags;
874 }
875 // @VisibleForTesting
876 public int getSize() {
877 return mSize;
878 }
879
880 private void resizeArrays() {
881 final int newSize = mSize * 2;
882 int[] uids = new int[newSize];
883 String[] tags = new String[newSize];
884
885 System.arraycopy(mUids, 0, uids, 0, mSize);
886 System.arraycopy(mTags, 0, tags, 0, mSize);
887
888 mUids = uids;
889 mTags = tags;
890 }
891
892 @Override
893 public String toString() {
894 StringBuilder result = new StringBuilder("WorkChain{");
895 for (int i = 0; i < mSize; ++i) {
896 if (i != 0) {
897 result.append(", ");
898 }
899 result.append("(");
900 result.append(mUids[i]);
901 if (mTags[i] != null) {
902 result.append(", ");
903 result.append(mTags[i]);
904 }
905 result.append(")");
906 }
907
908 result.append("}");
909 return result.toString();
910 }
911
912 @Override
913 public int hashCode() {
914 return (mSize + 31 * Arrays.hashCode(mUids)) * 31 + Arrays.hashCode(mTags);
915 }
916
917 @Override
918 public boolean equals(Object o) {
919 if (o instanceof WorkChain) {
920 WorkChain other = (WorkChain) o;
921
922 return mSize == other.mSize
923 && Arrays.equals(mUids, other.mUids)
924 && Arrays.equals(mTags, other.mTags);
925 }
926
927 return false;
928 }
929
930 @Override
931 public int describeContents() {
932 return 0;
933 }
934
935 @Override
936 public void writeToParcel(Parcel dest, int flags) {
937 dest.writeInt(mSize);
938 dest.writeIntArray(mUids);
939 dest.writeStringArray(mTags);
940 }
941
942 public static final Parcelable.Creator<WorkChain> CREATOR =
943 new Parcelable.Creator<WorkChain>() {
944 public WorkChain createFromParcel(Parcel in) {
945 return new WorkChain(in);
946 }
947 public WorkChain[] newArray(int size) {
948 return new WorkChain[size];
949 }
950 };
951 }
952
Narayan Kamath81822022017-12-08 11:56:01 +0000953 /**
954 * Computes the differences in WorkChains contained between {@code oldWs} and {@code newWs}.
955 *
956 * Returns {@code null} if no differences exist, otherwise returns a two element array. The
957 * first element is a list of "new" chains, i.e WorkChains present in {@code newWs} but not in
958 * {@code oldWs}. The second element is a list of "gone" chains, i.e WorkChains present in
959 * {@code oldWs} but not in {@code newWs}.
960 *
961 * @hide
962 */
963 public static ArrayList<WorkChain>[] diffChains(WorkSource oldWs, WorkSource newWs) {
964 ArrayList<WorkChain> newChains = null;
965 ArrayList<WorkChain> goneChains = null;
966
967 // TODO(narayan): This is a dumb O(M*N) algorithm that determines what has changed across
968 // WorkSource objects. We can replace this with something smarter, for e.g by defining
969 // a Comparator between WorkChains. It's unclear whether that will be more efficient if
970 // the number of chains associated with a WorkSource is expected to be small
971 if (oldWs.mChains != null) {
972 for (int i = 0; i < oldWs.mChains.size(); ++i) {
973 final WorkChain wc = oldWs.mChains.get(i);
974 if (newWs.mChains == null || !newWs.mChains.contains(wc)) {
975 if (goneChains == null) {
976 goneChains = new ArrayList<>(oldWs.mChains.size());
977 }
978 goneChains.add(wc);
979 }
980 }
981 }
982
983 if (newWs.mChains != null) {
984 for (int i = 0; i < newWs.mChains.size(); ++i) {
985 final WorkChain wc = newWs.mChains.get(i);
986 if (oldWs.mChains == null || !oldWs.mChains.contains(wc)) {
987 if (newChains == null) {
988 newChains = new ArrayList<>(newWs.mChains.size());
989 }
990 newChains.add(wc);
991 }
992 }
993 }
994
995 if (newChains != null || goneChains != null) {
996 return new ArrayList[] { newChains, goneChains };
997 }
998
999 return null;
1000 }
1001
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001002 @Override
1003 public int describeContents() {
1004 return 0;
1005 }
1006
1007 @Override
1008 public void writeToParcel(Parcel dest, int flags) {
1009 dest.writeInt(mNum);
1010 dest.writeIntArray(mUids);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001011 dest.writeStringArray(mNames);
Narayan Kamathf0202a92017-12-07 15:45:33 +00001012
1013 if (mChains == null) {
1014 dest.writeInt(-1);
1015 } else {
1016 dest.writeInt(mChains.size());
1017 dest.writeParcelableList(mChains, flags);
1018 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001019 }
1020
Jeff Brown96307042012-07-27 15:51:34 -07001021 @Override
1022 public String toString() {
1023 StringBuilder result = new StringBuilder();
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001024 result.append("WorkSource{");
Jeff Brown96307042012-07-27 15:51:34 -07001025 for (int i = 0; i < mNum; i++) {
1026 if (i != 0) {
1027 result.append(", ");
1028 }
1029 result.append(mUids[i]);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001030 if (mNames != null) {
1031 result.append(" ");
1032 result.append(mNames[i]);
1033 }
Jeff Brown96307042012-07-27 15:51:34 -07001034 }
Narayan Kamathf0202a92017-12-07 15:45:33 +00001035
1036 if (mChains != null) {
1037 result.append(" chains=");
1038 for (int i = 0; i < mChains.size(); ++i) {
1039 if (i != 0) {
1040 result.append(", ");
1041 }
1042 result.append(mChains.get(i));
1043 }
1044 }
1045
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001046 result.append("}");
Jeff Brown96307042012-07-27 15:51:34 -07001047 return result.toString();
1048 }
1049
Netta P958d0a52017-02-07 11:20:55 -08001050 /** @hide */
1051 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1052 final long workSourceToken = proto.start(fieldId);
1053 for (int i = 0; i < mNum; i++) {
1054 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1055 proto.write(WorkSourceProto.WorkSourceContentProto.UID, mUids[i]);
1056 if (mNames != null) {
1057 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, mNames[i]);
1058 }
1059 proto.end(contentProto);
1060 }
Narayan Kamath80434a72017-12-27 15:44:17 +00001061
1062 if (mChains != null) {
1063 for (int i = 0; i < mChains.size(); i++) {
1064 final WorkChain wc = mChains.get(i);
1065 final long workChain = proto.start(WorkSourceProto.WORK_CHAINS);
1066
1067 final String[] tags = wc.getTags();
1068 final int[] uids = wc.getUids();
1069 for (int j = 0; j < tags.length; j++) {
1070 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1071 proto.write(WorkSourceProto.WorkSourceContentProto.UID, uids[j]);
1072 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, tags[j]);
1073 proto.end(contentProto);
1074 }
1075
1076 proto.end(workChain);
1077 }
1078 }
1079
Netta P958d0a52017-02-07 11:20:55 -08001080 proto.end(workSourceToken);
1081 }
1082
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001083 public static final Parcelable.Creator<WorkSource> CREATOR
1084 = new Parcelable.Creator<WorkSource>() {
1085 public WorkSource createFromParcel(Parcel in) {
1086 return new WorkSource(in);
1087 }
1088 public WorkSource[] newArray(int size) {
1089 return new WorkSource[size];
1090 }
1091 };
1092}