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