1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.pm; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.annotation.UserIdInt; 21 import android.content.ComponentName; 22 import android.content.pm.ShortcutManager; 23 import android.metrics.LogMaker; 24 import android.text.TextUtils; 25 import android.text.format.Formatter; 26 import android.util.ArrayMap; 27 import android.util.Log; 28 import android.util.Slog; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.logging.MetricsLogger; 32 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 33 import com.android.internal.util.Preconditions; 34 import com.android.server.pm.ShortcutService.DumpFilter; 35 import com.android.server.pm.ShortcutService.InvalidFileFormatException; 36 37 import org.json.JSONArray; 38 import org.json.JSONException; 39 import org.json.JSONObject; 40 import org.xmlpull.v1.XmlPullParser; 41 import org.xmlpull.v1.XmlPullParserException; 42 import org.xmlpull.v1.XmlSerializer; 43 44 import java.io.File; 45 import java.io.IOException; 46 import java.io.PrintWriter; 47 import java.util.Objects; 48 import java.util.function.Consumer; 49 50 /** 51 * User information used by {@link ShortcutService}. 52 * 53 * All methods should be guarded by {@code #mService.mLock}. 54 */ 55 class ShortcutUser { 56 private static final String TAG = ShortcutService.TAG; 57 58 static final String TAG_ROOT = "user"; 59 private static final String TAG_LAUNCHER = "launcher"; 60 61 private static final String ATTR_VALUE = "value"; 62 private static final String ATTR_KNOWN_LOCALES = "locales"; 63 64 // Suffix "2" was added to force rescan all packages after the next OTA. 65 private static final String ATTR_LAST_APP_SCAN_TIME = "last-app-scan-time2"; 66 private static final String ATTR_LAST_APP_SCAN_OS_FINGERPRINT = "last-app-scan-fp"; 67 private static final String ATTR_RESTORE_SOURCE_FINGERPRINT = "restore-from-fp"; 68 private static final String KEY_USER_ID = "userId"; 69 private static final String KEY_LAUNCHERS = "launchers"; 70 private static final String KEY_PACKAGES = "packages"; 71 72 static final class PackageWithUser { 73 final int userId; 74 final String packageName; 75 PackageWithUser(int userId, String packageName)76 private PackageWithUser(int userId, String packageName) { 77 this.userId = userId; 78 this.packageName = Preconditions.checkNotNull(packageName); 79 } 80 of(int userId, String packageName)81 public static PackageWithUser of(int userId, String packageName) { 82 return new PackageWithUser(userId, packageName); 83 } 84 of(ShortcutPackageItem spi)85 public static PackageWithUser of(ShortcutPackageItem spi) { 86 return new PackageWithUser(spi.getPackageUserId(), spi.getPackageName()); 87 } 88 89 @Override hashCode()90 public int hashCode() { 91 return packageName.hashCode() ^ userId; 92 } 93 94 @Override equals(Object obj)95 public boolean equals(Object obj) { 96 if (!(obj instanceof PackageWithUser)) { 97 return false; 98 } 99 final PackageWithUser that = (PackageWithUser) obj; 100 101 return userId == that.userId && packageName.equals(that.packageName); 102 } 103 104 @Override toString()105 public String toString() { 106 return String.format("[Package: %d, %s]", userId, packageName); 107 } 108 } 109 110 final ShortcutService mService; 111 112 @UserIdInt 113 private final int mUserId; 114 115 private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>(); 116 117 private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>(); 118 119 /** 120 * Last known launcher. It's used when the default launcher isn't set in PM -- i.e. 121 * when getHomeActivitiesAsUser() return null. We need it so that in this situation the 122 * previously default launcher can still access shortcuts. 123 */ 124 private ComponentName mLastKnownLauncher; 125 126 /** In-memory-cached default launcher. */ 127 private ComponentName mCachedLauncher; 128 129 private String mKnownLocales; 130 131 private long mLastAppScanTime; 132 133 private String mLastAppScanOsFingerprint; 134 private String mRestoreFromOsFingerprint; 135 ShortcutUser(ShortcutService service, int userId)136 public ShortcutUser(ShortcutService service, int userId) { 137 mService = service; 138 mUserId = userId; 139 } 140 getUserId()141 public int getUserId() { 142 return mUserId; 143 } 144 getLastAppScanTime()145 public long getLastAppScanTime() { 146 return mLastAppScanTime; 147 } 148 setLastAppScanTime(long lastAppScanTime)149 public void setLastAppScanTime(long lastAppScanTime) { 150 mLastAppScanTime = lastAppScanTime; 151 } 152 getLastAppScanOsFingerprint()153 public String getLastAppScanOsFingerprint() { 154 return mLastAppScanOsFingerprint; 155 } 156 setLastAppScanOsFingerprint(String lastAppScanOsFingerprint)157 public void setLastAppScanOsFingerprint(String lastAppScanOsFingerprint) { 158 mLastAppScanOsFingerprint = lastAppScanOsFingerprint; 159 } 160 161 // We don't expose this directly to non-test code because only ShortcutUser should add to/ 162 // remove from it. 163 @VisibleForTesting getAllPackagesForTest()164 ArrayMap<String, ShortcutPackage> getAllPackagesForTest() { 165 return mPackages; 166 } 167 hasPackage(@onNull String packageName)168 public boolean hasPackage(@NonNull String packageName) { 169 return mPackages.containsKey(packageName); 170 } 171 addPackage(@onNull ShortcutPackage p)172 private void addPackage(@NonNull ShortcutPackage p) { 173 p.replaceUser(this); 174 mPackages.put(p.getPackageName(), p); 175 } 176 removePackage(@onNull String packageName)177 public ShortcutPackage removePackage(@NonNull String packageName) { 178 final ShortcutPackage removed = mPackages.remove(packageName); 179 180 mService.cleanupBitmapsForPackage(mUserId, packageName); 181 182 return removed; 183 } 184 185 // We don't expose this directly to non-test code because only ShortcutUser should add to/ 186 // remove from it. 187 @VisibleForTesting getAllLaunchersForTest()188 ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchersForTest() { 189 return mLaunchers; 190 } 191 addLauncher(ShortcutLauncher launcher)192 private void addLauncher(ShortcutLauncher launcher) { 193 launcher.replaceUser(this); 194 mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(), 195 launcher.getPackageName()), launcher); 196 } 197 198 @Nullable removeLauncher( @serIdInt int packageUserId, @NonNull String packageName)199 public ShortcutLauncher removeLauncher( 200 @UserIdInt int packageUserId, @NonNull String packageName) { 201 return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName)); 202 } 203 204 @Nullable getPackageShortcutsIfExists(@onNull String packageName)205 public ShortcutPackage getPackageShortcutsIfExists(@NonNull String packageName) { 206 final ShortcutPackage ret = mPackages.get(packageName); 207 if (ret != null) { 208 ret.attemptToRestoreIfNeededAndSave(); 209 } 210 return ret; 211 } 212 213 @NonNull getPackageShortcuts(@onNull String packageName)214 public ShortcutPackage getPackageShortcuts(@NonNull String packageName) { 215 ShortcutPackage ret = getPackageShortcutsIfExists(packageName); 216 if (ret == null) { 217 ret = new ShortcutPackage(this, mUserId, packageName); 218 mPackages.put(packageName, ret); 219 } 220 return ret; 221 } 222 223 @NonNull getLauncherShortcuts(@onNull String packageName, @UserIdInt int launcherUserId)224 public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName, 225 @UserIdInt int launcherUserId) { 226 final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName); 227 ShortcutLauncher ret = mLaunchers.get(key); 228 if (ret == null) { 229 ret = new ShortcutLauncher(this, mUserId, packageName, launcherUserId); 230 mLaunchers.put(key, ret); 231 } else { 232 ret.attemptToRestoreIfNeededAndSave(); 233 } 234 return ret; 235 } 236 forAllPackages(Consumer<? super ShortcutPackage> callback)237 public void forAllPackages(Consumer<? super ShortcutPackage> callback) { 238 final int size = mPackages.size(); 239 for (int i = 0; i < size; i++) { 240 callback.accept(mPackages.valueAt(i)); 241 } 242 } 243 forAllLaunchers(Consumer<? super ShortcutLauncher> callback)244 public void forAllLaunchers(Consumer<? super ShortcutLauncher> callback) { 245 final int size = mLaunchers.size(); 246 for (int i = 0; i < size; i++) { 247 callback.accept(mLaunchers.valueAt(i)); 248 } 249 } 250 forAllPackageItems(Consumer<? super ShortcutPackageItem> callback)251 public void forAllPackageItems(Consumer<? super ShortcutPackageItem> callback) { 252 forAllLaunchers(callback); 253 forAllPackages(callback); 254 } 255 forPackageItem(@onNull String packageName, @UserIdInt int packageUserId, Consumer<ShortcutPackageItem> callback)256 public void forPackageItem(@NonNull String packageName, @UserIdInt int packageUserId, 257 Consumer<ShortcutPackageItem> callback) { 258 forAllPackageItems(spi -> { 259 if ((spi.getPackageUserId() == packageUserId) 260 && spi.getPackageName().equals(packageName)) { 261 callback.accept(spi); 262 } 263 }); 264 } 265 266 /** 267 * Must be called at any entry points on {@link ShortcutManager} APIs to make sure the 268 * information on the package is up-to-date. 269 * 270 * We use broadcasts to handle locale changes and package changes, but because broadcasts 271 * are asynchronous, there's a chance a publisher calls getXxxShortcuts() after a certain event 272 * (e.g. system locale change) but shortcut manager hasn't finished processing the broadcast. 273 * 274 * So we call this method at all entry points from publishers to make sure we update all 275 * relevant information. 276 * 277 * Similar inconsistencies can happen when the launcher fetches shortcut information, but 278 * that's a less of an issue because for the launcher we report shortcut changes with 279 * callbacks. 280 */ onCalledByPublisher(@onNull String packageName)281 public void onCalledByPublisher(@NonNull String packageName) { 282 detectLocaleChange(); 283 rescanPackageIfNeeded(packageName, /*forceRescan=*/ false); 284 } 285 getKnownLocales()286 private String getKnownLocales() { 287 if (TextUtils.isEmpty(mKnownLocales)) { 288 mKnownLocales = mService.injectGetLocaleTagsForUser(mUserId); 289 mService.scheduleSaveUser(mUserId); 290 } 291 return mKnownLocales; 292 } 293 294 /** 295 * Check to see if the system locale has changed, and if so, reset throttling 296 * and update resource strings. 297 */ detectLocaleChange()298 public void detectLocaleChange() { 299 final String currentLocales = mService.injectGetLocaleTagsForUser(mUserId); 300 if (!TextUtils.isEmpty(mKnownLocales) && mKnownLocales.equals(currentLocales)) { 301 return; 302 } 303 if (ShortcutService.DEBUG) { 304 Slog.d(TAG, "Locale changed from " + mKnownLocales + " to " + currentLocales 305 + " for user " + mUserId); 306 } 307 308 mKnownLocales = currentLocales; 309 310 forAllPackages(pkg -> { 311 pkg.resetRateLimiting(); 312 pkg.resolveResourceStrings(); 313 }); 314 315 mService.scheduleSaveUser(mUserId); 316 } 317 rescanPackageIfNeeded(@onNull String packageName, boolean forceRescan)318 public void rescanPackageIfNeeded(@NonNull String packageName, boolean forceRescan) { 319 final boolean isNewApp = !mPackages.containsKey(packageName); 320 321 final ShortcutPackage shortcutPackage = getPackageShortcuts(packageName); 322 323 if (!shortcutPackage.rescanPackageIfNeeded(isNewApp, forceRescan)) { 324 if (isNewApp) { 325 mPackages.remove(packageName); 326 } 327 } 328 } 329 attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName, @UserIdInt int packageUserId)330 public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName, 331 @UserIdInt int packageUserId) { 332 forPackageItem(packageName, packageUserId, spi -> { 333 spi.attemptToRestoreIfNeededAndSave(); 334 }); 335 } 336 saveToXml(XmlSerializer out, boolean forBackup)337 public void saveToXml(XmlSerializer out, boolean forBackup) 338 throws IOException, XmlPullParserException { 339 out.startTag(null, TAG_ROOT); 340 341 if (!forBackup) { 342 // Don't have to back them up. 343 ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALES, mKnownLocales); 344 ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME, 345 mLastAppScanTime); 346 ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_OS_FINGERPRINT, 347 mLastAppScanOsFingerprint); 348 ShortcutService.writeAttr(out, ATTR_RESTORE_SOURCE_FINGERPRINT, 349 mRestoreFromOsFingerprint); 350 351 ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher); 352 } else { 353 ShortcutService.writeAttr(out, ATTR_RESTORE_SOURCE_FINGERPRINT, 354 mService.injectBuildFingerprint()); 355 } 356 357 // Can't use forEachPackageItem due to the checked exceptions. 358 { 359 final int size = mLaunchers.size(); 360 for (int i = 0; i < size; i++) { 361 saveShortcutPackageItem(out, mLaunchers.valueAt(i), forBackup); 362 } 363 } 364 { 365 final int size = mPackages.size(); 366 for (int i = 0; i < size; i++) { 367 saveShortcutPackageItem(out, mPackages.valueAt(i), forBackup); 368 } 369 } 370 371 out.endTag(null, TAG_ROOT); 372 } 373 saveShortcutPackageItem(XmlSerializer out, ShortcutPackageItem spi, boolean forBackup)374 private void saveShortcutPackageItem(XmlSerializer out, 375 ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException { 376 if (forBackup) { 377 if (spi.getPackageUserId() != spi.getOwnerUserId()) { 378 return; // Don't save cross-user information. 379 } 380 } 381 spi.saveToXml(out, forBackup); 382 } 383 loadFromXml(ShortcutService s, XmlPullParser parser, int userId, boolean fromBackup)384 public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId, 385 boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException { 386 final ShortcutUser ret = new ShortcutUser(s, userId); 387 388 try { 389 ret.mKnownLocales = ShortcutService.parseStringAttribute(parser, 390 ATTR_KNOWN_LOCALES); 391 392 // If lastAppScanTime is in the future, that means the clock went backwards. 393 // Just scan all apps again. 394 final long lastAppScanTime = ShortcutService.parseLongAttribute(parser, 395 ATTR_LAST_APP_SCAN_TIME); 396 final long currentTime = s.injectCurrentTimeMillis(); 397 ret.mLastAppScanTime = lastAppScanTime < currentTime ? lastAppScanTime : 0; 398 ret.mLastAppScanOsFingerprint = ShortcutService.parseStringAttribute(parser, 399 ATTR_LAST_APP_SCAN_OS_FINGERPRINT); 400 ret.mRestoreFromOsFingerprint = ShortcutService.parseStringAttribute(parser, 401 ATTR_RESTORE_SOURCE_FINGERPRINT); 402 final int outerDepth = parser.getDepth(); 403 int type; 404 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 405 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 406 if (type != XmlPullParser.START_TAG) { 407 continue; 408 } 409 final int depth = parser.getDepth(); 410 final String tag = parser.getName(); 411 412 if (depth == outerDepth + 1) { 413 switch (tag) { 414 case TAG_LAUNCHER: { 415 ret.mLastKnownLauncher = ShortcutService.parseComponentNameAttribute( 416 parser, ATTR_VALUE); 417 continue; 418 } 419 case ShortcutPackage.TAG_ROOT: { 420 final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml( 421 s, ret, parser, fromBackup); 422 423 // Don't use addShortcut(), we don't need to save the icon. 424 ret.mPackages.put(shortcuts.getPackageName(), shortcuts); 425 continue; 426 } 427 428 case ShortcutLauncher.TAG_ROOT: { 429 ret.addLauncher( 430 ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup)); 431 continue; 432 } 433 } 434 } 435 ShortcutService.warnForInvalidTag(depth, tag); 436 } 437 } catch (RuntimeException e) { 438 throw new ShortcutService.InvalidFileFormatException( 439 "Unable to parse file", e); 440 } 441 return ret; 442 } 443 getLastKnownLauncher()444 public ComponentName getLastKnownLauncher() { 445 return mLastKnownLauncher; 446 } 447 setLauncher(ComponentName launcherComponent)448 public void setLauncher(ComponentName launcherComponent) { 449 setLauncher(launcherComponent, /* allowPurgeLastKnown */ false); 450 } 451 452 /** Clears the launcher information without clearing the last known one */ clearLauncher()453 public void clearLauncher() { 454 setLauncher(null); 455 } 456 457 /** 458 * Clears the launcher information *with(* clearing the last known one; we do this witl 459 * "cmd shortcut clear-default-launcher". 460 */ forceClearLauncher()461 public void forceClearLauncher() { 462 setLauncher(null, /* allowPurgeLastKnown */ true); 463 } 464 setLauncher(ComponentName launcherComponent, boolean allowPurgeLastKnown)465 private void setLauncher(ComponentName launcherComponent, boolean allowPurgeLastKnown) { 466 mCachedLauncher = launcherComponent; // Always update the in-memory cache. 467 468 if (Objects.equals(mLastKnownLauncher, launcherComponent)) { 469 return; 470 } 471 if (!allowPurgeLastKnown && launcherComponent == null) { 472 return; 473 } 474 mLastKnownLauncher = launcherComponent; 475 mService.scheduleSaveUser(mUserId); 476 } 477 getCachedLauncher()478 public ComponentName getCachedLauncher() { 479 return mCachedLauncher; 480 } 481 resetThrottling()482 public void resetThrottling() { 483 for (int i = mPackages.size() - 1; i >= 0; i--) { 484 mPackages.valueAt(i).resetThrottling(); 485 } 486 } 487 mergeRestoredFile(ShortcutUser restored)488 public void mergeRestoredFile(ShortcutUser restored) { 489 final ShortcutService s = mService; 490 // Note, a restore happens only at the end of setup wizard. At this point, no apps are 491 // installed from Play Store yet, but it's still possible that system apps have already 492 // published dynamic shortcuts, since some apps do so on BOOT_COMPLETED. 493 // When such a system app has allowbackup=true, then we go ahead and replace all existing 494 // shortcuts with the restored shortcuts. (Then we'll re-publish manifest shortcuts later 495 // in the call site.) 496 // When such a system app has allowbackup=false, then we'll keep the shortcuts that have 497 // already been published. So we selectively add restored ShortcutPackages here. 498 // 499 // The same logic applies to launchers, but since launchers shouldn't pin shortcuts 500 // without users interaction it's really not a big deal, so we just clear existing 501 // ShortcutLauncher instances in mLaunchers and add all the restored ones here. 502 503 int[] restoredLaunchers = new int[1]; 504 int[] restoredPackages = new int[1]; 505 int[] restoredShortcuts = new int[1]; 506 507 mLaunchers.clear(); 508 restored.forAllLaunchers(sl -> { 509 // If the app is already installed and allowbackup = false, then ignore the restored 510 // data. 511 if (s.isPackageInstalled(sl.getPackageName(), getUserId()) 512 && !s.shouldBackupApp(sl.getPackageName(), getUserId())) { 513 return; 514 } 515 addLauncher(sl); 516 restoredLaunchers[0]++; 517 }); 518 restored.forAllPackages(sp -> { 519 // If the app is already installed and allowbackup = false, then ignore the restored 520 // data. 521 if (s.isPackageInstalled(sp.getPackageName(), getUserId()) 522 && !s.shouldBackupApp(sp.getPackageName(), getUserId())) { 523 return; 524 } 525 526 final ShortcutPackage previous = getPackageShortcutsIfExists(sp.getPackageName()); 527 if (previous != null && previous.hasNonManifestShortcuts()) { 528 Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored." 529 + " Existing non-manifeset shortcuts will be overwritten."); 530 } 531 addPackage(sp); 532 restoredPackages[0]++; 533 restoredShortcuts[0] += sp.getShortcutCount(); 534 }); 535 // Empty the launchers and packages in restored to avoid accidentally using them. 536 restored.mLaunchers.clear(); 537 restored.mPackages.clear(); 538 539 mRestoreFromOsFingerprint = restored.mRestoreFromOsFingerprint; 540 541 Slog.i(TAG, "Restored: L=" + restoredLaunchers[0] 542 + " P=" + restoredPackages[0] 543 + " S=" + restoredShortcuts[0]); 544 } 545 dump(@onNull PrintWriter pw, @NonNull String prefix, DumpFilter filter)546 public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) { 547 if (filter.shouldDumpDetails()) { 548 pw.print(prefix); 549 pw.print("User: "); 550 pw.print(mUserId); 551 pw.print(" Known locales: "); 552 pw.print(mKnownLocales); 553 pw.print(" Last app scan: ["); 554 pw.print(mLastAppScanTime); 555 pw.print("] "); 556 pw.println(ShortcutService.formatTime(mLastAppScanTime)); 557 558 prefix += prefix + " "; 559 560 pw.print(prefix); 561 pw.print("Last app scan FP: "); 562 pw.println(mLastAppScanOsFingerprint); 563 564 pw.print(prefix); 565 pw.print("Restore from FP: "); 566 pw.print(mRestoreFromOsFingerprint); 567 pw.println(); 568 569 570 pw.print(prefix); 571 pw.print("Cached launcher: "); 572 pw.print(mCachedLauncher); 573 pw.println(); 574 575 pw.print(prefix); 576 pw.print("Last known launcher: "); 577 pw.print(mLastKnownLauncher); 578 pw.println(); 579 } 580 581 for (int i = 0; i < mLaunchers.size(); i++) { 582 ShortcutLauncher launcher = mLaunchers.valueAt(i); 583 if (filter.isPackageMatch(launcher.getPackageName())) { 584 launcher.dump(pw, prefix, filter); 585 } 586 } 587 588 for (int i = 0; i < mPackages.size(); i++) { 589 ShortcutPackage pkg = mPackages.valueAt(i); 590 if (filter.isPackageMatch(pkg.getPackageName())) { 591 pkg.dump(pw, prefix, filter); 592 } 593 } 594 595 if (filter.shouldDumpDetails()) { 596 pw.println(); 597 pw.print(prefix); 598 pw.println("Bitmap directories: "); 599 dumpDirectorySize(pw, prefix + " ", mService.getUserBitmapFilePath(mUserId)); 600 } 601 } 602 dumpDirectorySize(@onNull PrintWriter pw, @NonNull String prefix, File path)603 private void dumpDirectorySize(@NonNull PrintWriter pw, 604 @NonNull String prefix, File path) { 605 int numFiles = 0; 606 long size = 0; 607 final File[] children = path.listFiles(); 608 if (children != null) { 609 for (File child : path.listFiles()) { 610 if (child.isFile()) { 611 numFiles++; 612 size += child.length(); 613 } else if (child.isDirectory()) { 614 dumpDirectorySize(pw, prefix + " ", child); 615 } 616 } 617 } 618 pw.print(prefix); 619 pw.print("Path: "); 620 pw.print(path.getName()); 621 pw.print("/ has "); 622 pw.print(numFiles); 623 pw.print(" files, size="); 624 pw.print(size); 625 pw.print(" ("); 626 pw.print(Formatter.formatFileSize(mService.mContext, size)); 627 pw.println(")"); 628 } 629 dumpCheckin(boolean clear)630 public JSONObject dumpCheckin(boolean clear) throws JSONException { 631 final JSONObject result = new JSONObject(); 632 633 result.put(KEY_USER_ID, mUserId); 634 635 { 636 final JSONArray launchers = new JSONArray(); 637 for (int i = 0; i < mLaunchers.size(); i++) { 638 launchers.put(mLaunchers.valueAt(i).dumpCheckin(clear)); 639 } 640 result.put(KEY_LAUNCHERS, launchers); 641 } 642 643 { 644 final JSONArray packages = new JSONArray(); 645 for (int i = 0; i < mPackages.size(); i++) { 646 packages.put(mPackages.valueAt(i).dumpCheckin(clear)); 647 } 648 result.put(KEY_PACKAGES, packages); 649 } 650 651 return result; 652 } 653 logSharingShortcutStats(MetricsLogger logger)654 void logSharingShortcutStats(MetricsLogger logger) { 655 int packageWithShareTargetsCount = 0; 656 int totalSharingShortcutCount = 0; 657 for (int i = 0; i < mPackages.size(); i++) { 658 if (mPackages.valueAt(i).hasShareTargets()) { 659 packageWithShareTargetsCount++; 660 totalSharingShortcutCount += mPackages.valueAt(i).getSharingShortcutCount(); 661 } 662 } 663 664 final LogMaker logMaker = new LogMaker(MetricsEvent.ACTION_SHORTCUTS_CHANGED); 665 logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_USER_ID) 666 .setSubtype(mUserId)); 667 logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_PACKAGE_COUNT) 668 .setSubtype(packageWithShareTargetsCount)); 669 logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_SHORTCUT_COUNT) 670 .setSubtype(totalSharingShortcutCount)); 671 } 672 } 673