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