blob: d0c2870bfefb143b19d52a89383563cbeff22be5 [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;
10
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070011/**
12 * Describes the source of some work that may be done by someone else.
13 * Currently the public representation of what a work source is is not
14 * defined; this is an opaque container.
15 */
16public class WorkSource implements Parcelable {
Dianne Hackborn002a54e2013-01-10 17:34:55 -080017 static final String TAG = "WorkSource";
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080018 static final boolean DEBUG = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080019
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070020 int mNum;
21 int[] mUids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080022 String[] mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070023
Narayan Kamathf0202a92017-12-07 15:45:33 +000024 private ArrayList<WorkChain> mChains;
25
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070026 /**
27 * Internal statics to avoid object allocations in some operations.
28 * The WorkSource object itself is not thread safe, but we need to
29 * hold sTmpWorkSource lock while working with these statics.
30 */
31 static final WorkSource sTmpWorkSource = new WorkSource(0);
32 /**
33 * For returning newbie work from a modification operation.
34 */
35 static WorkSource sNewbWork;
36 /**
37 * For returning gone work form a modification operation.
38 */
39 static WorkSource sGoneWork;
40
41 /**
42 * Create an empty work source.
43 */
44 public WorkSource() {
45 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000046 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070047 }
48
49 /**
50 * Create a new WorkSource that is a copy of an existing one.
51 * If <var>orig</var> is null, an empty WorkSource is created.
52 */
53 public WorkSource(WorkSource orig) {
54 if (orig == null) {
55 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000056 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070057 return;
58 }
59 mNum = orig.mNum;
60 if (orig.mUids != null) {
61 mUids = orig.mUids.clone();
Dianne Hackborn002a54e2013-01-10 17:34:55 -080062 mNames = orig.mNames != null ? orig.mNames.clone() : null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070063 } else {
64 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080065 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070066 }
Narayan Kamathf0202a92017-12-07 15:45:33 +000067
68 if (orig.mChains != null) {
69 // Make a copy of all WorkChains that exist on |orig| since they are mutable.
70 mChains = new ArrayList<>(orig.mChains.size());
71 for (WorkChain chain : orig.mChains) {
72 mChains.add(new WorkChain(chain));
73 }
74 } else {
75 mChains = null;
76 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070077 }
78
79 /** @hide */
80 public WorkSource(int uid) {
81 mNum = 1;
82 mUids = new int[] { uid, 0 };
Dianne Hackborn002a54e2013-01-10 17:34:55 -080083 mNames = null;
Narayan Kamathf0202a92017-12-07 15:45:33 +000084 mChains = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080085 }
86
87 /** @hide */
88 public WorkSource(int uid, String name) {
89 if (name == null) {
90 throw new NullPointerException("Name can't be null");
91 }
92 mNum = 1;
93 mUids = new int[] { uid, 0 };
94 mNames = new String[] { name, null };
Narayan Kamathf0202a92017-12-07 15:45:33 +000095 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070096 }
97
98 WorkSource(Parcel in) {
99 mNum = in.readInt();
100 mUids = in.createIntArray();
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800101 mNames = in.createStringArray();
Narayan Kamathf0202a92017-12-07 15:45:33 +0000102
103 int numChains = in.readInt();
104 if (numChains > 0) {
105 mChains = new ArrayList<>(numChains);
106 in.readParcelableList(mChains, WorkChain.class.getClassLoader());
107 } else {
108 mChains = null;
109 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700110 }
111
112 /** @hide */
113 public int size() {
114 return mNum;
115 }
116
117 /** @hide */
118 public int get(int index) {
119 return mUids[index];
120 }
121
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800122 /** @hide */
123 public String getName(int index) {
124 return mNames != null ? mNames[index] : null;
125 }
126
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700127 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000128 * Clear names from this WorkSource. Uids are left intact. WorkChains if any, are left
129 * intact.
David Christie6ab22842013-09-13 17:11:53 -0700130 *
131 * <p>Useful when combining with another WorkSource that doesn't have names.
132 * @hide
133 */
134 public void clearNames() {
David Christiea31510e2013-09-20 10:44:01 -0700135 if (mNames != null) {
136 mNames = null;
137 // Clear out any duplicate uids now that we don't have names to disambiguate them.
138 int destIndex = 1;
139 int newNum = mNum;
140 for (int sourceIndex = 1; sourceIndex < mNum; sourceIndex++) {
141 if (mUids[sourceIndex] == mUids[sourceIndex - 1]) {
142 newNum--;
143 } else {
144 mUids[destIndex] = mUids[sourceIndex];
145 destIndex++;
146 }
147 }
148 mNum = newNum;
149 }
David Christie6ab22842013-09-13 17:11:53 -0700150 }
151
152 /**
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700153 * Clear this WorkSource to be empty.
154 */
155 public void clear() {
156 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000157 if (mChains != null) {
158 mChains.clear();
159 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700160 }
161
Jeff Brown94838912012-07-27 12:04:37 -0700162 @Override
163 public boolean equals(Object o) {
Narayan Kamatheb279462018-01-09 16:09:35 +0000164 if (o instanceof WorkSource) {
165 WorkSource other = (WorkSource) o;
166
167 if (diff(other)) {
168 return false;
169 }
170
171 if (mChains != null && !mChains.isEmpty()) {
172 return mChains.equals(other.mChains);
173 } else {
174 return other.mChains == null || other.mChains.isEmpty();
175 }
176 }
177
178 return false;
Jeff Brown94838912012-07-27 12:04:37 -0700179 }
180
181 @Override
182 public int hashCode() {
183 int result = 0;
184 for (int i = 0; i < mNum; i++) {
185 result = ((result << 4) | (result >>> 28)) ^ mUids[i];
186 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800187 if (mNames != null) {
188 for (int i = 0; i < mNum; i++) {
189 result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
190 }
191 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000192
193 if (mChains != null) {
194 result = ((result << 4) | (result >>> 28)) ^ mChains.hashCode();
195 }
196
Jeff Brown94838912012-07-27 12:04:37 -0700197 return result;
198 }
199
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700200 /**
201 * Compare this WorkSource with another.
202 * @param other The WorkSource to compare against.
203 * @return If there is a difference, true is returned.
204 */
Narayan Kamathf0202a92017-12-07 15:45:33 +0000205 // TODO: This is a public API so it cannot be renamed. Because it is used in several places,
206 // we keep its semantics the same and ignore any differences in WorkChains (if any).
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700207 public boolean diff(WorkSource other) {
208 int N = mNum;
209 if (N != other.mNum) {
210 return true;
211 }
212 final int[] uids1 = mUids;
213 final int[] uids2 = other.mUids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800214 final String[] names1 = mNames;
215 final String[] names2 = other.mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700216 for (int i=0; i<N; i++) {
217 if (uids1[i] != uids2[i]) {
218 return true;
219 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800220 if (names1 != null && names2 != null && !names1[i].equals(names2[i])) {
221 return true;
222 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700223 }
224 return false;
225 }
226
227 /**
228 * Replace the current contents of this work source with the given
Narayan Kamathf0202a92017-12-07 15:45:33 +0000229 * work source. If {@code other} is null, the current work source
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700230 * will be made empty.
231 */
232 public void set(WorkSource other) {
233 if (other == null) {
234 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000235 if (mChains != null) {
236 mChains.clear();
237 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700238 return;
239 }
240 mNum = other.mNum;
241 if (other.mUids != null) {
242 if (mUids != null && mUids.length >= mNum) {
243 System.arraycopy(other.mUids, 0, mUids, 0, mNum);
244 } else {
245 mUids = other.mUids.clone();
246 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800247 if (other.mNames != null) {
248 if (mNames != null && mNames.length >= mNum) {
249 System.arraycopy(other.mNames, 0, mNames, 0, mNum);
250 } else {
251 mNames = other.mNames.clone();
252 }
253 } else {
254 mNames = null;
255 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700256 } else {
257 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800258 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700259 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000260
261 if (other.mChains != null) {
262 if (mChains != null) {
263 mChains.clear();
264 } else {
265 mChains = new ArrayList<>(other.mChains.size());
266 }
267
268 for (WorkChain chain : other.mChains) {
269 mChains.add(new WorkChain(chain));
270 }
271 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700272 }
273
274 /** @hide */
275 public void set(int uid) {
276 mNum = 1;
277 if (mUids == null) mUids = new int[2];
278 mUids[0] = uid;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800279 mNames = null;
Narayan Kamath6192f732017-12-21 09:43:38 +0000280 if (mChains != null) {
281 mChains.clear();
282 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800283 }
284
285 /** @hide */
286 public void set(int uid, String name) {
287 if (name == null) {
288 throw new NullPointerException("Name can't be null");
289 }
290 mNum = 1;
291 if (mUids == null) {
292 mUids = new int[2];
293 mNames = new String[2];
294 }
295 mUids[0] = uid;
296 mNames[0] = name;
Narayan Kamath6192f732017-12-21 09:43:38 +0000297 if (mChains != null) {
298 mChains.clear();
299 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700300 }
301
Narayan Kamathf0202a92017-12-07 15:45:33 +0000302 /**
303 * Legacy API, DO NOT USE: Only deals with flat UIDs and tags. No chains are transferred, and no
304 * differences in chains are returned. This will be removed once its callers have been
305 * rewritten.
306 *
307 * NOTE: This is currently only used in GnssLocationProvider.
308 *
309 * @hide
310 * @deprecated for internal use only. WorkSources are opaque and no external callers should need
311 * to be aware of internal differences.
312 */
313 @Deprecated
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700314 public WorkSource[] setReturningDiffs(WorkSource other) {
315 synchronized (sTmpWorkSource) {
316 sNewbWork = null;
317 sGoneWork = null;
318 updateLocked(other, true, true);
319 if (sNewbWork != null || sGoneWork != null) {
320 WorkSource[] diffs = new WorkSource[2];
321 diffs[0] = sNewbWork;
322 diffs[1] = sGoneWork;
323 return diffs;
324 }
325 return null;
326 }
327 }
328
329 /**
330 * Merge the contents of <var>other</var> WorkSource in to this one.
331 *
332 * @param other The other WorkSource whose contents are to be merged.
333 * @return Returns true if any new sources were added.
334 */
335 public boolean add(WorkSource other) {
336 synchronized (sTmpWorkSource) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000337 boolean uidAdded = updateLocked(other, false, false);
338
339 boolean chainAdded = false;
340 if (other.mChains != null) {
341 // NOTE: This is quite an expensive operation, especially if the number of chains
342 // is large. We could look into optimizing it if it proves problematic.
343 if (mChains == null) {
344 mChains = new ArrayList<>(other.mChains.size());
345 }
346
347 for (WorkChain wc : other.mChains) {
348 if (!mChains.contains(wc)) {
349 mChains.add(new WorkChain(wc));
350 }
351 }
352 }
353
354 return uidAdded || chainAdded;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700355 }
356 }
357
Narayan Kamathf0202a92017-12-07 15:45:33 +0000358 /**
359 * Legacy API: DO NOT USE. Only in use from unit tests.
360 *
361 * @hide
362 * @deprecated meant for unit testing use only. Will be removed in a future API revision.
363 */
364 @Deprecated
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700365 public WorkSource addReturningNewbs(WorkSource other) {
366 synchronized (sTmpWorkSource) {
367 sNewbWork = null;
368 updateLocked(other, false, true);
369 return sNewbWork;
370 }
371 }
372
373 /** @hide */
374 public boolean add(int uid) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800375 if (mNum <= 0) {
376 mNames = null;
377 insert(0, uid);
378 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700379 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800380 if (mNames != null) {
381 throw new IllegalArgumentException("Adding without name to named " + this);
382 }
383 int i = Arrays.binarySearch(mUids, 0, mNum, uid);
384 if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i);
385 if (i >= 0) {
386 return false;
387 }
388 insert(-i-1, uid);
389 return true;
390 }
391
392 /** @hide */
393 public boolean add(int uid, String name) {
394 if (mNum <= 0) {
395 insert(0, uid, name);
396 return true;
397 }
398 if (mNames == null) {
399 throw new IllegalArgumentException("Adding name to unnamed " + this);
400 }
401 int i;
402 for (i=0; i<mNum; i++) {
403 if (mUids[i] > uid) {
404 break;
405 }
406 if (mUids[i] == uid) {
Netta P958d0a52017-02-07 11:20:55 -0800407 int diff = mNames[i].compareTo(name);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800408 if (diff > 0) {
409 break;
410 }
411 if (diff == 0) {
412 return false;
413 }
414 }
415 }
416 insert(i, uid, name);
417 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700418 }
419
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700420 public boolean remove(WorkSource other) {
Narayan Kamathee07f622018-01-08 12:20:28 +0000421 if (isEmpty() || other.isEmpty()) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800422 return false;
423 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000424
Narayan Kamathee07f622018-01-08 12:20:28 +0000425 boolean uidRemoved;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800426 if (mNames == null && other.mNames == null) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000427 uidRemoved = removeUids(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800428 } else {
429 if (mNames == null) {
430 throw new IllegalArgumentException("Other " + other + " has names, but target "
431 + this + " does not");
432 }
433 if (other.mNames == null) {
434 throw new IllegalArgumentException("Target " + this + " has names, but other "
435 + other + " does not");
436 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000437 uidRemoved = removeUidsAndNames(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800438 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000439
440 boolean chainRemoved = false;
Narayan Kamathee07f622018-01-08 12:20:28 +0000441 if (other.mChains != null && mChains != null) {
442 chainRemoved = mChains.removeAll(other.mChains);
Narayan Kamathf0202a92017-12-07 15:45:33 +0000443 }
444
445 return uidRemoved || chainRemoved;
446 }
447
448 /**
449 * Create a new {@code WorkChain} associated with this WorkSource and return it.
450 *
451 * @hide
452 */
453 public WorkChain createWorkChain() {
454 if (mChains == null) {
455 mChains = new ArrayList<>(4);
456 }
457
458 final WorkChain wc = new WorkChain();
459 mChains.add(wc);
460
461 return wc;
462 }
463
464 /**
Narayan Kamath81822022017-12-08 11:56:01 +0000465 * Returns {@code true} iff. this work source contains zero UIDs and zero WorkChains to
466 * attribute usage to.
467 *
468 * @hide for internal use only.
469 */
470 public boolean isEmpty() {
471 return mNum == 0 && (mChains == null || mChains.isEmpty());
472 }
473
474 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000475 * @return the list of {@code WorkChains} associated with this {@code WorkSource}.
476 * @hide
477 */
478 public ArrayList<WorkChain> getWorkChains() {
479 return mChains;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800480 }
481
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800482 private boolean removeUids(WorkSource other) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700483 int N1 = mNum;
484 final int[] uids1 = mUids;
485 final int N2 = other.mNum;
486 final int[] uids2 = other.mUids;
487 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800488 int i1 = 0, i2 = 0;
489 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
490 while (i1 < N1 && i2 < N2) {
491 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
492 + " of " + N2);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700493 if (uids2[i2] == uids1[i1]) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800494 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
495 + ": remove " + uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700496 N1--;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800497 changed = true;
Dianne Hackborn83770282010-09-14 11:45:44 -0700498 if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800499 i2++;
500 } else if (uids2[i2] > uids1[i1]) {
501 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700502 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800503 } else {
504 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
505 i2++;
506 }
507 }
508
509 mNum = N1;
510
511 return changed;
512 }
513
514 private boolean removeUidsAndNames(WorkSource other) {
515 int N1 = mNum;
516 final int[] uids1 = mUids;
517 final String[] names1 = mNames;
518 final int N2 = other.mNum;
519 final int[] uids2 = other.mUids;
520 final String[] names2 = other.mNames;
521 boolean changed = false;
522 int i1 = 0, i2 = 0;
523 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
524 while (i1 < N1 && i2 < N2) {
525 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
526 + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]);
527 if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) {
528 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
529 + ": remove " + uids1[i1] + " " + names1[i1]);
530 N1--;
531 changed = true;
532 if (i1 < N1) {
533 System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
534 System.arraycopy(names1, i1+1, names1, i1, N1-i1);
535 }
536 i2++;
537 } else if (uids2[i2] > uids1[i1]
538 || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) {
539 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
540 i1++;
541 } else {
542 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
543 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700544 }
545 }
546
547 mNum = N1;
548
549 return changed;
550 }
551
552 private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800553 if (mNames == null && other.mNames == null) {
554 return updateUidsLocked(other, set, returnNewbs);
555 } else {
556 if (mNum > 0 && mNames == null) {
557 throw new IllegalArgumentException("Other " + other + " has names, but target "
558 + this + " does not");
559 }
560 if (other.mNum > 0 && other.mNames == null) {
561 throw new IllegalArgumentException("Target " + this + " has names, but other "
562 + other + " does not");
563 }
564 return updateUidsAndNamesLocked(other, set, returnNewbs);
565 }
566 }
567
568 private static WorkSource addWork(WorkSource cur, int newUid) {
569 if (cur == null) {
570 return new WorkSource(newUid);
571 }
572 cur.insert(cur.mNum, newUid);
573 return cur;
574 }
575
576 private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700577 int N1 = mNum;
578 int[] uids1 = mUids;
579 final int N2 = other.mNum;
580 final int[] uids2 = other.mUids;
581 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800582 int i1 = 0, i2 = 0;
583 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
584 + " returnNewbs=" + returnNewbs);
585 while (i1 < N1 || i2 < N2) {
586 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
587 + " of " + N2);
588 if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700589 // Need to insert a new uid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800590 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
591 + ": insert " + uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700592 changed = true;
593 if (uids1 == null) {
594 uids1 = new int[4];
595 uids1[0] = uids2[i2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800596 } else if (N1 >= uids1.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700597 int[] newuids = new int[(uids1.length*3)/2];
598 if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
599 if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
600 uids1 = newuids;
601 uids1[i1] = uids2[i2];
602 } else {
603 if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
604 uids1[i1] = uids2[i2];
605 }
606 if (returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800607 sNewbWork = addWork(sNewbWork, uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700608 }
609 N1++;
610 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800611 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700612 } else {
613 if (!set) {
614 // Skip uids that already exist or are not in 'other'.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800615 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
616 if (i2 < N2 && uids2[i2] == uids1[i1]) {
617 i2++;
618 }
619 i1++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700620 } else {
621 // Remove any uids that don't exist in 'other'.
622 int start = i1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800623 while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) {
624 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]);
625 sGoneWork = addWork(sGoneWork, uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700626 i1++;
627 }
628 if (start < i1) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800629 System.arraycopy(uids1, i1, uids1, start, N1-i1);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700630 N1 -= i1-start;
631 i1 = start;
632 }
633 // If there is a matching uid, skip it.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800634 if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) {
635 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700636 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800637 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700638 }
639 }
640 }
641 }
642
643 mNum = N1;
644 mUids = uids1;
645
646 return changed;
647 }
648
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800649 /**
650 * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'.
651 */
652 private int compare(WorkSource other, int i1, int i2) {
653 final int diff = mUids[i1] - other.mUids[i2];
654 if (diff != 0) {
655 return diff;
656 }
657 return mNames[i1].compareTo(other.mNames[i2]);
658 }
659
660 private static WorkSource addWork(WorkSource cur, int newUid, String newName) {
661 if (cur == null) {
662 return new WorkSource(newUid, newName);
663 }
664 cur.insert(cur.mNum, newUid, newName);
665 return cur;
666 }
667
668 private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) {
669 final int N2 = other.mNum;
670 final int[] uids2 = other.mUids;
671 String[] names2 = other.mNames;
672 boolean changed = false;
673 int i1 = 0, i2 = 0;
674 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
675 + " returnNewbs=" + returnNewbs);
676 while (i1 < mNum || i2 < N2) {
677 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2
678 + " of " + N2);
679 int diff = -1;
680 if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) {
681 // Need to insert a new uid.
682 changed = true;
683 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum
684 + ": insert " + uids2[i2] + " " + names2[i2]);
685 insert(i1, uids2[i2], names2[i2]);
686 if (returnNewbs) {
687 sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]);
688 }
689 i1++;
690 i2++;
691 } else {
692 if (!set) {
693 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
694 if (i2 < N2 && diff == 0) {
695 i2++;
696 }
697 i1++;
698 } else {
699 // Remove any uids that don't exist in 'other'.
700 int start = i1;
701 while (diff < 0) {
702 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1]
703 + " " + mNames[i1]);
704 sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]);
705 i1++;
706 if (i1 >= mNum) {
707 break;
708 }
709 diff = i2 < N2 ? compare(other, i1, i2) : -1;
710 }
711 if (start < i1) {
712 System.arraycopy(mUids, i1, mUids, start, mNum-i1);
713 System.arraycopy(mNames, i1, mNames, start, mNum-i1);
714 mNum -= i1-start;
715 i1 = start;
716 }
717 // If there is a matching uid, skip it.
718 if (i1 < mNum && diff == 0) {
719 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
720 i1++;
721 i2++;
722 }
723 }
724 }
725 }
726
727 return changed;
728 }
729
730 private void insert(int index, int uid) {
731 if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700732 if (mUids == null) {
733 mUids = new int[4];
734 mUids[0] = uid;
735 mNum = 1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800736 } else if (mNum >= mUids.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700737 int[] newuids = new int[(mNum*3)/2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800738 if (index > 0) {
739 System.arraycopy(mUids, 0, newuids, 0, index);
740 }
741 if (index < mNum) {
742 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
743 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700744 mUids = newuids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800745 mUids[index] = uid;
746 mNum++;
747 } else {
748 if (index < mNum) {
749 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
750 }
751 mUids[index] = uid;
752 mNum++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700753 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800754 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700755
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800756 private void insert(int index, int uid, String name) {
757 if (mUids == null) {
758 mUids = new int[4];
759 mUids[0] = uid;
760 mNames = new String[4];
761 mNames[0] = name;
762 mNum = 1;
763 } else if (mNum >= mUids.length) {
764 int[] newuids = new int[(mNum*3)/2];
765 String[] newnames = new String[(mNum*3)/2];
766 if (index > 0) {
767 System.arraycopy(mUids, 0, newuids, 0, index);
768 System.arraycopy(mNames, 0, newnames, 0, index);
769 }
770 if (index < mNum) {
771 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
772 System.arraycopy(mNames, index, newnames, index+1, mNum-index);
773 }
774 mUids = newuids;
775 mNames = newnames;
776 mUids[index] = uid;
777 mNames[index] = name;
778 mNum++;
779 } else {
780 if (index < mNum) {
781 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
782 System.arraycopy(mNames, index, mNames, index+1, mNum-index);
783 }
784 mUids[index] = uid;
785 mNames[index] = name;
786 mNum++;
787 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700788 }
789
Narayan Kamathf0202a92017-12-07 15:45:33 +0000790 /**
791 * Represents an attribution chain for an item of work being performed. An attribution chain is
792 * an indexed list of {code (uid, tag)} nodes. The node at {@code index == 0} is the initiator
793 * of the work, and the node at the highest index performs the work. Nodes at other indices
794 * are intermediaries that facilitate the work. Examples :
795 *
796 * (1) Work being performed by uid=2456 (no chaining):
797 * <pre>
798 * WorkChain {
799 * mUids = { 2456 }
800 * mTags = { null }
801 * mSize = 1;
802 * }
803 * </pre>
804 *
805 * (2) Work being performed by uid=2456 (from component "c1") on behalf of uid=5678:
806 *
807 * <pre>
808 * WorkChain {
809 * mUids = { 5678, 2456 }
810 * mTags = { null, "c1" }
811 * mSize = 1
812 * }
813 * </pre>
814 *
815 * Attribution chains are mutable, though the only operation that can be performed on them
816 * is the addition of a new node at the end of the attribution chain to represent
817 *
818 * @hide
819 */
820 public static class WorkChain implements Parcelable {
821 private int mSize;
822 private int[] mUids;
823 private String[] mTags;
824
825 // @VisibleForTesting
826 public WorkChain() {
827 mSize = 0;
828 mUids = new int[4];
829 mTags = new String[4];
830 }
831
832 // @VisibleForTesting
833 public WorkChain(WorkChain other) {
834 mSize = other.mSize;
835 mUids = other.mUids.clone();
836 mTags = other.mTags.clone();
837 }
838
839 private WorkChain(Parcel in) {
840 mSize = in.readInt();
841 mUids = in.createIntArray();
842 mTags = in.createStringArray();
843 }
844
845 /**
846 * Append a node whose uid is {@code uid} and whose optional tag is {@code tag} to this
847 * {@code WorkChain}.
848 */
849 public WorkChain addNode(int uid, @Nullable String tag) {
850 if (mSize == mUids.length) {
851 resizeArrays();
852 }
853
854 mUids[mSize] = uid;
855 mTags[mSize] = tag;
856 mSize++;
857
858 return this;
859 }
860
Narayan Kamath81822022017-12-08 11:56:01 +0000861 /**
Narayan Kamathcbe06772017-12-27 14:22:47 +0000862 * Return the UID to which this WorkChain should be attributed to, i.e, the UID that
863 * initiated the work and not the UID performing it.
Narayan Kamath81822022017-12-08 11:56:01 +0000864 */
865 public int getAttributionUid() {
Narayan Kamathcbe06772017-12-27 14:22:47 +0000866 return mUids[0];
Narayan Kamath81822022017-12-08 11:56:01 +0000867 }
868
Narayan Kamathf0202a92017-12-07 15:45:33 +0000869 // TODO: The following three trivial getters are purely for testing and will be removed
870 // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
871 // diffing it etc.
872 //
873 // @VisibleForTesting
874 public int[] getUids() {
875 return mUids;
876 }
877 // @VisibleForTesting
878 public String[] getTags() {
879 return mTags;
880 }
881 // @VisibleForTesting
882 public int getSize() {
883 return mSize;
884 }
885
886 private void resizeArrays() {
887 final int newSize = mSize * 2;
888 int[] uids = new int[newSize];
889 String[] tags = new String[newSize];
890
891 System.arraycopy(mUids, 0, uids, 0, mSize);
892 System.arraycopy(mTags, 0, tags, 0, mSize);
893
894 mUids = uids;
895 mTags = tags;
896 }
897
898 @Override
899 public String toString() {
900 StringBuilder result = new StringBuilder("WorkChain{");
901 for (int i = 0; i < mSize; ++i) {
902 if (i != 0) {
903 result.append(", ");
904 }
905 result.append("(");
906 result.append(mUids[i]);
907 if (mTags[i] != null) {
908 result.append(", ");
909 result.append(mTags[i]);
910 }
911 result.append(")");
912 }
913
914 result.append("}");
915 return result.toString();
916 }
917
918 @Override
919 public int hashCode() {
920 return (mSize + 31 * Arrays.hashCode(mUids)) * 31 + Arrays.hashCode(mTags);
921 }
922
923 @Override
924 public boolean equals(Object o) {
925 if (o instanceof WorkChain) {
926 WorkChain other = (WorkChain) o;
927
928 return mSize == other.mSize
929 && Arrays.equals(mUids, other.mUids)
930 && Arrays.equals(mTags, other.mTags);
931 }
932
933 return false;
934 }
935
936 @Override
937 public int describeContents() {
938 return 0;
939 }
940
941 @Override
942 public void writeToParcel(Parcel dest, int flags) {
943 dest.writeInt(mSize);
944 dest.writeIntArray(mUids);
945 dest.writeStringArray(mTags);
946 }
947
948 public static final Parcelable.Creator<WorkChain> CREATOR =
949 new Parcelable.Creator<WorkChain>() {
950 public WorkChain createFromParcel(Parcel in) {
951 return new WorkChain(in);
952 }
953 public WorkChain[] newArray(int size) {
954 return new WorkChain[size];
955 }
956 };
957 }
958
Narayan Kamath81822022017-12-08 11:56:01 +0000959 /**
960 * Computes the differences in WorkChains contained between {@code oldWs} and {@code newWs}.
961 *
962 * Returns {@code null} if no differences exist, otherwise returns a two element array. The
963 * first element is a list of "new" chains, i.e WorkChains present in {@code newWs} but not in
964 * {@code oldWs}. The second element is a list of "gone" chains, i.e WorkChains present in
965 * {@code oldWs} but not in {@code newWs}.
966 *
967 * @hide
968 */
969 public static ArrayList<WorkChain>[] diffChains(WorkSource oldWs, WorkSource newWs) {
970 ArrayList<WorkChain> newChains = null;
971 ArrayList<WorkChain> goneChains = null;
972
973 // TODO(narayan): This is a dumb O(M*N) algorithm that determines what has changed across
974 // WorkSource objects. We can replace this with something smarter, for e.g by defining
975 // a Comparator between WorkChains. It's unclear whether that will be more efficient if
976 // the number of chains associated with a WorkSource is expected to be small
977 if (oldWs.mChains != null) {
978 for (int i = 0; i < oldWs.mChains.size(); ++i) {
979 final WorkChain wc = oldWs.mChains.get(i);
980 if (newWs.mChains == null || !newWs.mChains.contains(wc)) {
981 if (goneChains == null) {
982 goneChains = new ArrayList<>(oldWs.mChains.size());
983 }
984 goneChains.add(wc);
985 }
986 }
987 }
988
989 if (newWs.mChains != null) {
990 for (int i = 0; i < newWs.mChains.size(); ++i) {
991 final WorkChain wc = newWs.mChains.get(i);
992 if (oldWs.mChains == null || !oldWs.mChains.contains(wc)) {
993 if (newChains == null) {
994 newChains = new ArrayList<>(newWs.mChains.size());
995 }
996 newChains.add(wc);
997 }
998 }
999 }
1000
1001 if (newChains != null || goneChains != null) {
1002 return new ArrayList[] { newChains, goneChains };
1003 }
1004
1005 return null;
1006 }
1007
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001008 @Override
1009 public int describeContents() {
1010 return 0;
1011 }
1012
1013 @Override
1014 public void writeToParcel(Parcel dest, int flags) {
1015 dest.writeInt(mNum);
1016 dest.writeIntArray(mUids);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001017 dest.writeStringArray(mNames);
Narayan Kamathf0202a92017-12-07 15:45:33 +00001018
1019 if (mChains == null) {
1020 dest.writeInt(-1);
1021 } else {
1022 dest.writeInt(mChains.size());
1023 dest.writeParcelableList(mChains, flags);
1024 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001025 }
1026
Jeff Brown96307042012-07-27 15:51:34 -07001027 @Override
1028 public String toString() {
1029 StringBuilder result = new StringBuilder();
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001030 result.append("WorkSource{");
Jeff Brown96307042012-07-27 15:51:34 -07001031 for (int i = 0; i < mNum; i++) {
1032 if (i != 0) {
1033 result.append(", ");
1034 }
1035 result.append(mUids[i]);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001036 if (mNames != null) {
1037 result.append(" ");
1038 result.append(mNames[i]);
1039 }
Jeff Brown96307042012-07-27 15:51:34 -07001040 }
Narayan Kamathf0202a92017-12-07 15:45:33 +00001041
1042 if (mChains != null) {
1043 result.append(" chains=");
1044 for (int i = 0; i < mChains.size(); ++i) {
1045 if (i != 0) {
1046 result.append(", ");
1047 }
1048 result.append(mChains.get(i));
1049 }
1050 }
1051
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001052 result.append("}");
Jeff Brown96307042012-07-27 15:51:34 -07001053 return result.toString();
1054 }
1055
Netta P958d0a52017-02-07 11:20:55 -08001056 /** @hide */
1057 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1058 final long workSourceToken = proto.start(fieldId);
1059 for (int i = 0; i < mNum; i++) {
1060 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1061 proto.write(WorkSourceProto.WorkSourceContentProto.UID, mUids[i]);
1062 if (mNames != null) {
1063 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, mNames[i]);
1064 }
1065 proto.end(contentProto);
1066 }
Narayan Kamath80434a72017-12-27 15:44:17 +00001067
1068 if (mChains != null) {
1069 for (int i = 0; i < mChains.size(); i++) {
1070 final WorkChain wc = mChains.get(i);
1071 final long workChain = proto.start(WorkSourceProto.WORK_CHAINS);
1072
1073 final String[] tags = wc.getTags();
1074 final int[] uids = wc.getUids();
1075 for (int j = 0; j < tags.length; j++) {
1076 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1077 proto.write(WorkSourceProto.WorkSourceContentProto.UID, uids[j]);
1078 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, tags[j]);
1079 proto.end(contentProto);
1080 }
1081
1082 proto.end(workChain);
1083 }
1084 }
1085
Netta P958d0a52017-02-07 11:20:55 -08001086 proto.end(workSourceToken);
1087 }
1088
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001089 public static final Parcelable.Creator<WorkSource> CREATOR
1090 = new Parcelable.Creator<WorkSource>() {
1091 public WorkSource createFromParcel(Parcel in) {
1092 return new WorkSource(in);
1093 }
1094 public WorkSource[] newArray(int size) {
1095 return new WorkSource[size];
1096 }
1097 };
1098}