blob: b5635a49b6d6c167cb1f2d583a78ecfe46575442 [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;
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -080016import com.android.internal.util.Preconditions;
Narayan Kamathdfcdcc92018-01-25 19:58:52 +000017
Narayan Kamathf0202a92017-12-07 15:45:33 +000018import java.util.ArrayList;
Jeff Brown96307042012-07-27 15:51:34 -070019import java.util.Arrays;
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -080020import java.util.List;
Jeff Brown96307042012-07-27 15:51:34 -070021
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070022/**
23 * Describes the source of some work that may be done by someone else.
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -080024 * Currently the public representation of what a work source is not
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070025 * defined; this is an opaque container.
26 */
27public class WorkSource implements Parcelable {
Dianne Hackborn002a54e2013-01-10 17:34:55 -080028 static final String TAG = "WorkSource";
Dianne Hackborn5e45ee62013-01-24 19:13:44 -080029 static final boolean DEBUG = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080030
Andrei Onea24ec3212019-03-15 17:35:05 +000031 @UnsupportedAppUsage
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070032 int mNum;
Andrei Onea24ec3212019-03-15 17:35:05 +000033 @UnsupportedAppUsage
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070034 int[] mUids;
Andrei Onea24ec3212019-03-15 17:35:05 +000035 @UnsupportedAppUsage
Dianne Hackborn002a54e2013-01-10 17:34:55 -080036 String[] mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070037
Narayan Kamathf0202a92017-12-07 15:45:33 +000038 private ArrayList<WorkChain> mChains;
39
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070040 /**
41 * Internal statics to avoid object allocations in some operations.
42 * The WorkSource object itself is not thread safe, but we need to
43 * hold sTmpWorkSource lock while working with these statics.
44 */
Artur Satayev70507ed2019-07-29 13:18:27 +010045 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070046 static final WorkSource sTmpWorkSource = new WorkSource(0);
47 /**
48 * For returning newbie work from a modification operation.
49 */
Artur Satayev70507ed2019-07-29 13:18:27 +010050 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070051 static WorkSource sNewbWork;
52 /**
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -080053 * For returning gone work from a modification operation.
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070054 */
Artur Satayev70507ed2019-07-29 13:18:27 +010055 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070056 static WorkSource sGoneWork;
57
58 /**
59 * Create an empty work source.
60 */
61 public WorkSource() {
62 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000063 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070064 }
65
66 /**
67 * Create a new WorkSource that is a copy of an existing one.
68 * If <var>orig</var> is null, an empty WorkSource is created.
69 */
70 public WorkSource(WorkSource orig) {
71 if (orig == null) {
72 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +000073 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070074 return;
75 }
76 mNum = orig.mNum;
77 if (orig.mUids != null) {
78 mUids = orig.mUids.clone();
Dianne Hackborn002a54e2013-01-10 17:34:55 -080079 mNames = orig.mNames != null ? orig.mNames.clone() : null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070080 } else {
81 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -080082 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070083 }
Narayan Kamathf0202a92017-12-07 15:45:33 +000084
85 if (orig.mChains != null) {
86 // Make a copy of all WorkChains that exist on |orig| since they are mutable.
87 mChains = new ArrayList<>(orig.mChains.size());
88 for (WorkChain chain : orig.mChains) {
89 mChains.add(new WorkChain(chain));
90 }
91 } else {
92 mChains = null;
93 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070094 }
95
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -080096
97 /**
98 * Creates a work source with the given uid.
99 * @param uid the uid performing the work
100 * @hide
101 */
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600102 @TestApi
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800103 @SystemApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700104 public WorkSource(int uid) {
105 mNum = 1;
106 mUids = new int[] { uid, 0 };
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800107 mNames = null;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000108 mChains = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800109 }
110
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800111 /**
112 * Creates a work source with the given uid and package name.
113 * @param uid the uid performing the work
114 * @param packageName the package performing the work
115 * @hide
116 */
117 @SystemApi
118 public WorkSource(int uid, @NonNull String packageName) {
119 Preconditions.checkNotNull(packageName, "packageName can't be null");
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800120 mNum = 1;
121 mUids = new int[] { uid, 0 };
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800122 mNames = new String[] { packageName, null };
Narayan Kamathf0202a92017-12-07 15:45:33 +0000123 mChains = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700124 }
125
Andrei Onea24ec3212019-03-15 17:35:05 +0000126 @UnsupportedAppUsage
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700127 WorkSource(Parcel in) {
128 mNum = in.readInt();
129 mUids = in.createIntArray();
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800130 mNames = in.createStringArray();
Narayan Kamathf0202a92017-12-07 15:45:33 +0000131
132 int numChains = in.readInt();
133 if (numChains > 0) {
134 mChains = new ArrayList<>(numChains);
135 in.readParcelableList(mChains, WorkChain.class.getClassLoader());
136 } else {
137 mChains = null;
138 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700139 }
140
Narayan Kamath8d828252018-01-11 15:22:37 +0000141 /**
142 * Whether system services should create {@code WorkChains} (wherever possible) in the place
143 * of flat UID lists.
144 *
145 * @hide
146 */
147 public static boolean isChainedBatteryAttributionEnabled(Context context) {
148 return Settings.Global.getInt(context.getContentResolver(),
149 Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, 0) == 1;
150 }
151
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800152 /**
153 * Returns the size of this work source.
154 * @hide
155 */
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600156 @TestApi
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800157 @SystemApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700158 public int size() {
159 return mNum;
160 }
161
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800162 /**
163 * @deprecated use {{@link #getUid(int)}} instead.
164 * @hide
165 */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000166 @UnsupportedAppUsage
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800167 @Deprecated
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700168 public int get(int index) {
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800169 return getUid(index);
170 }
171
172 /**
173 * Get the uid at the given index.
174 * If {@code index} < 0 or {@code index} >= {@link #size() N}, then the behavior is undefined.
175 * @hide
176 */
177 @TestApi
178 @SystemApi
179 public int getUid(int index) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700180 return mUids[index];
181 }
182
Marcin Oczeretko53295402018-12-17 12:44:35 +0000183 /**
184 * Return the UID to which this WorkSource should be attributed to, i.e, the UID that
185 * initiated the work and not the UID performing it. If the WorkSource has no UIDs, returns -1
186 * instead.
187 *
188 * @hide
189 */
190 public int getAttributionUid() {
191 if (isEmpty()) {
192 return -1;
193 }
194
195 return mNum > 0 ? mUids[0] : mChains.get(0).getAttributionUid();
196 }
197
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800198 /**
199 * @deprecated use {{@link #getPackageName(int)}} instead.
200 * @hide
201 */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000202 @UnsupportedAppUsage
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800203 @Deprecated
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800204 public String getName(int index) {
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800205 return getPackageName(index);
206 }
207
208 /**
209 * Get the package name at the given index.
210 * If {@code index} < 0 or {@code index} >= {@link #size() N}, then the behavior is undefined.
211 * @hide
212 */
213 @TestApi
214 @SystemApi
215 @Nullable
216 public String getPackageName(int index) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800217 return mNames != null ? mNames[index] : null;
218 }
219
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700220 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000221 * Clear names from this WorkSource. Uids are left intact. WorkChains if any, are left
222 * intact.
David Christie6ab22842013-09-13 17:11:53 -0700223 *
224 * <p>Useful when combining with another WorkSource that doesn't have names.
David Christie6ab22842013-09-13 17:11:53 -0700225 */
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800226 private void clearNames() {
David Christiea31510e2013-09-20 10:44:01 -0700227 if (mNames != null) {
228 mNames = null;
229 // Clear out any duplicate uids now that we don't have names to disambiguate them.
230 int destIndex = 1;
231 int newNum = mNum;
232 for (int sourceIndex = 1; sourceIndex < mNum; sourceIndex++) {
233 if (mUids[sourceIndex] == mUids[sourceIndex - 1]) {
234 newNum--;
235 } else {
236 mUids[destIndex] = mUids[sourceIndex];
237 destIndex++;
238 }
239 }
240 mNum = newNum;
241 }
David Christie6ab22842013-09-13 17:11:53 -0700242 }
243
244 /**
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700245 * Clear this WorkSource to be empty.
246 */
247 public void clear() {
248 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000249 if (mChains != null) {
250 mChains.clear();
251 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700252 }
253
Jeff Brown94838912012-07-27 12:04:37 -0700254 @Override
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -0700255 public boolean equals(@Nullable Object o) {
Narayan Kamatheb279462018-01-09 16:09:35 +0000256 if (o instanceof WorkSource) {
257 WorkSource other = (WorkSource) o;
258
259 if (diff(other)) {
260 return false;
261 }
262
263 if (mChains != null && !mChains.isEmpty()) {
264 return mChains.equals(other.mChains);
265 } else {
266 return other.mChains == null || other.mChains.isEmpty();
267 }
268 }
269
270 return false;
Jeff Brown94838912012-07-27 12:04:37 -0700271 }
272
273 @Override
274 public int hashCode() {
275 int result = 0;
276 for (int i = 0; i < mNum; i++) {
277 result = ((result << 4) | (result >>> 28)) ^ mUids[i];
278 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800279 if (mNames != null) {
280 for (int i = 0; i < mNum; i++) {
281 result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
282 }
283 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000284
285 if (mChains != null) {
286 result = ((result << 4) | (result >>> 28)) ^ mChains.hashCode();
287 }
288
Jeff Brown94838912012-07-27 12:04:37 -0700289 return result;
290 }
291
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700292 /**
293 * Compare this WorkSource with another.
294 * @param other The WorkSource to compare against.
295 * @return If there is a difference, true is returned.
296 */
Narayan Kamathf0202a92017-12-07 15:45:33 +0000297 // TODO: This is a public API so it cannot be renamed. Because it is used in several places,
298 // we keep its semantics the same and ignore any differences in WorkChains (if any).
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700299 public boolean diff(WorkSource other) {
300 int N = mNum;
301 if (N != other.mNum) {
302 return true;
303 }
304 final int[] uids1 = mUids;
305 final int[] uids2 = other.mUids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800306 final String[] names1 = mNames;
307 final String[] names2 = other.mNames;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700308 for (int i=0; i<N; i++) {
309 if (uids1[i] != uids2[i]) {
310 return true;
311 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800312 if (names1 != null && names2 != null && !names1[i].equals(names2[i])) {
313 return true;
314 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700315 }
316 return false;
317 }
318
319 /**
320 * Replace the current contents of this work source with the given
Narayan Kamathf0202a92017-12-07 15:45:33 +0000321 * work source. If {@code other} is null, the current work source
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700322 * will be made empty.
323 */
324 public void set(WorkSource other) {
325 if (other == null) {
326 mNum = 0;
Narayan Kamathf0202a92017-12-07 15:45:33 +0000327 if (mChains != null) {
328 mChains.clear();
329 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700330 return;
331 }
332 mNum = other.mNum;
333 if (other.mUids != null) {
334 if (mUids != null && mUids.length >= mNum) {
335 System.arraycopy(other.mUids, 0, mUids, 0, mNum);
336 } else {
337 mUids = other.mUids.clone();
338 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800339 if (other.mNames != null) {
340 if (mNames != null && mNames.length >= mNum) {
341 System.arraycopy(other.mNames, 0, mNames, 0, mNum);
342 } else {
343 mNames = other.mNames.clone();
344 }
345 } else {
346 mNames = null;
347 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700348 } else {
349 mUids = null;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800350 mNames = null;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700351 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000352
353 if (other.mChains != null) {
354 if (mChains != null) {
355 mChains.clear();
356 } else {
357 mChains = new ArrayList<>(other.mChains.size());
358 }
359
360 for (WorkChain chain : other.mChains) {
361 mChains.add(new WorkChain(chain));
362 }
363 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700364 }
365
366 /** @hide */
367 public void set(int uid) {
368 mNum = 1;
369 if (mUids == null) mUids = new int[2];
370 mUids[0] = uid;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800371 mNames = null;
Narayan Kamath6192f732017-12-21 09:43:38 +0000372 if (mChains != null) {
373 mChains.clear();
374 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800375 }
376
377 /** @hide */
378 public void set(int uid, String name) {
379 if (name == null) {
380 throw new NullPointerException("Name can't be null");
381 }
382 mNum = 1;
383 if (mUids == null) {
384 mUids = new int[2];
385 mNames = new String[2];
386 }
387 mUids[0] = uid;
388 mNames[0] = name;
Narayan Kamath6192f732017-12-21 09:43:38 +0000389 if (mChains != null) {
390 mChains.clear();
391 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700392 }
393
Narayan Kamathf0202a92017-12-07 15:45:33 +0000394 /**
395 * Legacy API, DO NOT USE: Only deals with flat UIDs and tags. No chains are transferred, and no
396 * differences in chains are returned. This will be removed once its callers have been
397 * rewritten.
398 *
399 * NOTE: This is currently only used in GnssLocationProvider.
400 *
401 * @hide
402 * @deprecated for internal use only. WorkSources are opaque and no external callers should need
403 * to be aware of internal differences.
404 */
405 @Deprecated
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600406 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700407 public WorkSource[] setReturningDiffs(WorkSource other) {
408 synchronized (sTmpWorkSource) {
409 sNewbWork = null;
410 sGoneWork = null;
411 updateLocked(other, true, true);
412 if (sNewbWork != null || sGoneWork != null) {
413 WorkSource[] diffs = new WorkSource[2];
414 diffs[0] = sNewbWork;
415 diffs[1] = sGoneWork;
416 return diffs;
417 }
418 return null;
419 }
420 }
421
422 /**
423 * Merge the contents of <var>other</var> WorkSource in to this one.
424 *
425 * @param other The other WorkSource whose contents are to be merged.
426 * @return Returns true if any new sources were added.
427 */
428 public boolean add(WorkSource other) {
429 synchronized (sTmpWorkSource) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000430 boolean uidAdded = updateLocked(other, false, false);
431
432 boolean chainAdded = false;
433 if (other.mChains != null) {
434 // NOTE: This is quite an expensive operation, especially if the number of chains
435 // is large. We could look into optimizing it if it proves problematic.
436 if (mChains == null) {
437 mChains = new ArrayList<>(other.mChains.size());
438 }
439
440 for (WorkChain wc : other.mChains) {
441 if (!mChains.contains(wc)) {
442 mChains.add(new WorkChain(wc));
443 }
444 }
445 }
446
447 return uidAdded || chainAdded;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700448 }
449 }
450
Narayan Kamathf0202a92017-12-07 15:45:33 +0000451 /**
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800452 * Returns a copy of this work source without any package names.
453 * If any {@link WorkChain WorkChains} are present, they are left intact.
454 *
455 * @return a {@link WorkSource} without any package names.
456 * @hide
457 */
458 @SystemApi
459 @TestApi
460 @NonNull
461 public WorkSource withoutNames() {
462 final WorkSource copy = new WorkSource(this);
463 copy.clearNames();
464 return copy;
465 }
466
467 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000468 * Legacy API: DO NOT USE. Only in use from unit tests.
469 *
470 * @hide
471 * @deprecated meant for unit testing use only. Will be removed in a future API revision.
472 */
473 @Deprecated
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600474 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700475 public WorkSource addReturningNewbs(WorkSource other) {
476 synchronized (sTmpWorkSource) {
477 sNewbWork = null;
478 updateLocked(other, false, true);
479 return sNewbWork;
480 }
481 }
482
483 /** @hide */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000484 @UnsupportedAppUsage
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600485 @TestApi
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700486 public boolean add(int uid) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800487 if (mNum <= 0) {
488 mNames = null;
489 insert(0, uid);
490 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700491 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800492 if (mNames != null) {
493 throw new IllegalArgumentException("Adding without name to named " + this);
494 }
495 int i = Arrays.binarySearch(mUids, 0, mNum, uid);
496 if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i);
497 if (i >= 0) {
498 return false;
499 }
500 insert(-i-1, uid);
501 return true;
502 }
503
504 /** @hide */
Artur Satayevf0b7d0b2019-11-04 11:16:45 +0000505 @UnsupportedAppUsage
Jeff Sharkeyc6091162018-06-29 17:15:40 -0600506 @TestApi
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800507 public boolean add(int uid, String name) {
508 if (mNum <= 0) {
509 insert(0, uid, name);
510 return true;
511 }
512 if (mNames == null) {
513 throw new IllegalArgumentException("Adding name to unnamed " + this);
514 }
515 int i;
516 for (i=0; i<mNum; i++) {
517 if (mUids[i] > uid) {
518 break;
519 }
520 if (mUids[i] == uid) {
Netta P958d0a52017-02-07 11:20:55 -0800521 int diff = mNames[i].compareTo(name);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800522 if (diff > 0) {
523 break;
524 }
525 if (diff == 0) {
526 return false;
527 }
528 }
529 }
530 insert(i, uid, name);
531 return true;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700532 }
533
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700534 public boolean remove(WorkSource other) {
Narayan Kamathee07f622018-01-08 12:20:28 +0000535 if (isEmpty() || other.isEmpty()) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800536 return false;
537 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000538
Narayan Kamathee07f622018-01-08 12:20:28 +0000539 boolean uidRemoved;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800540 if (mNames == null && other.mNames == null) {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000541 uidRemoved = removeUids(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800542 } else {
543 if (mNames == null) {
544 throw new IllegalArgumentException("Other " + other + " has names, but target "
545 + this + " does not");
546 }
547 if (other.mNames == null) {
548 throw new IllegalArgumentException("Target " + this + " has names, but other "
549 + other + " does not");
550 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000551 uidRemoved = removeUidsAndNames(other);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800552 }
Narayan Kamathf0202a92017-12-07 15:45:33 +0000553
554 boolean chainRemoved = false;
Narayan Kamathee07f622018-01-08 12:20:28 +0000555 if (other.mChains != null && mChains != null) {
556 chainRemoved = mChains.removeAll(other.mChains);
Narayan Kamathf0202a92017-12-07 15:45:33 +0000557 }
558
559 return uidRemoved || chainRemoved;
560 }
561
562 /**
563 * Create a new {@code WorkChain} associated with this WorkSource and return it.
564 *
565 * @hide
566 */
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000567 @SystemApi
Narayan Kamathf0202a92017-12-07 15:45:33 +0000568 public WorkChain createWorkChain() {
569 if (mChains == null) {
570 mChains = new ArrayList<>(4);
571 }
572
573 final WorkChain wc = new WorkChain();
574 mChains.add(wc);
575
576 return wc;
577 }
578
579 /**
Narayan Kamath81822022017-12-08 11:56:01 +0000580 * Returns {@code true} iff. this work source contains zero UIDs and zero WorkChains to
581 * attribute usage to.
582 *
583 * @hide for internal use only.
584 */
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800585 @SystemApi
586 @TestApi
Narayan Kamath81822022017-12-08 11:56:01 +0000587 public boolean isEmpty() {
588 return mNum == 0 && (mChains == null || mChains.isEmpty());
589 }
590
591 /**
Narayan Kamathf0202a92017-12-07 15:45:33 +0000592 * @return the list of {@code WorkChains} associated with this {@code WorkSource}.
593 * @hide
594 */
Suprabh Shuklaf7cffa72019-11-08 17:03:03 -0800595 @SystemApi
596 @Nullable
597 public List<WorkChain> getWorkChains() {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000598 return mChains;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800599 }
600
Narayan Kamath32684dd2018-01-08 17:32:51 +0000601 /**
602 * DO NOT USE: Hacky API provided solely for {@code GnssLocationProvider}. See
603 * {@code setReturningDiffs} as well.
604 *
605 * @hide
606 */
607 public void transferWorkChains(WorkSource other) {
608 if (mChains != null) {
609 mChains.clear();
610 }
611
612 if (other.mChains == null || other.mChains.isEmpty()) {
613 return;
614 }
615
616 if (mChains == null) {
617 mChains = new ArrayList<>(4);
618 }
619
620 mChains.addAll(other.mChains);
621 other.mChains.clear();
622 }
623
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800624 private boolean removeUids(WorkSource other) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700625 int N1 = mNum;
626 final int[] uids1 = mUids;
627 final int N2 = other.mNum;
628 final int[] uids2 = other.mUids;
629 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800630 int i1 = 0, i2 = 0;
631 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
632 while (i1 < N1 && i2 < N2) {
633 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
634 + " of " + N2);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700635 if (uids2[i2] == uids1[i1]) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800636 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
637 + ": remove " + uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700638 N1--;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800639 changed = true;
Dianne Hackborn83770282010-09-14 11:45:44 -0700640 if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800641 i2++;
642 } else if (uids2[i2] > uids1[i1]) {
643 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700644 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800645 } else {
646 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
647 i2++;
648 }
649 }
650
651 mNum = N1;
652
653 return changed;
654 }
655
656 private boolean removeUidsAndNames(WorkSource other) {
657 int N1 = mNum;
658 final int[] uids1 = mUids;
659 final String[] names1 = mNames;
660 final int N2 = other.mNum;
661 final int[] uids2 = other.mUids;
662 final String[] names2 = other.mNames;
663 boolean changed = false;
664 int i1 = 0, i2 = 0;
665 if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
666 while (i1 < N1 && i2 < N2) {
667 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
668 + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]);
669 if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) {
670 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
671 + ": remove " + uids1[i1] + " " + names1[i1]);
672 N1--;
673 changed = true;
674 if (i1 < N1) {
675 System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
676 System.arraycopy(names1, i1+1, names1, i1, N1-i1);
677 }
678 i2++;
679 } else if (uids2[i2] > uids1[i1]
680 || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) {
681 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
682 i1++;
683 } else {
684 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
685 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700686 }
687 }
688
689 mNum = N1;
690
691 return changed;
692 }
693
Artur Satayev70507ed2019-07-29 13:18:27 +0100694 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700695 private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800696 if (mNames == null && other.mNames == null) {
697 return updateUidsLocked(other, set, returnNewbs);
698 } else {
699 if (mNum > 0 && mNames == null) {
700 throw new IllegalArgumentException("Other " + other + " has names, but target "
701 + this + " does not");
702 }
703 if (other.mNum > 0 && other.mNames == null) {
704 throw new IllegalArgumentException("Target " + this + " has names, but other "
705 + other + " does not");
706 }
707 return updateUidsAndNamesLocked(other, set, returnNewbs);
708 }
709 }
710
711 private static WorkSource addWork(WorkSource cur, int newUid) {
712 if (cur == null) {
713 return new WorkSource(newUid);
714 }
715 cur.insert(cur.mNum, newUid);
716 return cur;
717 }
718
719 private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700720 int N1 = mNum;
721 int[] uids1 = mUids;
722 final int N2 = other.mNum;
723 final int[] uids2 = other.mUids;
724 boolean changed = false;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800725 int i1 = 0, i2 = 0;
726 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
727 + " returnNewbs=" + returnNewbs);
728 while (i1 < N1 || i2 < N2) {
729 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
730 + " of " + N2);
731 if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700732 // Need to insert a new uid.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800733 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
734 + ": insert " + uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700735 changed = true;
736 if (uids1 == null) {
737 uids1 = new int[4];
738 uids1[0] = uids2[i2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800739 } else if (N1 >= uids1.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700740 int[] newuids = new int[(uids1.length*3)/2];
741 if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
742 if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
743 uids1 = newuids;
744 uids1[i1] = uids2[i2];
745 } else {
746 if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
747 uids1[i1] = uids2[i2];
748 }
749 if (returnNewbs) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800750 sNewbWork = addWork(sNewbWork, uids2[i2]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700751 }
752 N1++;
753 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800754 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700755 } else {
756 if (!set) {
757 // Skip uids that already exist or are not in 'other'.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800758 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
759 if (i2 < N2 && uids2[i2] == uids1[i1]) {
760 i2++;
761 }
762 i1++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700763 } else {
764 // Remove any uids that don't exist in 'other'.
765 int start = i1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800766 while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) {
767 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]);
768 sGoneWork = addWork(sGoneWork, uids1[i1]);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700769 i1++;
770 }
771 if (start < i1) {
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800772 System.arraycopy(uids1, i1, uids1, start, N1-i1);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700773 N1 -= i1-start;
774 i1 = start;
775 }
776 // If there is a matching uid, skip it.
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800777 if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) {
778 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700779 i1++;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800780 i2++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700781 }
782 }
783 }
784 }
785
786 mNum = N1;
787 mUids = uids1;
788
789 return changed;
790 }
791
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800792 /**
793 * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'.
794 */
795 private int compare(WorkSource other, int i1, int i2) {
796 final int diff = mUids[i1] - other.mUids[i2];
797 if (diff != 0) {
798 return diff;
799 }
800 return mNames[i1].compareTo(other.mNames[i2]);
801 }
802
803 private static WorkSource addWork(WorkSource cur, int newUid, String newName) {
804 if (cur == null) {
805 return new WorkSource(newUid, newName);
806 }
807 cur.insert(cur.mNum, newUid, newName);
808 return cur;
809 }
810
811 private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) {
812 final int N2 = other.mNum;
813 final int[] uids2 = other.mUids;
814 String[] names2 = other.mNames;
815 boolean changed = false;
816 int i1 = 0, i2 = 0;
817 if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
818 + " returnNewbs=" + returnNewbs);
819 while (i1 < mNum || i2 < N2) {
820 if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2
821 + " of " + N2);
822 int diff = -1;
823 if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) {
824 // Need to insert a new uid.
825 changed = true;
826 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum
827 + ": insert " + uids2[i2] + " " + names2[i2]);
828 insert(i1, uids2[i2], names2[i2]);
829 if (returnNewbs) {
830 sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]);
831 }
832 i1++;
833 i2++;
834 } else {
835 if (!set) {
836 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
837 if (i2 < N2 && diff == 0) {
838 i2++;
839 }
840 i1++;
841 } else {
842 // Remove any uids that don't exist in 'other'.
843 int start = i1;
844 while (diff < 0) {
845 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1]
846 + " " + mNames[i1]);
847 sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]);
848 i1++;
849 if (i1 >= mNum) {
850 break;
851 }
852 diff = i2 < N2 ? compare(other, i1, i2) : -1;
853 }
854 if (start < i1) {
855 System.arraycopy(mUids, i1, mUids, start, mNum-i1);
856 System.arraycopy(mNames, i1, mNames, start, mNum-i1);
857 mNum -= i1-start;
858 i1 = start;
859 }
860 // If there is a matching uid, skip it.
861 if (i1 < mNum && diff == 0) {
862 if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
863 i1++;
864 i2++;
865 }
866 }
867 }
868 }
869
870 return changed;
871 }
872
873 private void insert(int index, int uid) {
874 if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700875 if (mUids == null) {
876 mUids = new int[4];
877 mUids[0] = uid;
878 mNum = 1;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800879 } else if (mNum >= mUids.length) {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700880 int[] newuids = new int[(mNum*3)/2];
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800881 if (index > 0) {
882 System.arraycopy(mUids, 0, newuids, 0, index);
883 }
884 if (index < mNum) {
885 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
886 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700887 mUids = newuids;
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800888 mUids[index] = uid;
889 mNum++;
890 } else {
891 if (index < mNum) {
892 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
893 }
894 mUids[index] = uid;
895 mNum++;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700896 }
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800897 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700898
Dianne Hackborn002a54e2013-01-10 17:34:55 -0800899 private void insert(int index, int uid, String name) {
900 if (mUids == null) {
901 mUids = new int[4];
902 mUids[0] = uid;
903 mNames = new String[4];
904 mNames[0] = name;
905 mNum = 1;
906 } else if (mNum >= mUids.length) {
907 int[] newuids = new int[(mNum*3)/2];
908 String[] newnames = new String[(mNum*3)/2];
909 if (index > 0) {
910 System.arraycopy(mUids, 0, newuids, 0, index);
911 System.arraycopy(mNames, 0, newnames, 0, index);
912 }
913 if (index < mNum) {
914 System.arraycopy(mUids, index, newuids, index+1, mNum-index);
915 System.arraycopy(mNames, index, newnames, index+1, mNum-index);
916 }
917 mUids = newuids;
918 mNames = newnames;
919 mUids[index] = uid;
920 mNames[index] = name;
921 mNum++;
922 } else {
923 if (index < mNum) {
924 System.arraycopy(mUids, index, mUids, index+1, mNum-index);
925 System.arraycopy(mNames, index, mNames, index+1, mNum-index);
926 }
927 mUids[index] = uid;
928 mNames[index] = name;
929 mNum++;
930 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700931 }
932
Narayan Kamathf0202a92017-12-07 15:45:33 +0000933 /**
934 * Represents an attribution chain for an item of work being performed. An attribution chain is
935 * an indexed list of {code (uid, tag)} nodes. The node at {@code index == 0} is the initiator
936 * of the work, and the node at the highest index performs the work. Nodes at other indices
937 * are intermediaries that facilitate the work. Examples :
938 *
939 * (1) Work being performed by uid=2456 (no chaining):
940 * <pre>
941 * WorkChain {
942 * mUids = { 2456 }
943 * mTags = { null }
944 * mSize = 1;
945 * }
946 * </pre>
947 *
948 * (2) Work being performed by uid=2456 (from component "c1") on behalf of uid=5678:
949 *
950 * <pre>
951 * WorkChain {
952 * mUids = { 5678, 2456 }
953 * mTags = { null, "c1" }
954 * mSize = 1
955 * }
956 * </pre>
957 *
958 * Attribution chains are mutable, though the only operation that can be performed on them
959 * is the addition of a new node at the end of the attribution chain to represent
960 *
961 * @hide
962 */
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000963 @SystemApi
964 public static final class WorkChain implements Parcelable {
Narayan Kamathf0202a92017-12-07 15:45:33 +0000965 private int mSize;
966 private int[] mUids;
967 private String[] mTags;
968
969 // @VisibleForTesting
970 public WorkChain() {
971 mSize = 0;
972 mUids = new int[4];
973 mTags = new String[4];
974 }
975
Narayan Kamathdfcdcc92018-01-25 19:58:52 +0000976 /** @hide */
977 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +0000978 public WorkChain(WorkChain other) {
979 mSize = other.mSize;
980 mUids = other.mUids.clone();
981 mTags = other.mTags.clone();
982 }
983
984 private WorkChain(Parcel in) {
985 mSize = in.readInt();
986 mUids = in.createIntArray();
987 mTags = in.createStringArray();
988 }
989
990 /**
991 * Append a node whose uid is {@code uid} and whose optional tag is {@code tag} to this
992 * {@code WorkChain}.
993 */
994 public WorkChain addNode(int uid, @Nullable String tag) {
995 if (mSize == mUids.length) {
996 resizeArrays();
997 }
998
999 mUids[mSize] = uid;
1000 mTags[mSize] = tag;
1001 mSize++;
1002
1003 return this;
1004 }
1005
Narayan Kamath81822022017-12-08 11:56:01 +00001006 /**
Narayan Kamathcbe06772017-12-27 14:22:47 +00001007 * Return the UID to which this WorkChain should be attributed to, i.e, the UID that
Marcin Oczeretko53295402018-12-17 12:44:35 +00001008 * initiated the work and not the UID performing it. Returns -1 if the chain is empty.
Narayan Kamath81822022017-12-08 11:56:01 +00001009 */
1010 public int getAttributionUid() {
Marcin Oczeretko53295402018-12-17 12:44:35 +00001011 return mSize > 0 ? mUids[0] : -1;
Narayan Kamath81822022017-12-08 11:56:01 +00001012 }
1013
Narayan Kamath32684dd2018-01-08 17:32:51 +00001014 /**
1015 * Return the tag associated with the attribution UID. See (@link #getAttributionUid}.
Marcin Oczeretko53295402018-12-17 12:44:35 +00001016 * Returns null if the chain is empty.
Narayan Kamath32684dd2018-01-08 17:32:51 +00001017 */
1018 public String getAttributionTag() {
Marcin Oczeretko53295402018-12-17 12:44:35 +00001019 return mTags.length > 0 ? mTags[0] : null;
Narayan Kamath32684dd2018-01-08 17:32:51 +00001020 }
1021
Narayan Kamathf0202a92017-12-07 15:45:33 +00001022 // TODO: The following three trivial getters are purely for testing and will be removed
1023 // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
1024 // diffing it etc.
Narayan Kamathdfcdcc92018-01-25 19:58:52 +00001025
1026
1027 /** @hide */
1028 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +00001029 public int[] getUids() {
Yangster-mac5fa895e2018-04-09 14:45:28 -07001030 int[] uids = new int[mSize];
1031 System.arraycopy(mUids, 0, uids, 0, mSize);
1032 return uids;
Narayan Kamathf0202a92017-12-07 15:45:33 +00001033 }
Narayan Kamathdfcdcc92018-01-25 19:58:52 +00001034
1035 /** @hide */
1036 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +00001037 public String[] getTags() {
Yangster-mac5fa895e2018-04-09 14:45:28 -07001038 String[] tags = new String[mSize];
1039 System.arraycopy(mTags, 0, tags, 0, mSize);
1040 return tags;
Narayan Kamathf0202a92017-12-07 15:45:33 +00001041 }
Narayan Kamathdfcdcc92018-01-25 19:58:52 +00001042
1043 /** @hide */
1044 @VisibleForTesting
Narayan Kamathf0202a92017-12-07 15:45:33 +00001045 public int getSize() {
1046 return mSize;
1047 }
1048
1049 private void resizeArrays() {
1050 final int newSize = mSize * 2;
1051 int[] uids = new int[newSize];
1052 String[] tags = new String[newSize];
1053
1054 System.arraycopy(mUids, 0, uids, 0, mSize);
1055 System.arraycopy(mTags, 0, tags, 0, mSize);
1056
1057 mUids = uids;
1058 mTags = tags;
1059 }
1060
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -07001061 @NonNull
Narayan Kamathf0202a92017-12-07 15:45:33 +00001062 @Override
1063 public String toString() {
1064 StringBuilder result = new StringBuilder("WorkChain{");
1065 for (int i = 0; i < mSize; ++i) {
1066 if (i != 0) {
1067 result.append(", ");
1068 }
1069 result.append("(");
1070 result.append(mUids[i]);
1071 if (mTags[i] != null) {
1072 result.append(", ");
1073 result.append(mTags[i]);
1074 }
1075 result.append(")");
1076 }
1077
1078 result.append("}");
1079 return result.toString();
1080 }
1081
1082 @Override
1083 public int hashCode() {
1084 return (mSize + 31 * Arrays.hashCode(mUids)) * 31 + Arrays.hashCode(mTags);
1085 }
1086
1087 @Override
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -07001088 public boolean equals(@Nullable Object o) {
Narayan Kamathf0202a92017-12-07 15:45:33 +00001089 if (o instanceof WorkChain) {
1090 WorkChain other = (WorkChain) o;
1091
1092 return mSize == other.mSize
1093 && Arrays.equals(mUids, other.mUids)
1094 && Arrays.equals(mTags, other.mTags);
1095 }
1096
1097 return false;
1098 }
1099
1100 @Override
1101 public int describeContents() {
1102 return 0;
1103 }
1104
1105 @Override
1106 public void writeToParcel(Parcel dest, int flags) {
1107 dest.writeInt(mSize);
1108 dest.writeIntArray(mUids);
1109 dest.writeStringArray(mTags);
1110 }
1111
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07001112 public static final @android.annotation.NonNull Parcelable.Creator<WorkChain> CREATOR =
Narayan Kamathf0202a92017-12-07 15:45:33 +00001113 new Parcelable.Creator<WorkChain>() {
1114 public WorkChain createFromParcel(Parcel in) {
1115 return new WorkChain(in);
1116 }
1117 public WorkChain[] newArray(int size) {
1118 return new WorkChain[size];
1119 }
1120 };
1121 }
1122
Narayan Kamath81822022017-12-08 11:56:01 +00001123 /**
1124 * Computes the differences in WorkChains contained between {@code oldWs} and {@code newWs}.
1125 *
1126 * Returns {@code null} if no differences exist, otherwise returns a two element array. The
1127 * first element is a list of "new" chains, i.e WorkChains present in {@code newWs} but not in
1128 * {@code oldWs}. The second element is a list of "gone" chains, i.e WorkChains present in
1129 * {@code oldWs} but not in {@code newWs}.
1130 *
1131 * @hide
1132 */
1133 public static ArrayList<WorkChain>[] diffChains(WorkSource oldWs, WorkSource newWs) {
1134 ArrayList<WorkChain> newChains = null;
1135 ArrayList<WorkChain> goneChains = null;
1136
1137 // TODO(narayan): This is a dumb O(M*N) algorithm that determines what has changed across
1138 // WorkSource objects. We can replace this with something smarter, for e.g by defining
1139 // a Comparator between WorkChains. It's unclear whether that will be more efficient if
1140 // the number of chains associated with a WorkSource is expected to be small
1141 if (oldWs.mChains != null) {
1142 for (int i = 0; i < oldWs.mChains.size(); ++i) {
1143 final WorkChain wc = oldWs.mChains.get(i);
1144 if (newWs.mChains == null || !newWs.mChains.contains(wc)) {
1145 if (goneChains == null) {
1146 goneChains = new ArrayList<>(oldWs.mChains.size());
1147 }
1148 goneChains.add(wc);
1149 }
1150 }
1151 }
1152
1153 if (newWs.mChains != null) {
1154 for (int i = 0; i < newWs.mChains.size(); ++i) {
1155 final WorkChain wc = newWs.mChains.get(i);
1156 if (oldWs.mChains == null || !oldWs.mChains.contains(wc)) {
1157 if (newChains == null) {
1158 newChains = new ArrayList<>(newWs.mChains.size());
1159 }
1160 newChains.add(wc);
1161 }
1162 }
1163 }
1164
1165 if (newChains != null || goneChains != null) {
1166 return new ArrayList[] { newChains, goneChains };
1167 }
1168
1169 return null;
1170 }
1171
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001172 @Override
1173 public int describeContents() {
1174 return 0;
1175 }
1176
1177 @Override
1178 public void writeToParcel(Parcel dest, int flags) {
1179 dest.writeInt(mNum);
1180 dest.writeIntArray(mUids);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001181 dest.writeStringArray(mNames);
Narayan Kamathf0202a92017-12-07 15:45:33 +00001182
1183 if (mChains == null) {
1184 dest.writeInt(-1);
1185 } else {
1186 dest.writeInt(mChains.size());
1187 dest.writeParcelableList(mChains, flags);
1188 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001189 }
1190
Jeff Brown96307042012-07-27 15:51:34 -07001191 @Override
1192 public String toString() {
1193 StringBuilder result = new StringBuilder();
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001194 result.append("WorkSource{");
Jeff Brown96307042012-07-27 15:51:34 -07001195 for (int i = 0; i < mNum; i++) {
1196 if (i != 0) {
1197 result.append(", ");
1198 }
1199 result.append(mUids[i]);
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001200 if (mNames != null) {
1201 result.append(" ");
1202 result.append(mNames[i]);
1203 }
Jeff Brown96307042012-07-27 15:51:34 -07001204 }
Narayan Kamathf0202a92017-12-07 15:45:33 +00001205
1206 if (mChains != null) {
1207 result.append(" chains=");
1208 for (int i = 0; i < mChains.size(); ++i) {
1209 if (i != 0) {
1210 result.append(", ");
1211 }
1212 result.append(mChains.get(i));
1213 }
1214 }
1215
Dianne Hackborn002a54e2013-01-10 17:34:55 -08001216 result.append("}");
Jeff Brown96307042012-07-27 15:51:34 -07001217 return result.toString();
1218 }
1219
Netta P958d0a52017-02-07 11:20:55 -08001220 /** @hide */
1221 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1222 final long workSourceToken = proto.start(fieldId);
1223 for (int i = 0; i < mNum; i++) {
1224 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1225 proto.write(WorkSourceProto.WorkSourceContentProto.UID, mUids[i]);
1226 if (mNames != null) {
1227 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, mNames[i]);
1228 }
1229 proto.end(contentProto);
1230 }
Narayan Kamath80434a72017-12-27 15:44:17 +00001231
1232 if (mChains != null) {
1233 for (int i = 0; i < mChains.size(); i++) {
1234 final WorkChain wc = mChains.get(i);
1235 final long workChain = proto.start(WorkSourceProto.WORK_CHAINS);
1236
1237 final String[] tags = wc.getTags();
1238 final int[] uids = wc.getUids();
1239 for (int j = 0; j < tags.length; j++) {
1240 final long contentProto = proto.start(WorkSourceProto.WORK_SOURCE_CONTENTS);
1241 proto.write(WorkSourceProto.WorkSourceContentProto.UID, uids[j]);
1242 proto.write(WorkSourceProto.WorkSourceContentProto.NAME, tags[j]);
1243 proto.end(contentProto);
1244 }
1245
1246 proto.end(workChain);
1247 }
1248 }
1249
Netta P958d0a52017-02-07 11:20:55 -08001250 proto.end(workSourceToken);
1251 }
1252
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07001253 public static final @android.annotation.NonNull Parcelable.Creator<WorkSource> CREATOR
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001254 = new Parcelable.Creator<WorkSource>() {
1255 public WorkSource createFromParcel(Parcel in) {
1256 return new WorkSource(in);
1257 }
1258 public WorkSource[] newArray(int size) {
1259 return new WorkSource[size];
1260 }
1261 };
1262}