WorkSource.java revision 83770289f8eca5aa4c62647a6c4d9a5bd4c80a45
1package android.os;
2
3/**
4 * Describes the source of some work that may be done by someone else.
5 * Currently the public representation of what a work source is is not
6 * defined; this is an opaque container.
7 */
8public class WorkSource implements Parcelable {
9    int mNum;
10    int[] mUids;
11
12    /**
13     * Internal statics to avoid object allocations in some operations.
14     * The WorkSource object itself is not thread safe, but we need to
15     * hold sTmpWorkSource lock while working with these statics.
16     */
17    static final WorkSource sTmpWorkSource = new WorkSource(0);
18    /**
19     * For returning newbie work from a modification operation.
20     */
21    static WorkSource sNewbWork;
22    /**
23     * For returning gone work form a modification operation.
24     */
25    static WorkSource sGoneWork;
26
27    /**
28     * Create an empty work source.
29     */
30    public WorkSource() {
31        mNum = 0;
32    }
33
34    /**
35     * Create a new WorkSource that is a copy of an existing one.
36     * If <var>orig</var> is null, an empty WorkSource is created.
37     */
38    public WorkSource(WorkSource orig) {
39        if (orig == null) {
40            mNum = 0;
41            return;
42        }
43        mNum = orig.mNum;
44        if (orig.mUids != null) {
45            mUids = orig.mUids.clone();
46        } else {
47            mUids = null;
48        }
49    }
50
51    /** @hide */
52    public WorkSource(int uid) {
53        mNum = 1;
54        mUids = new int[] { uid, 0 };
55    }
56
57    WorkSource(Parcel in) {
58        mNum = in.readInt();
59        mUids = in.createIntArray();
60    }
61
62    /** @hide */
63    public int size() {
64        return mNum;
65    }
66
67    /** @hide */
68    public int get(int index) {
69        return mUids[index];
70    }
71
72    /**
73     * Clear this WorkSource to be empty.
74     */
75    public void clear() {
76        mNum = 0;
77    }
78
79    /**
80     * Compare this WorkSource with another.
81     * @param other The WorkSource to compare against.
82     * @return If there is a difference, true is returned.
83     */
84    public boolean diff(WorkSource other) {
85        int N = mNum;
86        if (N != other.mNum) {
87            return true;
88        }
89        final int[] uids1 = mUids;
90        final int[] uids2 = other.mUids;
91        for (int i=0; i<N; i++) {
92            if (uids1[i] != uids2[i]) {
93                return true;
94            }
95        }
96        return false;
97    }
98
99    /**
100     * Replace the current contents of this work source with the given
101     * work source.  If <var>other</var> is null, the current work source
102     * will be made empty.
103     */
104    public void set(WorkSource other) {
105        if (other == null) {
106            mNum = 0;
107            return;
108        }
109        mNum = other.mNum;
110        if (other.mUids != null) {
111            if (mUids != null && mUids.length >= mNum) {
112                System.arraycopy(other.mUids, 0, mUids, 0, mNum);
113            } else {
114                mUids = other.mUids.clone();
115            }
116        } else {
117            mUids = null;
118        }
119    }
120
121    /** @hide */
122    public void set(int uid) {
123        mNum = 1;
124        if (mUids == null) mUids = new int[2];
125        mUids[0] = uid;
126    }
127
128    /** @hide */
129    public WorkSource[] setReturningDiffs(WorkSource other) {
130        synchronized (sTmpWorkSource) {
131            sNewbWork = null;
132            sGoneWork = null;
133            updateLocked(other, true, true);
134            if (sNewbWork != null || sGoneWork != null) {
135                WorkSource[] diffs = new WorkSource[2];
136                diffs[0] = sNewbWork;
137                diffs[1] = sGoneWork;
138                return diffs;
139            }
140            return null;
141        }
142    }
143
144    /**
145     * Merge the contents of <var>other</var> WorkSource in to this one.
146     *
147     * @param other The other WorkSource whose contents are to be merged.
148     * @return Returns true if any new sources were added.
149     */
150    public boolean add(WorkSource other) {
151        synchronized (sTmpWorkSource) {
152            return updateLocked(other, false, false);
153        }
154    }
155
156    /** @hide */
157    public WorkSource addReturningNewbs(WorkSource other) {
158        synchronized (sTmpWorkSource) {
159            sNewbWork = null;
160            updateLocked(other, false, true);
161            return sNewbWork;
162        }
163    }
164
165    /** @hide */
166    public boolean add(int uid) {
167        synchronized (sTmpWorkSource) {
168            sTmpWorkSource.mUids[0] = uid;
169            return updateLocked(sTmpWorkSource, false, false);
170        }
171    }
172
173    /** @hide */
174    public WorkSource addReturningNewbs(int uid) {
175        synchronized (sTmpWorkSource) {
176            sNewbWork = null;
177            sTmpWorkSource.mUids[0] = uid;
178            updateLocked(sTmpWorkSource, false, true);
179            return sNewbWork;
180        }
181    }
182
183    public boolean remove(WorkSource other) {
184        int N1 = mNum;
185        final int[] uids1 = mUids;
186        final int N2 = other.mNum;
187        final int[] uids2 = other.mUids;
188        boolean changed = false;
189        int i1 = 0;
190        for (int i2=0; i2<N2 && i1<N1; i2++) {
191            if (uids2[i2] == uids1[i1]) {
192                N1--;
193                if (i1 < N1) System.arraycopy(uids1, i1+1, uids1, i1, N1-i1);
194            }
195            while (i1 < N1 && uids2[i2] > uids1[i1]) {
196                i1++;
197            }
198        }
199
200        mNum = N1;
201
202        return changed;
203    }
204
205    private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
206        int N1 = mNum;
207        int[] uids1 = mUids;
208        final int N2 = other.mNum;
209        final int[] uids2 = other.mUids;
210        boolean changed = false;
211        int i1 = 0;
212        for (int i2=0; i2<N2; i2++) {
213            if (i1 >= N1 || uids2[i2] < uids1[i1]) {
214                // Need to insert a new uid.
215                changed = true;
216                if (uids1 == null) {
217                    uids1 = new int[4];
218                    uids1[0] = uids2[i2];
219                } else if (i1 >= uids1.length) {
220                    int[] newuids = new int[(uids1.length*3)/2];
221                    if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
222                    if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
223                    uids1 = newuids;
224                    uids1[i1] = uids2[i2];
225                } else {
226                    if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
227                    uids1[i1] = uids2[i2];
228                }
229                if (returnNewbs) {
230                    if (sNewbWork == null) {
231                        sNewbWork = new WorkSource(uids2[i2]);
232                    } else {
233                        sNewbWork.addLocked(uids2[i2]);
234                    }
235                }
236                N1++;
237                i1++;
238            } else {
239                if (!set) {
240                    // Skip uids that already exist or are not in 'other'.
241                    do {
242                        i1++;
243                    } while (i1 < N1 && uids2[i2] >= uids1[i1]);
244                } else {
245                    // Remove any uids that don't exist in 'other'.
246                    int start = i1;
247                    while (i1 < N1 && uids2[i2] > uids1[i1]) {
248                        if (sGoneWork == null) {
249                            sGoneWork = new WorkSource(uids1[i1]);
250                        } else {
251                            sGoneWork.addLocked(uids1[i1]);
252                        }
253                        i1++;
254                    }
255                    if (start < i1) {
256                        System.arraycopy(uids1, i1, uids1, start, i1-start);
257                        N1 -= i1-start;
258                        i1 = start;
259                    }
260                    // If there is a matching uid, skip it.
261                    if (i1 < N1 && uids2[i1] == uids1[i1]) {
262                        i1++;
263                    }
264                }
265            }
266        }
267
268        mNum = N1;
269        mUids = uids1;
270
271        return changed;
272    }
273
274    private void addLocked(int uid) {
275        if (mUids == null) {
276            mUids = new int[4];
277            mUids[0] = uid;
278            mNum = 1;
279            return;
280        }
281        if (mNum >= mUids.length) {
282            int[] newuids = new int[(mNum*3)/2];
283            System.arraycopy(mUids, 0, newuids, 0, mNum);
284            mUids = newuids;
285        }
286
287        mUids[mNum] = uid;
288        mNum++;
289    }
290
291    @Override
292    public int describeContents() {
293        return 0;
294    }
295
296    @Override
297    public void writeToParcel(Parcel dest, int flags) {
298        dest.writeInt(mNum);
299        dest.writeIntArray(mUids);
300    }
301
302    public static final Parcelable.Creator<WorkSource> CREATOR
303            = new Parcelable.Creator<WorkSource>() {
304        public WorkSource createFromParcel(Parcel in) {
305            return new WorkSource(in);
306        }
307        public WorkSource[] newArray(int size) {
308            return new WorkSource[size];
309        }
310    };
311}
312