blob: 825fc64df5cc04adaf50b2b99dcf6179f705071b [file] [log] [blame]
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001package android.os;
2
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -07003import android.annotation.NonNull;
Narayan Kamathf0202a92017-12-07 15:45:33 +00004import android.annotation.Nullable;
Narayan Kamathdfcdcc92018-01-25 19:58:52 +00005import android.annotation.SystemApi;
Jeff Sharkeyc6091162018-06-29 17:15:40 -06006import android.annotation.TestApi;
Andrei Onea24ec3212019-03-15 17:35:05 +00007import android.annotation.UnsupportedAppUsage;
Narayan Kamath8d828252018-01-11 15:22:37 +00008import android.content.Context;
Netta P958d0a52017-02-07 11:20:55 -08009import android.os.WorkSourceProto;
Narayan Kamath8d828252018-01-11 15:22:37 +000010import android.provider.Settings;
11import android.provider.Settings.Global;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080012import android.util.Log;
Netta P958d0a52017-02-07 11:20:55 -080013import android.util.proto.ProtoOutputStream;
Jeff Brown96307042012-07-27 15:51:34 -070014
Narayan Kamathdfcdcc92018-01-25 19:58:52 +000015import com.android.internal.annotations.VisibleForTesting;
16
Narayan Kamathf0202a92017-12-07 15:45:33 +000017import java.util.ArrayList;
Jeff Brown96307042012-07-27 15:51:34 -070018import java.util.Arrays;
19
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070020/**
21 * Describes the source of some work that may be done by someone else.
22 * Currently the public representation of what a work source is is not
23 * defined; this is an opaque container.
24 */
25public class WorkSource implements Parcelable {
Dianne Hackborn002a54e2013-01-10 17:34:55 -080026 static final String TAG = "WorkSource";
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080027 static final boolean DEBUG = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080028
Andrei Onea24ec3212019-03-15 17:35:05 +000029 @UnsupportedAppUsage
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070030 int mNum;
Andrei Onea24ec3212019-03-15 17:35:05 +000031 @UnsupportedAppUsage
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070032 int[] mUids;
Andrei Onea24ec3212019-03-15 17:35:05 +000033 @UnsupportedAppUsage
Dianne Hackborn002a54e2013-01-10 17:34:55 -080034 String[] mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070035
Narayan Kamathf0202a92017-12-07 15:45:33 +000036 private ArrayList<WorkChain> mChains;
37
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070038 /**
39 * Internal statics to avoid object allocations in some operations.
40 * The WorkSource object itself is not thread safe, but we need to
41 * hold sTmpWorkSource lock while working with these statics.
42 */
Artur Satayev70507ed2019-07-29 13:18:27 +010043 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070044 static final WorkSource sTmpWorkSource = new WorkSource(0);
45 /**
46 * For returning newbie work from a modification operation.
47 */
Artur Satayev70507ed2019-07-29 13:18:27 +010048 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070049 static WorkSource sNewbWork;
50 /**
51 * For returning gone work form a modification operation.
52 */
Artur Satayev70507ed2019-07-29 13:18:27 +010053 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070054 static WorkSource sGoneWork;
55
56 /**
57 * Create an empty work source.
58 */
59 public WorkSource() {
60 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000061 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070062 }
63
64 /**
65 * Create a new WorkSource that is a copy of an existing one.
66 * If <var>orig</var> is null, an empty WorkSource is created.
67 */
68 public WorkSource(WorkSource orig) {
69 if (orig == null) {
70 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000071 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070072 return;
73 }
74 mNum = orig.mNum;
75 if (orig.mUids != null) {
76 mUids = orig.mUids.clone();
Dianne Hackborn002a54e2013-01-10 17:34:55 -080077 mNames = orig.mNames != null ? orig.mNames.clone() : null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070078 } else {
79 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080080 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070081 }
Narayan Kamathf0202a92017-12-07 15:45:33 +000082
83 if (orig.mChains != null) {
84 // Make a copy of all WorkChains that exist on |orig| since they are mutable.
85 mChains = new ArrayList<>(orig.mChains.size());
86 for (WorkChain chain : orig.mChains) {
87 mChains.add(new WorkChain(chain));
88 }
89 } else {
90 mChains = null;
91 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070092 }
93
94 /** @hide */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +000095 @UnsupportedAppUsage
Jeff Sharkeyc6091162018-06-29 17:15:40 -060096 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070097 public WorkSource(int uid) {
98 mNum = 1;
99 mUids = new int[] { uid, 0 };
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800100 mNames = null;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000101 mChains = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800102 }
103
104 /** @hide */
105 public WorkSource(int uid, String name) {
106 if (name == null) {
107 throw new NullPointerException("Name can't be null");
108 }
109 mNum = 1;
110 mUids = new int[] { uid, 0 };
111 mNames = new String[] { name, null };
Narayan Kamathf0202a92017-12-07 15:45:33 +0000112 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700113 }
114
Andrei Onea24ec3212019-03-15 17:35:05 +0000115 @UnsupportedAppUsage
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700116 WorkSource(Parcel in) {
117 mNum = in.readInt();
118 mUids = in.createIntArray();
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800119 mNames = in.createStringArray();
Narayan Kamathf0202a92017-12-07 15:45:33 +0000120
121 int numChains = in.readInt();
122 if (numChains > 0) {
123 mChains = new ArrayList<>(numChains);
124 in.readParcelableList(mChains, WorkChain.class.getClassLoader());
125 } else {
126 mChains = null;
127 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700128 }
129
Narayan Kamath8d828252018-01-11 15:22:37 +0000130 /**
131 * Whether system services should create {@code WorkChains} (wherever possible) in the place
132 * of flat UID lists.
133 *
134 * @hide
135 */
136 public static boolean isChainedBatteryAttributionEnabled(Context context) {
137 return Settings.Global.getInt(context.getContentResolver(),
138 Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, 0) == 1;
139 }
140
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700141 /** @hide */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000142 @UnsupportedAppUsage
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600143 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700144 public int size() {
145 return mNum;
146 }
147
148 /** @hide */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000149 @UnsupportedAppUsage
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600150 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700151 public int get(int index) {
152 return mUids[index];
153 }
154
Marcin Oczeretko53295402018-12-17 12:44:35 +0000155 /**
156 * Return the UID to which this WorkSource should be attributed to, i.e, the UID that
157 * initiated the work and not the UID performing it. If the WorkSource has no UIDs, returns -1
158 * instead.
159 *
160 * @hide
161 */
162 public int getAttributionUid() {
163 if (isEmpty()) {
164 return -1;
165 }
166
167 return mNum > 0 ? mUids[0] : mChains.get(0).getAttributionUid();
168 }
169
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800170 /** @hide */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000171 @UnsupportedAppUsage
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600172 @TestApi
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800173 public String getName(int index) {
174 return mNames != null ? mNames[index] : null;
175 }
176
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700177 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000178 * Clear names from this WorkSource. Uids are left intact. WorkChains if any, are left
179 * intact.
David Christie6ab22842013-09-13 17:11:53 -0700180 *
181 * <p>Useful when combining with another WorkSource that doesn't have names.
182 * @hide
183 */
184 public void clearNames() {
David Christiea31510e2013-09-20 10:44:01 -0700185 if (mNames != null) {
186 mNames = null;
187 // Clear out any duplicate uids now that we don't have names to disambiguate them.
188 int destIndex = 1;
189 int newNum = mNum;
190 for (int sourceIndex = 1; sourceIndex < mNum; sourceIndex++) {
191 if (mUids[sourceIndex] == mUids[sourceIndex - 1]) {
192 newNum--;
193 } else {
194 mUids[destIndex] = mUids[sourceIndex];
195 destIndex++;
196 }
197 }
198 mNum = newNum;
199 }
David Christie6ab22842013-09-13 17:11:53 -0700200 }
201
202 /**
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700203 * Clear this WorkSource to be empty.
204 */
205 public void clear() {
206 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000207 if (mChains != null) {
208 mChains.clear();
209 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700210 }
211
Jeff Brown94838912012-07-27 12:04:37 -0700212 @Override
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -0700213 public boolean equals(@Nullable Object o) {
Narayan Kamatheb279462018-01-09 16:09:35 +0000214 if (o instanceof WorkSource) {
215 WorkSource other = (WorkSource) o;
216
217 if (diff(other)) {
218 return false;
219 }
220
221 if (mChains != null && !mChains.isEmpty()) {
222 return mChains.equals(other.mChains);
223 } else {
224 return other.mChains == null || other.mChains.isEmpty();
225 }
226 }
227
228 return false;
Jeff Brown94838912012-07-27 12:04:37 -0700229 }
230
231 @Override
232 public int hashCode() {
233 int result = 0;
234 for (int i = 0; i < mNum; i++) {
235 result = ((result << 4) | (result >>> 28)) ^ mUids[i];
236 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800237 if (mNames != null) {
238 for (int i = 0; i < mNum; i++) {
239 result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
240 }
241 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000242
243 if (mChains != null) {
244 result = ((result << 4) | (result >>> 28)) ^ mChains.hashCode();
245 }
246
Jeff Brown94838912012-07-27 12:04:37 -0700247 return result;
248 }
249
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700250 /**
251 * Compare this WorkSource with another.
252 * @param other The WorkSource to compare against.
253 * @return If there is a difference, true is returned.
254 */
Narayan Kamathf0202a92017-12-07 15:45:33 +0000255 // TODO: This is a public API so it cannot be renamed. Because it is used in several places,
256 // we keep its semantics the same and ignore any differences in WorkChains (if any).
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700257 public boolean diff(WorkSource other) {
258 int N = mNum;
259 if (N != other.mNum) {
260 return true;
261 }
262 final int[] uids1 = mUids;
263 final int[] uids2 = other.mUids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800264 final String[] names1 = mNames;
265 final String[] names2 = other.mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700266 for (int i=0; i<N; i++) {
267 if (uids1[i] != uids2[i]) {
268 return true;
269 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800270 if (names1 != null && names2 != null && !names1[i].equals(names2[i])) {
271 return true;
272 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700273 }
274 return false;
275 }
276
277 /**
278 * Replace the current contents of this work source with the given
Narayan Kamathf0202a92017-12-07 15:45:33 +0000279 * work source. If {@code other} is null, the current work source
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700280 * will be made empty.
281 */
282 public void set(WorkSource other) {
283 if (other == null) {
284 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000285 if (mChains != null) {
286 mChains.clear();
287 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700288 return;
289 }
290 mNum = other.mNum;
291 if (other.mUids != null) {
292 if (mUids != null && mUids.length >= mNum) {
293 System.arraycopy(other.mUids, 0, mUids, 0, mNum);
294 } else {
295 mUids = other.mUids.clone();
296 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800297 if (other.mNames != null) {
298 if (mNames != null && mNames.length >= mNum) {
299 System.arraycopy(other.mNames, 0, mNames, 0, mNum);
300 } else {
301 mNames = other.mNames.clone();
302 }
303 } else {
304 mNames = null;
305 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700306 } else {
307 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800308 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700309 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000310
311 if (other.mChains != null) {
312 if (mChains != null) {
313 mChains.clear();
314 } else {
315 mChains = new ArrayList<>(other.mChains.size());
316 }
317
318 for (WorkChain chain : other.mChains) {
319 mChains.add(new WorkChain(chain));
320 }
321 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700322 }
323
324 /** @hide */
325 public void set(int uid) {
326 mNum = 1;
327 if (mUids == null) mUids = new int[2];
328 mUids[0] = uid;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800329 mNames = null;
Narayan Kamath6192f732017-12-21 09:43:38 +0000330 if (mChains != null) {
331 mChains.clear();
332 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800333 }
334
335 /** @hide */
336 public void set(int uid, String name) {
337 if (name == null) {
338 throw new NullPointerException("Name can't be null");
339 }
340 mNum = 1;
341 if (mUids == null) {
342 mUids = new int[2];
343 mNames = new String[2];
344 }
345 mUids[0] = uid;
346 mNames[0] = name;
Narayan Kamath6192f732017-12-21 09:43:38 +0000347 if (mChains != null) {
348 mChains.clear();
349 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700350 }
351
Narayan Kamathf0202a92017-12-07 15:45:33 +0000352 /**
353 * Legacy API, DO NOT USE: Only deals with flat UIDs and tags. No chains are transferred, and no
354 * differences in chains are returned. This will be removed once its callers have been
355 * rewritten.
356 *
357 * NOTE: This is currently only used in GnssLocationProvider.
358 *
359 * @hide
360 * @deprecated for internal use only. WorkSources are opaque and no external callers should need
361 * to be aware of internal differences.
362 */
363 @Deprecated
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600364 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700365 public WorkSource[] setReturningDiffs(WorkSource other) {
366 synchronized (sTmpWorkSource) {
367 sNewbWork = null;
368 sGoneWork = null;
369 updateLocked(other, true, true);
370 if (sNewbWork != null || sGoneWork != null) {
371 WorkSource[] diffs = new WorkSource[2];
372 diffs[0] = sNewbWork;
373 diffs[1] = sGoneWork;
374 return diffs;
375 }
376 return null;
377 }
378 }
379
380 /**
381 * Merge the contents of <var>other</var> WorkSource in to this one.
382 *
383 * @param other The other WorkSource whose contents are to be merged.
384 * @return Returns true if any new sources were added.
385 */
386 public boolean add(WorkSource other) {
387 synchronized (sTmpWorkSource) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000388 boolean uidAdded = updateLocked(other, false, false);
389
390 boolean chainAdded = false;
391 if (other.mChains != null) {
392 // NOTE: This is quite an expensive operation, especially if the number of chains
393 // is large. We could look into optimizing it if it proves problematic.
394 if (mChains == null) {
395 mChains = new ArrayList<>(other.mChains.size());
396 }
397
398 for (WorkChain wc : other.mChains) {
399 if (!mChains.contains(wc)) {
400 mChains.add(new WorkChain(wc));
401 }
402 }
403 }
404
405 return uidAdded || chainAdded;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700406 }
407 }
408
Narayan Kamathf0202a92017-12-07 15:45:33 +0000409 /**
410 * Legacy API: DO NOT USE. Only in use from unit tests.
411 *
412 * @hide
413 * @deprecated meant for unit testing use only. Will be removed in a future API revision.
414 */
415 @Deprecated
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600416 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700417 public WorkSource addReturningNewbs(WorkSource other) {
418 synchronized (sTmpWorkSource) {
419 sNewbWork = null;
420 updateLocked(other, false, true);
421 return sNewbWork;
422 }
423 }
424
425 /** @hide */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000426 @UnsupportedAppUsage
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600427 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700428 public boolean add(int uid) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800429 if (mNum <= 0) {
430 mNames = null;
431 insert(0, uid);
432 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700433 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800434 if (mNames != null) {
435 throw new IllegalArgumentException("Adding without name to named " + this);
436 }
437 int i = Arrays.binarySearch(mUids, 0, mNum, uid);
438 if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i);
439 if (i >= 0) {
440 return false;
441 }
442 insert(-i-1, uid);
443 return true;
444 }
445
446 /** @hide */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000447 @UnsupportedAppUsage
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600448 @TestApi
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800449 public boolean add(int uid, String name) {
450 if (mNum <= 0) {
451 insert(0, uid, name);
452 return true;
453 }
454 if (mNames == null) {
455 throw new IllegalArgumentException("Adding name to unnamed " + this);
456 }
457 int i;
458 for (i=0; i<mNum; i++) {
459 if (mUids[i] > uid) {
460 break;
461 }
462 if (mUids[i] == uid) {
Netta P958d0a52017-02-07 11:20:55 -0800463 int diff = mNames[i].compareTo(name);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800464 if (diff > 0) {
465 break;
466 }
467 if (diff == 0) {
468 return false;
469 }
470 }
471 }
472 insert(i, uid, name);
473 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700474 }
475
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700476 public boolean remove(WorkSource other) {
Narayan Kamathee07f622018-01-08 12:20:28 +0000477 if (isEmpty() || other.isEmpty()) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800478 return false;
479 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000480
Narayan Kamathee07f622018-01-08 12:20:28 +0000481 boolean uidRemoved;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800482 if (mNames == null && other.mNames == null) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000483 uidRemoved = removeUids(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800484 } else {
485 if (mNames == null) {
486 throw new IllegalArgumentException("Other " + other + " has names, but target "
487 + this + " does not");
488 }
489 if (other.mNames == null) {
490 throw new IllegalArgumentException("Target " + this + " has names, but other "
491 + other + " does not");
492 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000493 uidRemoved = removeUidsAndNames(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800494 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000495
496 boolean chainRemoved = false;
Narayan Kamathee07f622018-01-08 12:20:28 +0000497 if (other.mChains != null && mChains != null) {
498 chainRemoved = mChains.removeAll(other.mChains);
Narayan Kamathf0202a92017-12-07 15:45:33 +0000499 }
500
501 return uidRemoved || chainRemoved;
502 }
503
504 /**
505 * Create a new {@code WorkChain} associated with this WorkSource and return it.
506 *
507 * @hide
508 */
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000509 @SystemApi
Narayan Kamathf0202a92017-12-07 15:45:33 +0000510 public WorkChain createWorkChain() {
511 if (mChains == null) {
512 mChains = new ArrayList<>(4);
513 }
514
515 final WorkChain wc = new WorkChain();
516 mChains.add(wc);
517
518 return wc;
519 }
520
521 /**
Narayan Kamath81822022017-12-08 11:56:01 +0000522 * Returns {@code true} iff. this work source contains zero UIDs and zero WorkChains to
523 * attribute usage to.
524 *
525 * @hide for internal use only.
526 */
527 public boolean isEmpty() {
528 return mNum == 0 && (mChains == null || mChains.isEmpty());
529 }
530
531 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000532 * @return the list of {@code WorkChains} associated with this {@code WorkSource}.
533 * @hide
534 */
535 public ArrayList<WorkChain> getWorkChains() {
536 return mChains;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800537 }
538
Narayan Kamath32684dd2018-01-08 17:32:51 +0000539 /**
540 * DO NOT USE: Hacky API provided solely for {@code GnssLocationProvider}. See
541 * {@code setReturningDiffs} as well.
542 *
543 * @hide
544 */
545 public void transferWorkChains(WorkSource other) {
546 if (mChains != null) {
547 mChains.clear();
548 }
549
550 if (other.mChains == null || other.mChains.isEmpty()) {
551 return;
552 }
553
554 if (mChains == null) {
555 mChains = new ArrayList<>(4);
556 }
557
558 mChains.addAll(other.mChains);
559 other.mChains.clear();
560 }
561
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800562 private boolean removeUids(WorkSource other) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700563 int N1 = mNum;
564 final int[] uids1 = mUids;
565 final int N2 = other.mNum;
566 final int[] uids2 = other.mUids;
567 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800568 int i1 = 0, i2 = 0;
569 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
570 while (i1 < N1 && i2 < N2) {
571 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
572 + " of " + N2);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700573 if (uids2[i2] == uids1[i1]) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800574 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
575 + ": remove " + uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700576 N1--;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800577 changed = true;
Dianne Hackborn83770282010-09-14 11:45:44 -0700578 if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800579 i2++;
580 } else if (uids2[i2] > uids1[i1]) {
581 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700582 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800583 } else {
584 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
585 i2++;
586 }
587 }
588
589 mNum = N1;
590
591 return changed;
592 }
593
594 private boolean removeUidsAndNames(WorkSource other) {
595 int N1 = mNum;
596 final int[] uids1 = mUids;
597 final String[] names1 = mNames;
598 final int N2 = other.mNum;
599 final int[] uids2 = other.mUids;
600 final String[] names2 = other.mNames;
601 boolean changed = false;
602 int i1 = 0, i2 = 0;
603 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
604 while (i1 < N1 && i2 < N2) {
605 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
606 + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]);
607 if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) {
608 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
609 + ": remove " + uids1[i1] + " " + names1[i1]);
610 N1--;
611 changed = true;
612 if (i1 < N1) {
613 System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
614 System.arraycopy(names1, i1+1, names1, i1, N1-i1);
615 }
616 i2++;
617 } else if (uids2[i2] > uids1[i1]
618 || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) {
619 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
620 i1++;
621 } else {
622 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
623 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700624 }
625 }
626
627 mNum = N1;
628
629 return changed;
630 }
631
Artur Satayev70507ed2019-07-29 13:18:27 +0100632 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700633 private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800634 if (mNames == null && other.mNames == null) {
635 return updateUidsLocked(other, set, returnNewbs);
636 } else {
637 if (mNum > 0 && mNames == null) {
638 throw new IllegalArgumentException("Other " + other + " has names, but target "
639 + this + " does not");
640 }
641 if (other.mNum > 0 && other.mNames == null) {
642 throw new IllegalArgumentException("Target " + this + " has names, but other "
643 + other + " does not");
644 }
645 return updateUidsAndNamesLocked(other, set, returnNewbs);
646 }
647 }
648
649 private static WorkSource addWork(WorkSource cur, int newUid) {
650 if (cur == null) {
651 return new WorkSource(newUid);
652 }
653 cur.insert(cur.mNum, newUid);
654 return cur;
655 }
656
657 private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700658 int N1 = mNum;
659 int[] uids1 = mUids;
660 final int N2 = other.mNum;
661 final int[] uids2 = other.mUids;
662 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800663 int i1 = 0, i2 = 0;
664 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
665 + " returnNewbs=" + returnNewbs);
666 while (i1 < N1 || i2 < N2) {
667 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
668 + " of " + N2);
669 if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700670 // Need to insert a new uid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800671 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
672 + ": insert " + uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700673 changed = true;
674 if (uids1 == null) {
675 uids1 = new int[4];
676 uids1[0] = uids2[i2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800677 } else if (N1 >= uids1.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700678 int[] newuids = new int[(uids1.length*3)/2];
679 if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
680 if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
681 uids1 = newuids;
682 uids1[i1] = uids2[i2];
683 } else {
684 if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
685 uids1[i1] = uids2[i2];
686 }
687 if (returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800688 sNewbWork = addWork(sNewbWork, uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700689 }
690 N1++;
691 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800692 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700693 } else {
694 if (!set) {
695 // Skip uids that already exist or are not in 'other'.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800696 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
697 if (i2 < N2 && uids2[i2] == uids1[i1]) {
698 i2++;
699 }
700 i1++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700701 } else {
702 // Remove any uids that don't exist in 'other'.
703 int start = i1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800704 while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) {
705 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]);
706 sGoneWork = addWork(sGoneWork, uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700707 i1++;
708 }
709 if (start < i1) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800710 System.arraycopy(uids1, i1, uids1, start, N1-i1);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700711 N1 -= i1-start;
712 i1 = start;
713 }
714 // If there is a matching uid, skip it.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800715 if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) {
716 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700717 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800718 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700719 }
720 }
721 }
722 }
723
724 mNum = N1;
725 mUids = uids1;
726
727 return changed;
728 }
729
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800730 /**
731 * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'.
732 */
733 private int compare(WorkSource other, int i1, int i2) {
734 final int diff = mUids[i1] - other.mUids[i2];
735 if (diff != 0) {
736 return diff;
737 }
738 return mNames[i1].compareTo(other.mNames[i2]);
739 }
740
741 private static WorkSource addWork(WorkSource cur, int newUid, String newName) {
742 if (cur == null) {
743 return new WorkSource(newUid, newName);
744 }
745 cur.insert(cur.mNum, newUid, newName);
746 return cur;
747 }
748
749 private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) {
750 final int N2 = other.mNum;
751 final int[] uids2 = other.mUids;
752 String[] names2 = other.mNames;
753 boolean changed = false;
754 int i1 = 0, i2 = 0;
755 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
756 + " returnNewbs=" + returnNewbs);
757 while (i1 < mNum || i2 < N2) {
758 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2
759 + " of " + N2);
760 int diff = -1;
761 if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) {
762 // Need to insert a new uid.
763 changed = true;
764 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum
765 + ": insert " + uids2[i2] + " " + names2[i2]);
766 insert(i1, uids2[i2], names2[i2]);
767 if (returnNewbs) {
768 sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]);
769 }
770 i1++;
771 i2++;
772 } else {
773 if (!set) {
774 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
775 if (i2 < N2 && diff == 0) {
776 i2++;
777 }
778 i1++;
779 } else {
780 // Remove any uids that don't exist in 'other'.
781 int start = i1;
782 while (diff < 0) {
783 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1]
784 + " " + mNames[i1]);
785 sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]);
786 i1++;
787 if (i1 >= mNum) {
788 break;
789 }
790 diff = i2 < N2 ? compare(other, i1, i2) : -1;
791 }
792 if (start < i1) {
793 System.arraycopy(mUids, i1, mUids, start, mNum-i1);
794 System.arraycopy(mNames, i1, mNames, start, mNum-i1);
795 mNum -= i1-start;
796 i1 = start;
797 }
798 // If there is a matching uid, skip it.
799 if (i1 < mNum && diff == 0) {
800 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
801 i1++;
802 i2++;
803 }
804 }
805 }
806 }
807
808 return changed;
809 }
810
811 private void insert(int index, int uid) {
812 if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700813 if (mUids == null) {
814 mUids = new int[4];
815 mUids[0] = uid;
816 mNum = 1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800817 } else if (mNum >= mUids.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700818 int[] newuids = new int[(mNum*3)/2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800819 if (index > 0) {
820 System.arraycopy(mUids, 0, newuids, 0, index);
821 }
822 if (index < mNum) {
823 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
824 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700825 mUids = newuids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800826 mUids[index] = uid;
827 mNum++;
828 } else {
829 if (index < mNum) {
830 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
831 }
832 mUids[index] = uid;
833 mNum++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700834 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800835 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700836
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800837 private void insert(int index, int uid, String name) {
838 if (mUids == null) {
839 mUids = new int[4];
840 mUids[0] = uid;
841 mNames = new String[4];
842 mNames[0] = name;
843 mNum = 1;
844 } else if (mNum >= mUids.length) {
845 int[] newuids = new int[(mNum*3)/2];
846 String[] newnames = new String[(mNum*3)/2];
847 if (index > 0) {
848 System.arraycopy(mUids, 0, newuids, 0, index);
849 System.arraycopy(mNames, 0, newnames, 0, index);
850 }
851 if (index < mNum) {
852 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
853 System.arraycopy(mNames, index, newnames, index+1, mNum-index);
854 }
855 mUids = newuids;
856 mNames = newnames;
857 mUids[index] = uid;
858 mNames[index] = name;
859 mNum++;
860 } else {
861 if (index < mNum) {
862 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
863 System.arraycopy(mNames, index, mNames, index+1, mNum-index);
864 }
865 mUids[index] = uid;
866 mNames[index] = name;
867 mNum++;
868 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700869 }
870
Narayan Kamathf0202a92017-12-07 15:45:33 +0000871 /**
872 * Represents an attribution chain for an item of work being performed. An attribution chain is
873 * an indexed list of {code (uid, tag)} nodes. The node at {@code index == 0} is the initiator
874 * of the work, and the node at the highest index performs the work. Nodes at other indices
875 * are intermediaries that facilitate the work. Examples :
876 *
877 * (1) Work being performed by uid=2456 (no chaining):
878 * <pre>
879 * WorkChain {
880 * mUids = { 2456 }
881 * mTags = { null }
882 * mSize = 1;
883 * }
884 * </pre>
885 *
886 * (2) Work being performed by uid=2456 (from component "c1") on behalf of uid=5678:
887 *
888 * <pre>
889 * WorkChain {
890 * mUids = { 5678, 2456 }
891 * mTags = { null, "c1" }
892 * mSize = 1
893 * }
894 * </pre>
895 *
896 * Attribution chains are mutable, though the only operation that can be performed on them
897 * is the addition of a new node at the end of the attribution chain to represent
898 *
899 * @hide
900 */
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000901 @SystemApi
902 public static final class WorkChain implements Parcelable {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000903 private int mSize;
904 private int[] mUids;
905 private String[] mTags;
906
907 // @VisibleForTesting
908 public WorkChain() {
909 mSize = 0;
910 mUids = new int[4];
911 mTags = new String[4];
912 }
913
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000914 /** @hide */
915 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000916 public WorkChain(WorkChain other) {
917 mSize = other.mSize;
918 mUids = other.mUids.clone();
919 mTags = other.mTags.clone();
920 }
921
922 private WorkChain(Parcel in) {
923 mSize = in.readInt();
924 mUids = in.createIntArray();
925 mTags = in.createStringArray();
926 }
927
928 /**
929 * Append a node whose uid is {@code uid} and whose optional tag is {@code tag} to this
930 * {@code WorkChain}.
931 */
932 public WorkChain addNode(int uid, @Nullable String tag) {
933 if (mSize == mUids.length) {
934 resizeArrays();
935 }
936
937 mUids[mSize] = uid;
938 mTags[mSize] = tag;
939 mSize++;
940
941 return this;
942 }
943
Narayan Kamath81822022017-12-08 11:56:01 +0000944 /**
Narayan Kamathcbe06772017-12-27 14:22:47 +0000945 * Return the UID to which this WorkChain should be attributed to, i.e, the UID that
Marcin Oczeretko53295402018-12-17 12:44:35 +0000946 * initiated the work and not the UID performing it. Returns -1 if the chain is empty.
Narayan Kamath81822022017-12-08 11:56:01 +0000947 */
948 public int getAttributionUid() {
Marcin Oczeretko53295402018-12-17 12:44:35 +0000949 return mSize > 0 ? mUids[0] : -1;
Narayan Kamath81822022017-12-08 11:56:01 +0000950 }
951
Narayan Kamath32684dd2018-01-08 17:32:51 +0000952 /**
953 * Return the tag associated with the attribution UID. See (@link #getAttributionUid}.
Marcin Oczeretko53295402018-12-17 12:44:35 +0000954 * Returns null if the chain is empty.
Narayan Kamath32684dd2018-01-08 17:32:51 +0000955 */
956 public String getAttributionTag() {
Marcin Oczeretko53295402018-12-17 12:44:35 +0000957 return mTags.length > 0 ? mTags[0] : null;
Narayan Kamath32684dd2018-01-08 17:32:51 +0000958 }
959
Narayan Kamathf0202a92017-12-07 15:45:33 +0000960 // TODO: The following three trivial getters are purely for testing and will be removed
961 // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
962 // diffing it etc.
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000963
964
965 /** @hide */
966 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000967 public int[] getUids() {
Yangster-mac5fa895e2018-04-09 14:45:28 -0700968 int[] uids = new int[mSize];
969 System.arraycopy(mUids, 0, uids, 0, mSize);
970 return uids;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000971 }
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000972
973 /** @hide */
974 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000975 public String[] getTags() {
Yangster-mac5fa895e2018-04-09 14:45:28 -0700976 String[] tags = new String[mSize];
977 System.arraycopy(mTags, 0, tags, 0, mSize);
978 return tags;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000979 }
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000980
981 /** @hide */
982 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000983 public int getSize() {
984 return mSize;
985 }
986
987 private void resizeArrays() {
988 final int newSize = mSize * 2;
989 int[] uids = new int[newSize];
990 String[] tags = new String[newSize];
991
992 System.arraycopy(mUids, 0, uids, 0, mSize);
993 System.arraycopy(mTags, 0, tags, 0, mSize);
994
995 mUids = uids;
996 mTags = tags;
997 }
998
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -0700999 @NonNull
Narayan Kamathf0202a92017-12-07 15:45:33 +00001000 @Override
1001 public String toString() {
1002 StringBuilder result = new StringBuilder("WorkChain{");
1003 for (int i = 0; i < mSize; ++i) {
1004 if (i != 0) {
1005 result.append(", ");
1006 }
1007 result.append("(");
1008 result.append(mUids[i]);
1009 if (mTags[i] != null) {
1010 result.append(", ");
1011 result.append(mTags[i]);
1012 }
1013 result.append(")");
1014 }
1015
1016 result.append("}");
1017 return result.toString();
1018 }
1019
1020 @Override
1021 public int hashCode() {
1022 return (mSize + 31 * Arrays.hashCode(mUids)) * 31 + Arrays.hashCode(mTags);
1023 }
1024
1025 @Override
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -07001026 public boolean equals(@Nullable Object o) {
Narayan Kamathf0202a92017-12-07 15:45:33 +00001027 if (o instanceof WorkChain) {
1028 WorkChain other = (WorkChain) o;
1029
1030 return mSize == other.mSize
1031 && Arrays.equals(mUids, other.mUids)
1032 && Arrays.equals(mTags, other.mTags);
1033 }
1034
1035 return false;
1036 }
1037
1038 @Override
1039 public int describeContents() {
1040 return 0;
1041 }
1042
1043 @Override
1044 public void writeToParcel(Parcel dest, int flags) {
1045 dest.writeInt(mSize);
1046 dest.writeIntArray(mUids);
1047 dest.writeStringArray(mTags);
1048 }
1049
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07001050 public static final @android.annotation.NonNull Parcelable.Creator<WorkChain> CREATOR =
Narayan Kamathf0202a92017-12-07 15:45:33 +00001051 new Parcelable.Creator<WorkChain>() {
1052 public WorkChain createFromParcel(Parcel in) {
1053 return new WorkChain(in);
1054 }
1055 public WorkChain[] newArray(int size) {
1056 return new WorkChain[size];
1057 }
1058 };
1059 }
1060
Narayan Kamath81822022017-12-08 11:56:01 +00001061 /**
1062 * Computes the differences in WorkChains contained between {@code oldWs} and {@code newWs}.
1063 *
1064 * Returns {@code null} if no differences exist, otherwise returns a two element array. The
1065 * first element is a list of "new" chains, i.e WorkChains present in {@code newWs} but not in
1066 * {@code oldWs}. The second element is a list of "gone" chains, i.e WorkChains present in
1067 * {@code oldWs} but not in {@code newWs}.
1068 *
1069 * @hide
1070 */
1071 public static ArrayList<WorkChain>[] diffChains(WorkSource oldWs, WorkSource newWs) {
1072 ArrayList<WorkChain> newChains = null;
1073 ArrayList<WorkChain> goneChains = null;
1074
1075 // TODO(narayan): This is a dumb O(M*N) algorithm that determines what has changed across
1076 // WorkSource objects. We can replace this with something smarter, for e.g by defining
1077 // a Comparator between WorkChains. It's unclear whether that will be more efficient if
1078 // the number of chains associated with a WorkSource is expected to be small
1079 if (oldWs.mChains != null) {
1080 for (int i = 0; i < oldWs.mChains.size(); ++i) {
1081 final WorkChain wc = oldWs.mChains.get(i);
1082 if (newWs.mChains == null || !newWs.mChains.contains(wc)) {
1083 if (goneChains == null) {
1084 goneChains = new ArrayList<>(oldWs.mChains.size());
1085 }
1086 goneChains.add(wc);
1087 }
1088 }
1089 }
1090
1091 if (newWs.mChains != null) {
1092 for (int i = 0; i < newWs.mChains.size(); ++i) {
1093 final WorkChain wc = newWs.mChains.get(i);
1094 if (oldWs.mChains == null || !oldWs.mChains.contains(wc)) {
1095 if (newChains == null) {
1096 newChains = new ArrayList<>(newWs.mChains.size());
1097 }
1098 newChains.add(wc);
1099 }
1100 }
1101 }
1102
1103 if (newChains != null || goneChains != null) {
1104 return new ArrayList[] { newChains, goneChains };
1105 }
1106
1107 return null;
1108 }
1109
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001110 @Override
1111 public int describeContents() {
1112 return 0;
1113 }
1114
1115 @Override
1116 public void writeToParcel(Parcel dest, int flags) {
1117 dest.writeInt(mNum);
1118 dest.writeIntArray(mUids);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001119 dest.writeStringArray(mNames);
Narayan Kamathf0202a92017-12-07 15:45:33 +00001120
1121 if (mChains == null) {
1122 dest.writeInt(-1);
1123 } else {
1124 dest.writeInt(mChains.size());
1125 dest.writeParcelableList(mChains, flags);
1126 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001127 }
1128
Jeff Brown96307042012-07-27 15:51:34 -07001129 @Override
1130 public String toString() {
1131 StringBuilder result = new StringBuilder();
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001132 result.append("WorkSource{");
Jeff Brown96307042012-07-27 15:51:34 -07001133 for (int i = 0; i < mNum; i++) {
1134 if (i != 0) {
1135 result.append(", ");
1136 }
1137 result.append(mUids[i]);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001138 if (mNames != null) {
1139 result.append(" ");
1140 result.append(mNames[i]);
1141 }
Jeff Brown96307042012-07-27 15:51:34 -07001142 }
Narayan Kamathf0202a92017-12-07 15:45:33 +00001143
1144 if (mChains != null) {
1145 result.append(" chains=");
1146 for (int i = 0; i < mChains.size(); ++i) {
1147 if (i != 0) {
1148 result.append(", ");
1149 }
1150 result.append(mChains.get(i));
1151 }
1152 }
1153
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001154 result.append("}");
Jeff Brown96307042012-07-27 15:51:34 -07001155 return result.toString();
1156 }
1157
Netta P958d0a52017-02-07 11:20:55 -08001158 /** @hide */
1159 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1160 final long workSourceToken = proto.start(fieldId);
1161 for (int i = 0; i < mNum; i++) {
1162 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1163 proto.write(WorkSourceProto.WorkSourceContentProto.UID, mUids[i]);
1164 if (mNames != null) {
1165 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, mNames[i]);
1166 }
1167 proto.end(contentProto);
1168 }
Narayan Kamath80434a72017-12-27 15:44:17 +00001169
1170 if (mChains != null) {
1171 for (int i = 0; i < mChains.size(); i++) {
1172 final WorkChain wc = mChains.get(i);
1173 final long workChain = proto.start(WorkSourceProto.WORK_CHAINS);
1174
1175 final String[] tags = wc.getTags();
1176 final int[] uids = wc.getUids();
1177 for (int j = 0; j < tags.length; j++) {
1178 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1179 proto.write(WorkSourceProto.WorkSourceContentProto.UID, uids[j]);
1180 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, tags[j]);
1181 proto.end(contentProto);
1182 }
1183
1184 proto.end(workChain);
1185 }
1186 }
1187
Netta P958d0a52017-02-07 11:20:55 -08001188 proto.end(workSourceToken);
1189 }
1190
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07001191 public static final @android.annotation.NonNull Parcelable.Creator<WorkSource> CREATOR
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001192 = new Parcelable.Creator<WorkSource>() {
1193 public WorkSource createFromParcel(Parcel in) {
1194 return new WorkSource(in);
1195 }
1196 public WorkSource[] newArray(int size) {
1197 return new WorkSource[size];
1198 }
1199 };
1200}