WorkSource.java revision 002a54e2291eeb3a3fd0b6b3f9dbc96a7c805062
1package android.os;
2
3import android.util.Log;
4
5import java.util.Arrays;
6
7/**
8 * Describes the source of some work that may be done by someone else.
9 * Currently the public representation of what a work source is is not
10 * defined; this is an opaque container.
11 */
12public class WorkSource implements Parcelable {
13    static final String TAG = "WorkSource";
14    static final boolean DEBUG = true;
15
16    int mNum;
17    int[] mUids;
18    String[] mNames;
19
20    /**
21     * Internal statics to avoid object allocations in some operations.
22     * The WorkSource object itself is not thread safe, but we need to
23     * hold sTmpWorkSource lock while working with these statics.
24     */
25    static final WorkSource sTmpWorkSource = new WorkSource(0);
26    /**
27     * For returning newbie work from a modification operation.
28     */
29    static WorkSource sNewbWork;
30    /**
31     * For returning gone work form a modification operation.
32     */
33    static WorkSource sGoneWork;
34
35    /**
36     * Create an empty work source.
37     */
38    public WorkSource() {
39        mNum = 0;
40    }
41
42    /**
43     * Create a new WorkSource that is a copy of an existing one.
44     * If <var>orig</var> is null, an empty WorkSource is created.
45     */
46    public WorkSource(WorkSource orig) {
47        if (orig == null) {
48            mNum = 0;
49            return;
50        }
51        mNum = orig.mNum;
52        if (orig.mUids != null) {
53            mUids = orig.mUids.clone();
54            mNames = orig.mNames != null ? orig.mNames.clone() : null;
55        } else {
56            mUids = null;
57            mNames = null;
58        }
59    }
60
61    /** @hide */
62    public WorkSource(int uid) {
63        mNum = 1;
64        mUids = new int[] { uid, 0 };
65        mNames = null;
66    }
67
68    /** @hide */
69    public WorkSource(int uid, String name) {
70        if (name == null) {
71            throw new NullPointerException("Name can't be null");
72        }
73        mNum = 1;
74        mUids = new int[] { uid, 0 };
75        mNames = new String[] { name, null };
76    }
77
78    WorkSource(Parcel in) {
79        mNum = in.readInt();
80        mUids = in.createIntArray();
81        mNames = in.createStringArray();
82    }
83
84    /** @hide */
85    public int size() {
86        return mNum;
87    }
88
89    /** @hide */
90    public int get(int index) {
91        return mUids[index];
92    }
93
94    /** @hide */
95    public String getName(int index) {
96        return mNames != null ? mNames[index] : null;
97    }
98
99    /**
100     * Clear this WorkSource to be empty.
101     */
102    public void clear() {
103        mNum = 0;
104    }
105
106    @Override
107    public boolean equals(Object o) {
108        return o instanceof WorkSource && !diff((WorkSource)o);
109    }
110
111    @Override
112    public int hashCode() {
113        int result = 0;
114        for (int i = 0; i < mNum; i++) {
115            result = ((result << 4) | (result >>> 28)) ^ mUids[i];
116        }
117        if (mNames != null) {
118            for (int i = 0; i < mNum; i++) {
119                result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
120            }
121        }
122        return result;
123    }
124
125    /**
126     * Compare this WorkSource with another.
127     * @param other The WorkSource to compare against.
128     * @return If there is a difference, true is returned.
129     */
130    public boolean diff(WorkSource other) {
131        int N = mNum;
132        if (N != other.mNum) {
133            return true;
134        }
135        final int[] uids1 = mUids;
136        final int[] uids2 = other.mUids;
137        final String[] names1 = mNames;
138        final String[] names2 = other.mNames;
139        for (int i=0; i<N; i++) {
140            if (uids1[i] != uids2[i]) {
141                return true;
142            }
143            if (names1 != null && names2 != null && !names1[i].equals(names2[i])) {
144                return true;
145            }
146        }
147        return false;
148    }
149
150    /**
151     * Replace the current contents of this work source with the given
152     * work source.  If <var>other</var> is null, the current work source
153     * will be made empty.
154     */
155    public void set(WorkSource other) {
156        if (other == null) {
157            mNum = 0;
158            return;
159        }
160        mNum = other.mNum;
161        if (other.mUids != null) {
162            if (mUids != null && mUids.length >= mNum) {
163                System.arraycopy(other.mUids, 0, mUids, 0, mNum);
164            } else {
165                mUids = other.mUids.clone();
166            }
167            if (other.mNames != null) {
168                if (mNames != null && mNames.length >= mNum) {
169                    System.arraycopy(other.mNames, 0, mNames, 0, mNum);
170                } else {
171                    mNames = other.mNames.clone();
172                }
173            } else {
174                mNames = null;
175            }
176        } else {
177            mUids = null;
178            mNames = null;
179        }
180    }
181
182    /** @hide */
183    public void set(int uid) {
184        mNum = 1;
185        if (mUids == null) mUids = new int[2];
186        mUids[0] = uid;
187        mNames = null;
188    }
189
190    /** @hide */
191    public void set(int uid, String name) {
192        if (name == null) {
193            throw new NullPointerException("Name can't be null");
194        }
195        mNum = 1;
196        if (mUids == null) {
197            mUids = new int[2];
198            mNames = new String[2];
199        }
200        mUids[0] = uid;
201        mNames[0] = name;
202        mNames = null;
203    }
204
205    /** @hide */
206    public WorkSource[] setReturningDiffs(WorkSource other) {
207        synchronized (sTmpWorkSource) {
208            sNewbWork = null;
209            sGoneWork = null;
210            updateLocked(other, true, true);
211            if (sNewbWork != null || sGoneWork != null) {
212                WorkSource[] diffs = new WorkSource[2];
213                diffs[0] = sNewbWork;
214                diffs[1] = sGoneWork;
215                return diffs;
216            }
217            return null;
218        }
219    }
220
221    /**
222     * Merge the contents of <var>other</var> WorkSource in to this one.
223     *
224     * @param other The other WorkSource whose contents are to be merged.
225     * @return Returns true if any new sources were added.
226     */
227    public boolean add(WorkSource other) {
228        synchronized (sTmpWorkSource) {
229            return updateLocked(other, false, false);
230        }
231    }
232
233    /** @hide */
234    public WorkSource addReturningNewbs(WorkSource other) {
235        synchronized (sTmpWorkSource) {
236            sNewbWork = null;
237            updateLocked(other, false, true);
238            return sNewbWork;
239        }
240    }
241
242    /** @hide */
243    public boolean add(int uid) {
244        if (mNum <= 0) {
245            mNames = null;
246            insert(0, uid);
247            return true;
248        }
249        if (mNames != null) {
250            throw new IllegalArgumentException("Adding without name to named " + this);
251        }
252        int i = Arrays.binarySearch(mUids, 0, mNum, uid);
253        if (DEBUG) Log.d(TAG, "Adding uid " + uid + " to " + this + ": binsearch res = " + i);
254        if (i >= 0) {
255            return false;
256        }
257        insert(-i-1, uid);
258        return true;
259    }
260
261    /** @hide */
262    public boolean add(int uid, String name) {
263        if (mNum <= 0) {
264            insert(0, uid, name);
265            return true;
266        }
267        if (mNames == null) {
268            throw new IllegalArgumentException("Adding name to unnamed " + this);
269        }
270        int i;
271        for (i=0; i<mNum; i++) {
272            if (mUids[i] > uid) {
273                break;
274            }
275            if (mUids[i] == uid) {
276                int diff = mNames[i].compareTo(name);
277                if (diff > 0) {
278                    break;
279                }
280                if (diff == 0) {
281                    return false;
282                }
283            }
284        }
285        insert(i, uid, name);
286        return true;
287    }
288
289    /** @hide */
290    public WorkSource addReturningNewbs(int uid) {
291        synchronized (sTmpWorkSource) {
292            sNewbWork = null;
293            sTmpWorkSource.mUids[0] = uid;
294            updateLocked(sTmpWorkSource, false, true);
295            return sNewbWork;
296        }
297    }
298
299    public boolean remove(WorkSource other) {
300        if (mNum <= 0 || other.mNum <= 0) {
301            return false;
302        }
303        if (mNames == null && other.mNames == null) {
304            return removeUids(other);
305        } else {
306            if (mNames == null) {
307                throw new IllegalArgumentException("Other " + other + " has names, but target "
308                        + this + " does not");
309            }
310            if (other.mNames == null) {
311                throw new IllegalArgumentException("Target " + this + " has names, but other "
312                        + other + " does not");
313            }
314            return removeUidsAndNames(other);
315        }
316    }
317
318    /** @hide */
319    public WorkSource stripNames() {
320        if (mNum <= 0) {
321            return new WorkSource();
322        }
323        WorkSource result = new WorkSource();
324        int lastUid = -1;
325        for (int i=0; i<mNum; i++) {
326            int uid = mUids[i];
327            if (i == 0 || lastUid != uid) {
328                result.add(uid);
329            }
330        }
331        return result;
332    }
333
334    private boolean removeUids(WorkSource other) {
335        int N1 = mNum;
336        final int[] uids1 = mUids;
337        final int N2 = other.mNum;
338        final int[] uids2 = other.mUids;
339        boolean changed = false;
340        int i1 = 0, i2 = 0;
341        if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
342        while (i1 < N1 && i2 < N2) {
343            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
344                    + " of " + N2);
345            if (uids2[i2] == uids1[i1]) {
346                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
347                        + ": remove " + uids1[i1]);
348                N1--;
349                changed = true;
350                if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
351                i2++;
352            } else if (uids2[i2] > uids1[i1]) {
353                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
354                i1++;
355            } else {
356                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
357                i2++;
358            }
359        }
360
361        mNum = N1;
362
363        return changed;
364    }
365
366    private boolean removeUidsAndNames(WorkSource other) {
367        int N1 = mNum;
368        final int[] uids1 = mUids;
369        final String[] names1 = mNames;
370        final int N2 = other.mNum;
371        final int[] uids2 = other.mUids;
372        final String[] names2 = other.mNames;
373        boolean changed = false;
374        int i1 = 0, i2 = 0;
375        if (DEBUG) Log.d(TAG, "Remove " + other + " from " + this);
376        while (i1 < N1 && i2 < N2) {
377            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
378                    + " of " + N2 + ": " + uids1[i1] + " " + names1[i1]);
379            if (uids2[i2] == uids1[i1] && names2[i2].equals(names1[i1])) {
380                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
381                        + ": remove " + uids1[i1] + " " + names1[i1]);
382                N1--;
383                changed = true;
384                if (i1 < N1) {
385                    System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
386                    System.arraycopy(names1, i1+1, names1, i1, N1-i1);
387                }
388                i2++;
389            } else if (uids2[i2] > uids1[i1]
390                    || (uids2[i2] == uids1[i1] && names2[i2].compareTo(names1[i1]) > 0)) {
391                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i1");
392                i1++;
393            } else {
394                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip i2");
395                i2++;
396            }
397        }
398
399        mNum = N1;
400
401        return changed;
402    }
403
404    private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
405        if (mNames == null && other.mNames == null) {
406            return updateUidsLocked(other, set, returnNewbs);
407        } else {
408            if (mNum > 0 && mNames == null) {
409                throw new IllegalArgumentException("Other " + other + " has names, but target "
410                        + this + " does not");
411            }
412            if (other.mNum > 0 && other.mNames == null) {
413                throw new IllegalArgumentException("Target " + this + " has names, but other "
414                        + other + " does not");
415            }
416            return updateUidsAndNamesLocked(other, set, returnNewbs);
417        }
418    }
419
420    private static WorkSource addWork(WorkSource cur, int newUid) {
421        if (cur == null) {
422            return new WorkSource(newUid);
423        }
424        cur.insert(cur.mNum, newUid);
425        return cur;
426    }
427
428    private boolean updateUidsLocked(WorkSource other, boolean set, boolean returnNewbs) {
429        int N1 = mNum;
430        int[] uids1 = mUids;
431        final int N2 = other.mNum;
432        final int[] uids2 = other.mUids;
433        boolean changed = false;
434        int i1 = 0, i2 = 0;
435        if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
436                + " returnNewbs=" + returnNewbs);
437        while (i1 < N1 || i2 < N2) {
438            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + N1 + ", other @ " + i2
439                    + " of " + N2);
440            if (i1 >= N1 || (i2 < N2 && uids2[i2] < uids1[i1])) {
441                // Need to insert a new uid.
442                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1
443                        + ": insert " + uids2[i2]);
444                changed = true;
445                if (uids1 == null) {
446                    uids1 = new int[4];
447                    uids1[0] = uids2[i2];
448                } else if (N1 >= uids1.length) {
449                    int[] newuids = new int[(uids1.length*3)/2];
450                    if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
451                    if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
452                    uids1 = newuids;
453                    uids1[i1] = uids2[i2];
454                } else {
455                    if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
456                    uids1[i1] = uids2[i2];
457                }
458                if (returnNewbs) {
459                    sNewbWork = addWork(sNewbWork, uids2[i2]);
460                }
461                N1++;
462                i1++;
463                i2++;
464            } else {
465                if (!set) {
466                    // Skip uids that already exist or are not in 'other'.
467                    if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
468                    if (i2 < N2 && uids2[i2] == uids1[i1]) {
469                        i2++;
470                    }
471                    i1++;
472                } else {
473                    // Remove any uids that don't exist in 'other'.
474                    int start = i1;
475                    while (i1 < N1 && (i2 >= N2 || uids2[i2] > uids1[i1])) {
476                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + uids1[i1]);
477                        sGoneWork = addWork(sGoneWork, uids1[i1]);
478                        i1++;
479                    }
480                    if (start < i1) {
481                        System.arraycopy(uids1, i1, uids1, start, N1-i1);
482                        N1 -= i1-start;
483                        i1 = start;
484                    }
485                    // If there is a matching uid, skip it.
486                    if (i1 < N1 && i2 < N2 && uids2[i2] == uids1[i1]) {
487                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + N1 + ": skip");
488                        i1++;
489                        i2++;
490                    }
491                }
492            }
493        }
494
495        mNum = N1;
496        mUids = uids1;
497
498        return changed;
499    }
500
501    /**
502     * Returns 0 if equal, negative if 'this' is before 'other', positive if 'this' is after 'other'.
503     */
504    private int compare(WorkSource other, int i1, int i2) {
505        final int diff = mUids[i1] - other.mUids[i2];
506        if (diff != 0) {
507            return diff;
508        }
509        return mNames[i1].compareTo(other.mNames[i2]);
510    }
511
512    private static WorkSource addWork(WorkSource cur, int newUid, String newName) {
513        if (cur == null) {
514            return new WorkSource(newUid, newName);
515        }
516        cur.insert(cur.mNum, newUid, newName);
517        return cur;
518    }
519
520    private boolean updateUidsAndNamesLocked(WorkSource other, boolean set, boolean returnNewbs) {
521        final int N2 = other.mNum;
522        final int[] uids2 = other.mUids;
523        String[] names2 = other.mNames;
524        boolean changed = false;
525        int i1 = 0, i2 = 0;
526        if (DEBUG) Log.d(TAG, "Update " + this + " with " + other + " set=" + set
527                + " returnNewbs=" + returnNewbs);
528        while (i1 < mNum || i2 < N2) {
529            if (DEBUG) Log.d(TAG, "Step: target @ " + i1 + " of " + mNum + ", other @ " + i2
530                    + " of " + N2);
531            int diff = -1;
532            if (i1 >= mNum || (i2 < N2 && (diff=compare(other, i1, i2)) > 0)) {
533                // Need to insert a new uid.
534                changed = true;
535                if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum
536                        + ": insert " + uids2[i2] + " " + names2[i2]);
537                insert(i1, uids2[i2], names2[i2]);
538                if (returnNewbs) {
539                    sNewbWork = addWork(sNewbWork, uids2[i2], names2[i2]);
540                }
541                i1++;
542                i2++;
543            } else {
544                if (!set) {
545                    if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
546                    if (i2 < N2 && diff == 0) {
547                        i2++;
548                    }
549                    i1++;
550                } else {
551                    // Remove any uids that don't exist in 'other'.
552                    int start = i1;
553                    while (diff < 0) {
554                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + ": remove " + mUids[i1]
555                                + " " + mNames[i1]);
556                        sGoneWork = addWork(sGoneWork, mUids[i1], mNames[i1]);
557                        i1++;
558                        if (i1 >= mNum) {
559                            break;
560                        }
561                        diff = i2 < N2 ? compare(other, i1, i2) : -1;
562                    }
563                    if (start < i1) {
564                        System.arraycopy(mUids, i1, mUids, start, mNum-i1);
565                        System.arraycopy(mNames, i1, mNames, start, mNum-i1);
566                        mNum -= i1-start;
567                        i1 = start;
568                    }
569                    // If there is a matching uid, skip it.
570                    if (i1 < mNum && diff == 0) {
571                        if (DEBUG) Log.d(TAG, "i1=" + i1 + " i2=" + i2 + " N1=" + mNum + ": skip");
572                        i1++;
573                        i2++;
574                    }
575                }
576            }
577        }
578
579        return changed;
580    }
581
582    private void insert(int index, int uid)  {
583        if (DEBUG) Log.d(TAG, "Insert in " + this + " @ " + index + " uid " + uid);
584        if (mUids == null) {
585            mUids = new int[4];
586            mUids[0] = uid;
587            mNum = 1;
588        } else if (mNum >= mUids.length) {
589            int[] newuids = new int[(mNum*3)/2];
590            if (index > 0) {
591                System.arraycopy(mUids, 0, newuids, 0, index);
592            }
593            if (index < mNum) {
594                System.arraycopy(mUids, index, newuids, index+1, mNum-index);
595            }
596            mUids = newuids;
597            mUids[index] = uid;
598            mNum++;
599        } else {
600            if (index < mNum) {
601                System.arraycopy(mUids, index, mUids, index+1, mNum-index);
602            }
603            mUids[index] = uid;
604            mNum++;
605        }
606    }
607
608    private void insert(int index, int uid, String name)  {
609        if (mUids == null) {
610            mUids = new int[4];
611            mUids[0] = uid;
612            mNames = new String[4];
613            mNames[0] = name;
614            mNum = 1;
615        } else if (mNum >= mUids.length) {
616            int[] newuids = new int[(mNum*3)/2];
617            String[] newnames = new String[(mNum*3)/2];
618            if (index > 0) {
619                System.arraycopy(mUids, 0, newuids, 0, index);
620                System.arraycopy(mNames, 0, newnames, 0, index);
621            }
622            if (index < mNum) {
623                System.arraycopy(mUids, index, newuids, index+1, mNum-index);
624                System.arraycopy(mNames, index, newnames, index+1, mNum-index);
625            }
626            mUids = newuids;
627            mNames = newnames;
628            mUids[index] = uid;
629            mNames[index] = name;
630            mNum++;
631        } else {
632            if (index < mNum) {
633                System.arraycopy(mUids, index, mUids, index+1, mNum-index);
634                System.arraycopy(mNames, index, mNames, index+1, mNum-index);
635            }
636            mUids[index] = uid;
637            mNames[index] = name;
638            mNum++;
639        }
640    }
641
642    @Override
643    public int describeContents() {
644        return 0;
645    }
646
647    @Override
648    public void writeToParcel(Parcel dest, int flags) {
649        dest.writeInt(mNum);
650        dest.writeIntArray(mUids);
651        dest.writeStringArray(mNames);
652    }
653
654    @Override
655    public String toString() {
656        StringBuilder result = new StringBuilder();
657        result.append("WorkSource{");
658        for (int i = 0; i < mNum; i++) {
659            if (i != 0) {
660                result.append(", ");
661            }
662            result.append(mUids[i]);
663            if (mNames != null) {
664                result.append(" ");
665                result.append(mNames[i]);
666            }
667        }
668        result.append("}");
669        return result.toString();
670    }
671
672    public static final Parcelable.Creator<WorkSource> CREATOR
673            = new Parcelable.Creator<WorkSource>() {
674        public WorkSource createFromParcel(Parcel in) {
675            return new WorkSource(in);
676        }
677        public WorkSource[] newArray(int size) {
678            return new WorkSource[size];
679        }
680    };
681}
682