ShortcutService.java revision ac042501c816fa9a65aed005060ebdcfc0a0f3b2
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 */ 16package com.android.server.pm; 17 18import android.annotation.IntDef; 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.annotation.UserIdInt; 22import android.app.ActivityManager; 23import android.app.ActivityManagerNative; 24import android.app.AppGlobals; 25import android.app.IUidObserver; 26import android.app.usage.UsageStatsManagerInternal; 27import android.content.ComponentName; 28import android.content.Context; 29import android.content.Intent; 30import android.content.pm.ActivityInfo; 31import android.content.pm.ApplicationInfo; 32import android.content.pm.IPackageManager; 33import android.content.pm.IShortcutService; 34import android.content.pm.LauncherApps; 35import android.content.pm.LauncherApps.ShortcutQuery; 36import android.content.pm.PackageInfo; 37import android.content.pm.PackageManager; 38import android.content.pm.PackageManager.NameNotFoundException; 39import android.content.pm.PackageManagerInternal; 40import android.content.pm.ParceledListSlice; 41import android.content.pm.ResolveInfo; 42import android.content.pm.ShortcutInfo; 43import android.content.pm.ShortcutServiceInternal; 44import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; 45import android.content.res.XmlResourceParser; 46import android.graphics.Bitmap; 47import android.graphics.Bitmap.CompressFormat; 48import android.graphics.Canvas; 49import android.graphics.RectF; 50import android.graphics.drawable.Icon; 51import android.os.Binder; 52import android.os.Environment; 53import android.os.FileUtils; 54import android.os.Handler; 55import android.os.Looper; 56import android.os.ParcelFileDescriptor; 57import android.os.PersistableBundle; 58import android.os.Process; 59import android.os.RemoteException; 60import android.os.ResultReceiver; 61import android.os.SELinux; 62import android.os.ShellCommand; 63import android.os.SystemClock; 64import android.os.UserHandle; 65import android.os.UserManager; 66import android.text.TextUtils; 67import android.text.format.Time; 68import android.util.ArraySet; 69import android.util.AtomicFile; 70import android.util.KeyValueListParser; 71import android.util.Log; 72import android.util.Slog; 73import android.util.SparseArray; 74import android.util.SparseIntArray; 75import android.util.SparseLongArray; 76import android.util.TypedValue; 77import android.util.Xml; 78 79import com.android.internal.annotations.GuardedBy; 80import com.android.internal.annotations.VisibleForTesting; 81import com.android.internal.content.PackageMonitor; 82import com.android.internal.os.BackgroundThread; 83import com.android.internal.util.FastXmlSerializer; 84import com.android.internal.util.Preconditions; 85import com.android.server.LocalServices; 86import com.android.server.SystemService; 87import com.android.server.pm.ShortcutUser.PackageWithUser; 88 89import libcore.io.IoUtils; 90 91import org.xmlpull.v1.XmlPullParser; 92import org.xmlpull.v1.XmlPullParserException; 93import org.xmlpull.v1.XmlSerializer; 94 95import java.io.BufferedInputStream; 96import java.io.BufferedOutputStream; 97import java.io.ByteArrayInputStream; 98import java.io.ByteArrayOutputStream; 99import java.io.File; 100import java.io.FileDescriptor; 101import java.io.FileInputStream; 102import java.io.FileNotFoundException; 103import java.io.FileOutputStream; 104import java.io.IOException; 105import java.io.InputStream; 106import java.io.OutputStream; 107import java.io.PrintWriter; 108import java.lang.annotation.Retention; 109import java.lang.annotation.RetentionPolicy; 110import java.net.URISyntaxException; 111import java.nio.charset.StandardCharsets; 112import java.util.ArrayList; 113import java.util.Collections; 114import java.util.List; 115import java.util.concurrent.atomic.AtomicBoolean; 116import java.util.concurrent.atomic.AtomicLong; 117import java.util.function.Consumer; 118import java.util.function.Predicate; 119 120/** 121 * TODO: 122 * - HandleUnlockUser needs to be async. Wait on it in onCleanupUser. 123 * 124 * - Implement reportShortcutUsed(). 125 * 126 * - validateForXml() should be removed. 127 * 128 * - Ranks should be recalculated after each update. 129 * 130 * - When the system locale changes, update timestamps for shortcuts with string resources, 131 * and notify the launcher. Right now, it resets the throttling, but timestamps are not changed 132 * and there's no notification either. 133 * 134 * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi. 135 * 136 * - Default launcher check does take a few ms. Worth caching. 137 * 138 * - Detect when already registered instances are passed to APIs again, which might break 139 * internal bitmap handling. 140 * 141 * - Add more call stats. 142 * 143 * - Rename mMaxDynamicShortcuts, because it includes manifest shortcuts too. 144 */ 145public class ShortcutService extends IShortcutService.Stub { 146 static final String TAG = "ShortcutService"; 147 148 static final boolean DEBUG = false; // STOPSHIP if true 149 static final boolean DEBUG_LOAD = false; // STOPSHIP if true 150 static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true 151 152 @VisibleForTesting 153 static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day 154 155 @VisibleForTesting 156 static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10; 157 158 @VisibleForTesting 159 static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5; 160 161 @VisibleForTesting 162 static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; 163 164 @VisibleForTesting 165 static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48; 166 167 @VisibleForTesting 168 static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name(); 169 170 @VisibleForTesting 171 static final int DEFAULT_ICON_PERSIST_QUALITY = 100; 172 173 @VisibleForTesting 174 static final int DEFAULT_SAVE_DELAY_MS = 3000; 175 176 @VisibleForTesting 177 static final String FILENAME_BASE_STATE = "shortcut_service.xml"; 178 179 @VisibleForTesting 180 static final String DIRECTORY_PER_USER = "shortcut_service"; 181 182 @VisibleForTesting 183 static final String FILENAME_USER_PACKAGES = "shortcuts.xml"; 184 185 static final String DIRECTORY_BITMAPS = "bitmaps"; 186 187 private static final String TAG_ROOT = "root"; 188 private static final String TAG_LAST_RESET_TIME = "last_reset_time"; 189 private static final String TAG_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale_seq_no"; 190 191 private static final String ATTR_VALUE = "value"; 192 193 @VisibleForTesting 194 interface ConfigConstants { 195 /** 196 * Key name for the save delay, in milliseconds. (int) 197 */ 198 String KEY_SAVE_DELAY_MILLIS = "save_delay_ms"; 199 200 /** 201 * Key name for the throttling reset interval, in seconds. (long) 202 */ 203 String KEY_RESET_INTERVAL_SEC = "reset_interval_sec"; 204 205 /** 206 * Key name for the max number of modifying API calls per app for every interval. (int) 207 */ 208 String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval"; 209 210 /** 211 * Key name for the max icon dimensions in DP, for non-low-memory devices. 212 */ 213 String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp"; 214 215 /** 216 * Key name for the max icon dimensions in DP, for low-memory devices. 217 */ 218 String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram"; 219 220 /** 221 * Key name for the max dynamic shortcuts per app. (int) 222 */ 223 String KEY_MAX_SHORTCUTS = "max_shortcuts"; 224 225 /** 226 * Key name for icon compression quality, 0-100. 227 */ 228 String KEY_ICON_QUALITY = "icon_quality"; 229 230 /** 231 * Key name for icon compression format: "PNG", "JPEG" or "WEBP" 232 */ 233 String KEY_ICON_FORMAT = "icon_format"; 234 } 235 236 final Context mContext; 237 238 private final Object mLock = new Object(); 239 240 private final Handler mHandler; 241 242 @GuardedBy("mLock") 243 private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1); 244 245 @GuardedBy("mLock") 246 private long mRawLastResetTime; 247 248 /** 249 * User ID -> UserShortcuts 250 */ 251 @GuardedBy("mLock") 252 private final SparseArray<ShortcutUser> mUsers = new SparseArray<>(); 253 254 /** 255 * Max number of dynamic shortcuts that each application can have at a time. 256 */ 257 private int mMaxDynamicShortcuts; 258 259 /** 260 * Max number of updating API calls that each application can make during the interval. 261 */ 262 int mMaxUpdatesPerInterval; 263 264 /** 265 * Actual throttling-reset interval. By default it's a day. 266 */ 267 private long mResetInterval; 268 269 /** 270 * Icon max width/height in pixels. 271 */ 272 private int mMaxIconDimension; 273 274 private CompressFormat mIconPersistFormat; 275 private int mIconPersistQuality; 276 277 private int mSaveDelayMillis; 278 279 private final IPackageManager mIPackageManager; 280 private final PackageManagerInternal mPackageManagerInternal; 281 private final UserManager mUserManager; 282 private final UsageStatsManagerInternal mUsageStatsManagerInternal; 283 284 @GuardedBy("mLock") 285 final SparseIntArray mUidState = new SparseIntArray(); 286 287 @GuardedBy("mLock") 288 final SparseLongArray mUidLastForegroundElapsedTime = new SparseLongArray(); 289 290 @GuardedBy("mLock") 291 private List<Integer> mDirtyUserIds = new ArrayList<>(); 292 293 /** 294 * A counter that increments every time the system locale changes. We keep track of it to reset 295 * throttling counters on the first call from each package after the last locale change. 296 * 297 * We need this mechanism because we can't do much in the locale change callback, which is 298 * {@link ShortcutServiceInternal#onSystemLocaleChangedNoLock()}. 299 */ 300 private final AtomicLong mLocaleChangeSequenceNumber = new AtomicLong(); 301 302 private final AtomicBoolean mBootCompleted = new AtomicBoolean(); 303 304 private static final int PACKAGE_MATCH_FLAGS = 305 PackageManager.MATCH_DIRECT_BOOT_AWARE 306 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 307 | PackageManager.MATCH_UNINSTALLED_PACKAGES; 308 309 // Stats 310 @VisibleForTesting 311 interface Stats { 312 int GET_DEFAULT_HOME = 0; 313 int GET_PACKAGE_INFO = 1; 314 int GET_PACKAGE_INFO_WITH_SIG = 2; 315 int GET_APPLICATION_INFO = 3; 316 int LAUNCHER_PERMISSION_CHECK = 4; 317 int CLEANUP_DANGLING_BITMAPS = 5; 318 int GET_ACTIVITIES_WITH_METADATA = 6; 319 int GET_INSTALLED_APPLICATIONS = 7; 320 int CHECK_PACKAGE_CHANGES = 8; 321 322 int COUNT = CHECK_PACKAGE_CHANGES + 1; 323 } 324 325 final Object mStatLock = new Object(); 326 327 @GuardedBy("mStatLock") 328 private final int[] mCountStats = new int[Stats.COUNT]; 329 330 @GuardedBy("mStatLock") 331 private final long[] mDurationStats = new long[Stats.COUNT]; 332 333 private static final int PROCESS_STATE_FOREGROUND_THRESHOLD = 334 ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; 335 336 static final int OPERATION_SET = 0; 337 static final int OPERATION_ADD = 1; 338 static final int OPERATION_UPDATE = 2; 339 340 /** @hide */ 341 @IntDef(value = { 342 OPERATION_SET, 343 OPERATION_ADD, 344 OPERATION_UPDATE 345 }) 346 @Retention(RetentionPolicy.SOURCE) 347 @interface ShortcutOperation {} 348 349 public ShortcutService(Context context) { 350 this(context, BackgroundThread.get().getLooper()); 351 } 352 353 @VisibleForTesting 354 ShortcutService(Context context, Looper looper) { 355 mContext = Preconditions.checkNotNull(context); 356 LocalServices.addService(ShortcutServiceInternal.class, new LocalService()); 357 mHandler = new Handler(looper); 358 mIPackageManager = AppGlobals.getPackageManager(); 359 mPackageManagerInternal = Preconditions.checkNotNull( 360 LocalServices.getService(PackageManagerInternal.class)); 361 mUserManager = Preconditions.checkNotNull(context.getSystemService(UserManager.class)); 362 mUsageStatsManagerInternal = Preconditions.checkNotNull( 363 LocalServices.getService(UsageStatsManagerInternal.class)); 364 365 mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false); 366 367 injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE 368 | ActivityManager.UID_OBSERVER_GONE); 369 } 370 371 void logDurationStat(int statId, long start) { 372 synchronized (mStatLock) { 373 mCountStats[statId]++; 374 mDurationStats[statId] += (injectElapsedRealtime() - start); 375 } 376 } 377 378 public long getLocaleChangeSequenceNumber() { 379 return mLocaleChangeSequenceNumber.get(); 380 } 381 382 final private IUidObserver mUidObserver = new IUidObserver.Stub() { 383 @Override public void onUidStateChanged(int uid, int procState) throws RemoteException { 384 handleOnUidStateChanged(uid, procState); 385 } 386 387 @Override public void onUidGone(int uid) throws RemoteException { 388 handleOnUidStateChanged(uid, ActivityManager.MAX_PROCESS_STATE); 389 } 390 391 @Override public void onUidActive(int uid) throws RemoteException { 392 } 393 394 @Override public void onUidIdle(int uid) throws RemoteException { 395 } 396 }; 397 398 void handleOnUidStateChanged(int uid, int procState) { 399 if (DEBUG_PROCSTATE) { 400 Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState); 401 } 402 synchronized (mLock) { 403 mUidState.put(uid, procState); 404 405 // We need to keep track of last time an app comes to foreground. 406 // See ShortcutPackage.getApiCallCount() for how it's used. 407 // It doesn't have to be persisted, but it needs to be the elapsed time. 408 if (isProcessStateForeground(procState)) { 409 mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime()); 410 } 411 } 412 } 413 414 private boolean isProcessStateForeground(int processState) { 415 return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD; 416 } 417 418 boolean isUidForegroundLocked(int uid) { 419 if (uid == Process.SYSTEM_UID) { 420 // IUidObserver doesn't report the state of SYSTEM, but it always has bound services, 421 // so it's foreground anyway. 422 return true; 423 } 424 return isProcessStateForeground(mUidState.get(uid, ActivityManager.MAX_PROCESS_STATE)); 425 } 426 427 long getUidLastForegroundElapsedTimeLocked(int uid) { 428 return mUidLastForegroundElapsedTime.get(uid); 429 } 430 431 /** 432 * System service lifecycle. 433 */ 434 public static final class Lifecycle extends SystemService { 435 final ShortcutService mService; 436 437 public Lifecycle(Context context) { 438 super(context); 439 mService = new ShortcutService(context); 440 } 441 442 @Override 443 public void onStart() { 444 publishBinderService(Context.SHORTCUT_SERVICE, mService); 445 } 446 447 @Override 448 public void onBootPhase(int phase) { 449 mService.onBootPhase(phase); 450 } 451 452 @Override 453 public void onCleanupUser(int userHandle) { 454 mService.handleCleanupUser(userHandle); 455 } 456 457 @Override 458 public void onUnlockUser(int userId) { 459 mService.handleUnlockUser(userId); 460 } 461 } 462 463 /** lifecycle event */ 464 void onBootPhase(int phase) { 465 if (DEBUG) { 466 Slog.d(TAG, "onBootPhase: " + phase); 467 } 468 switch (phase) { 469 case SystemService.PHASE_LOCK_SETTINGS_READY: 470 initialize(); 471 break; 472 case SystemService.PHASE_BOOT_COMPLETED: 473 mBootCompleted.set(true); 474 break; 475 } 476 } 477 478 /** lifecycle event */ 479 void handleUnlockUser(int userId) { 480 if (DEBUG) { 481 Slog.d(TAG, "handleUnlockUser: user=" + userId); 482 } 483 synchronized (mLock) { 484 // Preload 485 getUserShortcutsLocked(userId); 486 487 checkPackageChanges(userId); 488 } 489 } 490 491 /** lifecycle event */ 492 void handleCleanupUser(int userId) { 493 synchronized (mLock) { 494 unloadUserLocked(userId); 495 } 496 } 497 498 private void unloadUserLocked(int userId) { 499 if (DEBUG) { 500 Slog.d(TAG, "unloadUserLocked: user=" + userId); 501 } 502 // Save all dirty information. 503 saveDirtyInfo(); 504 505 // Unload 506 mUsers.delete(userId); 507 } 508 509 /** Return the base state file name */ 510 private AtomicFile getBaseStateFile() { 511 final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE); 512 path.mkdirs(); 513 return new AtomicFile(path); 514 } 515 516 /** 517 * Init the instance. (load the state file, etc) 518 */ 519 private void initialize() { 520 synchronized (mLock) { 521 loadConfigurationLocked(); 522 loadBaseStateLocked(); 523 } 524 } 525 526 /** 527 * Load the configuration from Settings. 528 */ 529 private void loadConfigurationLocked() { 530 updateConfigurationLocked(injectShortcutManagerConstants()); 531 } 532 533 /** 534 * Load the configuration from Settings. 535 */ 536 @VisibleForTesting 537 boolean updateConfigurationLocked(String config) { 538 boolean result = true; 539 540 final KeyValueListParser parser = new KeyValueListParser(','); 541 try { 542 parser.setString(config); 543 } catch (IllegalArgumentException e) { 544 // Failed to parse the settings string, log this and move on 545 // with defaults. 546 Slog.e(TAG, "Bad shortcut manager settings", e); 547 result = false; 548 } 549 550 mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS, 551 DEFAULT_SAVE_DELAY_MS)); 552 553 mResetInterval = Math.max(1, parser.getLong( 554 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC) 555 * 1000L); 556 557 mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong( 558 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL)); 559 560 mMaxDynamicShortcuts = Math.max(0, (int) parser.getLong( 561 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP)); 562 563 final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() 564 ? (int) parser.getLong( 565 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, 566 DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP) 567 : (int) parser.getLong( 568 ConfigConstants.KEY_MAX_ICON_DIMENSION_DP, 569 DEFAULT_MAX_ICON_DIMENSION_DP)); 570 571 mMaxIconDimension = injectDipToPixel(iconDimensionDp); 572 573 mIconPersistFormat = CompressFormat.valueOf( 574 parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT)); 575 576 mIconPersistQuality = (int) parser.getLong( 577 ConfigConstants.KEY_ICON_QUALITY, 578 DEFAULT_ICON_PERSIST_QUALITY); 579 580 return result; 581 } 582 583 @VisibleForTesting 584 String injectShortcutManagerConstants() { 585 return android.provider.Settings.Global.getString( 586 mContext.getContentResolver(), 587 android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS); 588 } 589 590 @VisibleForTesting 591 int injectDipToPixel(int dip) { 592 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, 593 mContext.getResources().getDisplayMetrics()); 594 } 595 596 // === Persisting === 597 598 @Nullable 599 static String parseStringAttribute(XmlPullParser parser, String attribute) { 600 return parser.getAttributeValue(null, attribute); 601 } 602 603 static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) { 604 return parseLongAttribute(parser, attribute) == 1; 605 } 606 607 static int parseIntAttribute(XmlPullParser parser, String attribute) { 608 return (int) parseLongAttribute(parser, attribute); 609 } 610 611 static int parseIntAttribute(XmlPullParser parser, String attribute, int def) { 612 return (int) parseLongAttribute(parser, attribute, def); 613 } 614 615 static long parseLongAttribute(XmlPullParser parser, String attribute) { 616 return parseLongAttribute(parser, attribute, 0); 617 } 618 619 static long parseLongAttribute(XmlPullParser parser, String attribute, long def) { 620 final String value = parseStringAttribute(parser, attribute); 621 if (TextUtils.isEmpty(value)) { 622 return def; 623 } 624 try { 625 return Long.parseLong(value); 626 } catch (NumberFormatException e) { 627 Slog.e(TAG, "Error parsing long " + value); 628 return def; 629 } 630 } 631 632 @Nullable 633 static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) { 634 final String value = parseStringAttribute(parser, attribute); 635 if (TextUtils.isEmpty(value)) { 636 return null; 637 } 638 return ComponentName.unflattenFromString(value); 639 } 640 641 @Nullable 642 static Intent parseIntentAttribute(XmlPullParser parser, String attribute) { 643 final String value = parseStringAttribute(parser, attribute); 644 if (TextUtils.isEmpty(value)) { 645 return null; 646 } 647 try { 648 return Intent.parseUri(value, /* flags =*/ 0); 649 } catch (URISyntaxException e) { 650 Slog.e(TAG, "Error parsing intent", e); 651 return null; 652 } 653 } 654 655 static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException { 656 if (TextUtils.isEmpty(value)) return; 657 658 out.startTag(null, tag); 659 out.attribute(null, ATTR_VALUE, value); 660 out.endTag(null, tag); 661 } 662 663 static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException { 664 writeTagValue(out, tag, Long.toString(value)); 665 } 666 667 static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException { 668 if (name == null) return; 669 writeTagValue(out, tag, name.flattenToString()); 670 } 671 672 static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle) 673 throws IOException, XmlPullParserException { 674 if (bundle == null) return; 675 676 out.startTag(null, tag); 677 bundle.saveToXml(out); 678 out.endTag(null, tag); 679 } 680 681 static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException { 682 if (TextUtils.isEmpty(value)) return; 683 684 out.attribute(null, name, value.toString()); 685 } 686 687 static void writeAttr(XmlSerializer out, String name, long value) throws IOException { 688 writeAttr(out, name, String.valueOf(value)); 689 } 690 691 static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException { 692 if (value) { 693 writeAttr(out, name, "1"); 694 } 695 } 696 697 static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException { 698 if (comp == null) return; 699 writeAttr(out, name, comp.flattenToString()); 700 } 701 702 static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException { 703 if (intent == null) return; 704 705 writeAttr(out, name, intent.toUri(/* flags =*/ 0)); 706 } 707 708 @VisibleForTesting 709 void saveBaseStateLocked() { 710 final AtomicFile file = getBaseStateFile(); 711 if (DEBUG) { 712 Slog.d(TAG, "Saving to " + file.getBaseFile()); 713 } 714 715 FileOutputStream outs = null; 716 try { 717 outs = file.startWrite(); 718 719 // Write to XML 720 XmlSerializer out = new FastXmlSerializer(); 721 out.setOutput(outs, StandardCharsets.UTF_8.name()); 722 out.startDocument(null, true); 723 out.startTag(null, TAG_ROOT); 724 725 // Body. 726 writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime); 727 writeTagValue(out, TAG_LOCALE_CHANGE_SEQUENCE_NUMBER, 728 mLocaleChangeSequenceNumber.get()); 729 730 // Epilogue. 731 out.endTag(null, TAG_ROOT); 732 out.endDocument(); 733 734 // Close. 735 file.finishWrite(outs); 736 } catch (IOException e) { 737 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 738 file.failWrite(outs); 739 } 740 } 741 742 private void loadBaseStateLocked() { 743 mRawLastResetTime = 0; 744 745 final AtomicFile file = getBaseStateFile(); 746 if (DEBUG) { 747 Slog.d(TAG, "Loading from " + file.getBaseFile()); 748 } 749 try (FileInputStream in = file.openRead()) { 750 XmlPullParser parser = Xml.newPullParser(); 751 parser.setInput(in, StandardCharsets.UTF_8.name()); 752 753 int type; 754 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 755 if (type != XmlPullParser.START_TAG) { 756 continue; 757 } 758 final int depth = parser.getDepth(); 759 // Check the root tag 760 final String tag = parser.getName(); 761 if (depth == 1) { 762 if (!TAG_ROOT.equals(tag)) { 763 Slog.e(TAG, "Invalid root tag: " + tag); 764 return; 765 } 766 continue; 767 } 768 // Assume depth == 2 769 switch (tag) { 770 case TAG_LAST_RESET_TIME: 771 mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE); 772 break; 773 case TAG_LOCALE_CHANGE_SEQUENCE_NUMBER: 774 mLocaleChangeSequenceNumber.set(parseLongAttribute(parser, ATTR_VALUE)); 775 break; 776 default: 777 Slog.e(TAG, "Invalid tag: " + tag); 778 break; 779 } 780 } 781 } catch (FileNotFoundException e) { 782 // Use the default 783 } catch (IOException|XmlPullParserException e) { 784 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); 785 786 mRawLastResetTime = 0; 787 } 788 // Adjust the last reset time. 789 getLastResetTimeLocked(); 790 } 791 792 private void saveUserLocked(@UserIdInt int userId) { 793 final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); 794 if (DEBUG) { 795 Slog.d(TAG, "Saving to " + path); 796 } 797 path.mkdirs(); 798 final AtomicFile file = new AtomicFile(path); 799 FileOutputStream os = null; 800 try { 801 os = file.startWrite(); 802 803 saveUserInternalLocked(userId, os, /* forBackup= */ false); 804 805 file.finishWrite(os); 806 } catch (XmlPullParserException|IOException e) { 807 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); 808 file.failWrite(os); 809 } 810 } 811 812 private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, 813 boolean forBackup) throws IOException, XmlPullParserException { 814 815 final BufferedOutputStream bos = new BufferedOutputStream(os); 816 817 // Write to XML 818 XmlSerializer out = new FastXmlSerializer(); 819 out.setOutput(bos, StandardCharsets.UTF_8.name()); 820 out.startDocument(null, true); 821 822 getUserShortcutsLocked(userId).saveToXml(out, forBackup); 823 824 out.endDocument(); 825 826 bos.flush(); 827 os.flush(); 828 } 829 830 static IOException throwForInvalidTag(int depth, String tag) throws IOException { 831 throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth)); 832 } 833 834 static void warnForInvalidTag(int depth, String tag) throws IOException { 835 Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth)); 836 } 837 838 @Nullable 839 private ShortcutUser loadUserLocked(@UserIdInt int userId) { 840 final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); 841 if (DEBUG) { 842 Slog.d(TAG, "Loading from " + path); 843 } 844 final AtomicFile file = new AtomicFile(path); 845 846 final FileInputStream in; 847 try { 848 in = file.openRead(); 849 } catch (FileNotFoundException e) { 850 if (DEBUG) { 851 Slog.d(TAG, "Not found " + path); 852 } 853 return null; 854 } 855 try { 856 final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false); 857 cleanupDanglingBitmapDirectoriesLocked(userId, ret); 858 return ret; 859 } catch (IOException|XmlPullParserException e) { 860 Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); 861 return null; 862 } finally { 863 IoUtils.closeQuietly(in); 864 } 865 } 866 867 private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, 868 boolean fromBackup) throws XmlPullParserException, IOException { 869 870 final BufferedInputStream bis = new BufferedInputStream(is); 871 872 ShortcutUser ret = null; 873 XmlPullParser parser = Xml.newPullParser(); 874 parser.setInput(bis, StandardCharsets.UTF_8.name()); 875 876 int type; 877 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 878 if (type != XmlPullParser.START_TAG) { 879 continue; 880 } 881 final int depth = parser.getDepth(); 882 883 final String tag = parser.getName(); 884 if (DEBUG_LOAD) { 885 Slog.d(TAG, String.format("depth=%d type=%d name=%s", 886 depth, type, tag)); 887 } 888 if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) { 889 ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup); 890 continue; 891 } 892 throwForInvalidTag(depth, tag); 893 } 894 return ret; 895 } 896 897 private void scheduleSaveBaseState() { 898 scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state. 899 } 900 901 void scheduleSaveUser(@UserIdInt int userId) { 902 scheduleSaveInner(userId); 903 } 904 905 // In order to re-schedule, we need to reuse the same instance, so keep it in final. 906 private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo; 907 908 private void scheduleSaveInner(@UserIdInt int userId) { 909 if (DEBUG) { 910 Slog.d(TAG, "Scheduling to save for " + userId); 911 } 912 synchronized (mLock) { 913 if (!mDirtyUserIds.contains(userId)) { 914 mDirtyUserIds.add(userId); 915 } 916 } 917 // If already scheduled, remove that and re-schedule in N seconds. 918 mHandler.removeCallbacks(mSaveDirtyInfoRunner); 919 mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis); 920 } 921 922 @VisibleForTesting 923 void saveDirtyInfo() { 924 if (DEBUG) { 925 Slog.d(TAG, "saveDirtyInfo"); 926 } 927 synchronized (mLock) { 928 for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) { 929 final int userId = mDirtyUserIds.get(i); 930 if (userId == UserHandle.USER_NULL) { // USER_NULL for base state. 931 saveBaseStateLocked(); 932 } else { 933 saveUserLocked(userId); 934 } 935 } 936 mDirtyUserIds.clear(); 937 } 938 } 939 940 /** Return the last reset time. */ 941 long getLastResetTimeLocked() { 942 updateTimesLocked(); 943 return mRawLastResetTime; 944 } 945 946 /** Return the next reset time. */ 947 long getNextResetTimeLocked() { 948 updateTimesLocked(); 949 return mRawLastResetTime + mResetInterval; 950 } 951 952 static boolean isClockValid(long time) { 953 return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT 954 } 955 956 /** 957 * Update the last reset time. 958 */ 959 private void updateTimesLocked() { 960 961 final long now = injectCurrentTimeMillis(); 962 963 final long prevLastResetTime = mRawLastResetTime; 964 965 if (mRawLastResetTime == 0) { // first launch. 966 // TODO Randomize?? 967 mRawLastResetTime = now; 968 } else if (now < mRawLastResetTime) { 969 // Clock rewound. 970 if (isClockValid(now)) { 971 Slog.w(TAG, "Clock rewound"); 972 // TODO Randomize?? 973 mRawLastResetTime = now; 974 } 975 } else { 976 if ((mRawLastResetTime + mResetInterval) <= now) { 977 final long offset = mRawLastResetTime % mResetInterval; 978 mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset; 979 } 980 } 981 if (prevLastResetTime != mRawLastResetTime) { 982 scheduleSaveBaseState(); 983 } 984 } 985 986 @GuardedBy("mLock") 987 @NonNull 988 private boolean isUserLoadedLocked(@UserIdInt int userId) { 989 return mUsers.get(userId) != null; 990 } 991 992 /** Return the per-user state. */ 993 @GuardedBy("mLock") 994 @NonNull 995 ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) { 996 ShortcutUser userPackages = mUsers.get(userId); 997 if (userPackages == null) { 998 userPackages = loadUserLocked(userId); 999 if (userPackages == null) { 1000 userPackages = new ShortcutUser(this, userId); 1001 } 1002 mUsers.put(userId, userPackages); 1003 } 1004 return userPackages; 1005 } 1006 1007 void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) { 1008 for (int i = mUsers.size() - 1; i >= 0; i--) { 1009 c.accept(mUsers.valueAt(i)); 1010 } 1011 } 1012 1013 /** Return the per-user per-package state. */ 1014 @GuardedBy("mLock") 1015 @NonNull 1016 ShortcutPackage getPackageShortcutsLocked( 1017 @NonNull String packageName, @UserIdInt int userId) { 1018 return getUserShortcutsLocked(userId).getPackageShortcuts(packageName); 1019 } 1020 1021 @GuardedBy("mLock") 1022 @NonNull 1023 ShortcutLauncher getLauncherShortcutsLocked( 1024 @NonNull String packageName, @UserIdInt int ownerUserId, 1025 @UserIdInt int launcherUserId) { 1026 return getUserShortcutsLocked(ownerUserId) 1027 .getLauncherShortcuts(packageName, launcherUserId); 1028 } 1029 1030 // === Caller validation === 1031 1032 void removeIcon(@UserIdInt int userId, ShortcutInfo shortcut) { 1033 if (shortcut.getBitmapPath() != null) { 1034 if (DEBUG) { 1035 Slog.d(TAG, "Removing " + shortcut.getBitmapPath()); 1036 } 1037 new File(shortcut.getBitmapPath()).delete(); 1038 1039 shortcut.setBitmapPath(null); 1040 shortcut.setIconResourceId(0); 1041 shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES); 1042 } 1043 } 1044 1045 public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) { 1046 final File packagePath = new File(getUserBitmapFilePath(userId), packageName); 1047 if (!packagePath.isDirectory()) { 1048 return; 1049 } 1050 if (!(FileUtils.deleteContents(packagePath) && packagePath.delete())) { 1051 Slog.w(TAG, "Unable to remove directory " + packagePath); 1052 } 1053 } 1054 1055 private void cleanupDanglingBitmapDirectoriesLocked( 1056 @UserIdInt int userId, @NonNull ShortcutUser user) { 1057 if (DEBUG) { 1058 Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId); 1059 } 1060 final long start = injectElapsedRealtime(); 1061 1062 final File bitmapDir = getUserBitmapFilePath(userId); 1063 final File[] children = bitmapDir.listFiles(); 1064 if (children == null) { 1065 return; 1066 } 1067 for (File child : children) { 1068 if (!child.isDirectory()) { 1069 continue; 1070 } 1071 final String packageName = child.getName(); 1072 if (DEBUG) { 1073 Slog.d(TAG, "cleanupDanglingBitmaps: Found directory=" + packageName); 1074 } 1075 if (!user.hasPackage(packageName)) { 1076 if (DEBUG) { 1077 Slog.d(TAG, "Removing dangling bitmap directory: " + packageName); 1078 } 1079 cleanupBitmapsForPackage(userId, packageName); 1080 } else { 1081 cleanupDanglingBitmapFilesLocked(userId, user, packageName, child); 1082 } 1083 } 1084 logDurationStat(Stats.CLEANUP_DANGLING_BITMAPS, start); 1085 } 1086 1087 private void cleanupDanglingBitmapFilesLocked(@UserIdInt int userId, @NonNull ShortcutUser user, 1088 @NonNull String packageName, @NonNull File path) { 1089 final ArraySet<String> usedFiles = 1090 user.getPackageShortcuts(packageName).getUsedBitmapFiles(); 1091 1092 for (File child : path.listFiles()) { 1093 if (!child.isFile()) { 1094 continue; 1095 } 1096 final String name = child.getName(); 1097 if (!usedFiles.contains(name)) { 1098 if (DEBUG) { 1099 Slog.d(TAG, "Removing dangling bitmap file: " + child.getAbsolutePath()); 1100 } 1101 child.delete(); 1102 } 1103 } 1104 } 1105 1106 @VisibleForTesting 1107 static class FileOutputStreamWithPath extends FileOutputStream { 1108 private final File mFile; 1109 1110 public FileOutputStreamWithPath(File file) throws FileNotFoundException { 1111 super(file); 1112 mFile = file; 1113 } 1114 1115 public File getFile() { 1116 return mFile; 1117 } 1118 } 1119 1120 /** 1121 * Build the cached bitmap filename for a shortcut icon. 1122 * 1123 * The filename will be based on the ID, except certain characters will be escaped. 1124 */ 1125 @VisibleForTesting 1126 FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut) 1127 throws IOException { 1128 final File packagePath = new File(getUserBitmapFilePath(userId), 1129 shortcut.getPackage()); 1130 if (!packagePath.isDirectory()) { 1131 packagePath.mkdirs(); 1132 if (!packagePath.isDirectory()) { 1133 throw new IOException("Unable to create directory " + packagePath); 1134 } 1135 SELinux.restorecon(packagePath); 1136 } 1137 1138 final String baseName = String.valueOf(injectCurrentTimeMillis()); 1139 for (int suffix = 0;; suffix++) { 1140 final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png"; 1141 final File file = new File(packagePath, filename); 1142 if (!file.exists()) { 1143 if (DEBUG) { 1144 Slog.d(TAG, "Saving icon to " + file.getAbsolutePath()); 1145 } 1146 return new FileOutputStreamWithPath(file); 1147 } 1148 } 1149 } 1150 1151 void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) { 1152 if (shortcut.hasIconFile() || shortcut.hasIconResource()) { 1153 return; 1154 } 1155 1156 final long token = injectClearCallingIdentity(); 1157 try { 1158 // Clear icon info on the shortcut. 1159 shortcut.setIconResourceId(0); 1160 shortcut.setBitmapPath(null); 1161 1162 final Icon icon = shortcut.getIcon(); 1163 if (icon == null) { 1164 return; // has no icon 1165 } 1166 1167 Bitmap bitmap; 1168 try { 1169 switch (icon.getType()) { 1170 case Icon.TYPE_RESOURCE: { 1171 injectValidateIconResPackage(shortcut, icon); 1172 1173 shortcut.setIconResourceId(icon.getResId()); 1174 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES); 1175 return; 1176 } 1177 case Icon.TYPE_BITMAP: { 1178 bitmap = icon.getBitmap(); // Don't recycle in this case. 1179 break; 1180 } 1181 default: 1182 // This shouldn't happen because we've already validated the icon, but 1183 // just in case. 1184 throw ShortcutInfo.getInvalidIconException(); 1185 } 1186 if (bitmap == null) { 1187 Slog.e(TAG, "Null bitmap detected"); 1188 return; 1189 } 1190 // Shrink and write to the file. 1191 File path = null; 1192 try { 1193 final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut); 1194 try { 1195 path = out.getFile(); 1196 1197 Bitmap shrunk = shrinkBitmap(bitmap, mMaxIconDimension); 1198 try { 1199 shrunk.compress(mIconPersistFormat, mIconPersistQuality, out); 1200 } finally { 1201 if (bitmap != shrunk) { 1202 shrunk.recycle(); 1203 } 1204 } 1205 1206 shortcut.setBitmapPath(out.getFile().getAbsolutePath()); 1207 shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE); 1208 } finally { 1209 IoUtils.closeQuietly(out); 1210 } 1211 } catch (IOException|RuntimeException e) { 1212 // STOPSHIP Change wtf to e 1213 Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e); 1214 if (path != null && path.exists()) { 1215 path.delete(); 1216 } 1217 } 1218 } finally { 1219 // Once saved, we won't use the original icon information, so null it out. 1220 shortcut.clearIcon(); 1221 } 1222 } finally { 1223 injectRestoreCallingIdentity(token); 1224 } 1225 } 1226 1227 // Unfortunately we can't do this check in unit tests because we fake creator package names, 1228 // so override in unit tests. 1229 // TODO CTS this case. 1230 void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) { 1231 if (!shortcut.getPackage().equals(icon.getResPackage())) { 1232 throw new IllegalArgumentException( 1233 "Icon resource must reside in shortcut owner package"); 1234 } 1235 } 1236 1237 @VisibleForTesting 1238 static Bitmap shrinkBitmap(Bitmap in, int maxSize) { 1239 // Original width/height. 1240 final int ow = in.getWidth(); 1241 final int oh = in.getHeight(); 1242 if ((ow <= maxSize) && (oh <= maxSize)) { 1243 if (DEBUG) { 1244 Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh)); 1245 } 1246 return in; 1247 } 1248 final int longerDimension = Math.max(ow, oh); 1249 1250 // New width and height. 1251 final int nw = ow * maxSize / longerDimension; 1252 final int nh = oh * maxSize / longerDimension; 1253 if (DEBUG) { 1254 Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d", 1255 ow, oh, nw, nh)); 1256 } 1257 1258 final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888); 1259 final Canvas c = new Canvas(scaledBitmap); 1260 1261 final RectF dst = new RectF(0, 0, nw, nh); 1262 1263 c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null); 1264 1265 return scaledBitmap; 1266 } 1267 1268 // === Caller validation === 1269 1270 private boolean isCallerSystem() { 1271 final int callingUid = injectBinderCallingUid(); 1272 return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID); 1273 } 1274 1275 private boolean isCallerShell() { 1276 final int callingUid = injectBinderCallingUid(); 1277 return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; 1278 } 1279 1280 private void enforceSystemOrShell() { 1281 Preconditions.checkState(isCallerSystem() || isCallerShell(), 1282 "Caller must be system or shell"); 1283 } 1284 1285 private void enforceShell() { 1286 Preconditions.checkState(isCallerShell(), "Caller must be shell"); 1287 } 1288 1289 private void enforceSystem() { 1290 Preconditions.checkState(isCallerSystem(), "Caller must be system"); 1291 } 1292 1293 private void enforceResetThrottlingPermission() { 1294 if (isCallerSystem()) { 1295 return; 1296 } 1297 injectEnforceCallingPermission( 1298 android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null); 1299 } 1300 1301 /** 1302 * Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse 1303 * mockito. So instead we extracted it here and override it in the tests. 1304 */ 1305 @VisibleForTesting 1306 void injectEnforceCallingPermission( 1307 @NonNull String permission, @Nullable String message) { 1308 mContext.enforceCallingPermission(permission, message); 1309 } 1310 1311 private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) { 1312 Preconditions.checkStringNotEmpty(packageName, "packageName"); 1313 1314 if (isCallerSystem()) { 1315 return; // no check 1316 } 1317 1318 final int callingUid = injectBinderCallingUid(); 1319 1320 // Otherwise, make sure the arguments are valid. 1321 if (UserHandle.getUserId(callingUid) != userId) { 1322 throw new SecurityException("Invalid user-ID"); 1323 } 1324 if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) { 1325 return; // Caller is valid. 1326 } 1327 throw new SecurityException("Calling package name mismatch"); 1328 } 1329 1330 void postToHandler(Runnable r) { 1331 mHandler.post(r); 1332 } 1333 1334 /** 1335 * @throws IllegalArgumentException if {@code numShortcuts} is bigger than 1336 * {@link #getMaxActivityShortcuts()}. 1337 */ 1338 void enforceMaxActivityShortcuts(int numShortcuts) { 1339 if (numShortcuts > mMaxDynamicShortcuts) { 1340 throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded"); 1341 } 1342 } 1343 1344 /** 1345 * Return the max number of dynamic + manifest shortcuts for each launcher icon. 1346 */ 1347 int getMaxActivityShortcuts() { 1348 return mMaxDynamicShortcuts; 1349 } 1350 1351 /** 1352 * - Sends a notification to LauncherApps 1353 * - Write to file 1354 */ 1355 void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) { 1356 if (DEBUG) { 1357 Slog.d(TAG, String.format( 1358 "Shortcut changes: package=%s, user=%d", packageName, userId)); 1359 } 1360 notifyListeners(packageName, userId); 1361 scheduleSaveUser(userId); 1362 } 1363 1364 private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) { 1365 final long token = injectClearCallingIdentity(); 1366 try { 1367 if (!mUserManager.isUserRunning(userId)) { 1368 return; 1369 } 1370 } finally { 1371 injectRestoreCallingIdentity(token); 1372 } 1373 postToHandler(() -> { 1374 final ArrayList<ShortcutChangeListener> copy; 1375 synchronized (mLock) { 1376 copy = new ArrayList<>(mListeners); 1377 } 1378 // Note onShortcutChanged() needs to be called with the system service permissions. 1379 for (int i = copy.size() - 1; i >= 0; i--) { 1380 copy.get(i).onShortcutChanged(packageName, userId); 1381 } 1382 }); 1383 } 1384 1385 /** 1386 * Clean up / validate an incoming shortcut. 1387 * - Make sure all mandatory fields are set. 1388 * - Make sure the intent's extras are persistable, and them to set 1389 * {@link ShortcutInfo#mIntentPersistableExtras}. Also clear its extras. 1390 * - Clear flags. 1391 * 1392 * TODO Detailed unit tests 1393 */ 1394 private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { 1395 Preconditions.checkNotNull(shortcut, "Null shortcut detected"); 1396 if (shortcut.getActivity() != null) { 1397 Preconditions.checkState( 1398 shortcut.getPackage().equals(shortcut.getActivity().getPackageName()), 1399 "Activity package name mismatch"); 1400 } 1401 1402 if (!forUpdate) { 1403 shortcut.enforceMandatoryFields(); 1404 } 1405 if (shortcut.getIcon() != null) { 1406 ShortcutInfo.validateIcon(shortcut.getIcon()); 1407 } 1408 1409 validateForXml(shortcut.getId()); 1410 validateForXml(shortcut.getTitle()); 1411 validatePersistableBundleForXml(shortcut.getIntentPersistableExtras()); 1412 validatePersistableBundleForXml(shortcut.getExtras()); 1413 1414 shortcut.replaceFlags(0); 1415 } 1416 1417 // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those 1418 // characters. 1419 1420 private static void validatePersistableBundleForXml(PersistableBundle b) { 1421 if (b == null || b.size() == 0) { 1422 return; 1423 } 1424 for (String key : b.keySet()) { 1425 validateForXml(key); 1426 final Object value = b.get(key); 1427 if (value == null) { 1428 continue; 1429 } else if (value instanceof String) { 1430 validateForXml((String) value); 1431 } else if (value instanceof String[]) { 1432 for (String v : (String[]) value) { 1433 validateForXml(v); 1434 } 1435 } else if (value instanceof PersistableBundle) { 1436 validatePersistableBundleForXml((PersistableBundle) value); 1437 } 1438 } 1439 } 1440 1441 private static void validateForXml(CharSequence s) { 1442 if (TextUtils.isEmpty(s)) { 1443 return; 1444 } 1445 for (int i = s.length() - 1; i >= 0; i--) { 1446 if (!isAllowedInXml(s.charAt(i))) { 1447 throw new IllegalArgumentException("Unsupported character detected in: " + s); 1448 } 1449 } 1450 } 1451 1452 private static boolean isAllowedInXml(char c) { 1453 return (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); 1454 } 1455 1456 // === APIs === 1457 1458 @Override 1459 public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1460 @UserIdInt int userId) { 1461 verifyCaller(packageName, userId); 1462 1463 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1464 final int size = newShortcuts.size(); 1465 1466 synchronized (mLock) { 1467 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1468 1469 ps.ensureImmutableShortcutsNotIncluded(newShortcuts); 1470 1471 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET); 1472 1473 // Throttling. 1474 if (!ps.tryApiCall()) { 1475 return false; 1476 } 1477 1478 // Validate the shortcuts. 1479 for (int i = 0; i < size; i++) { 1480 fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false); 1481 } 1482 1483 // First, remove all un-pinned; dynamic shortcuts 1484 ps.deleteAllDynamicShortcuts(); 1485 1486 // Then, add/update all. We need to make sure to take over "pinned" flag. 1487 for (int i = 0; i < size; i++) { 1488 final ShortcutInfo newShortcut = newShortcuts.get(i); 1489 ps.addOrUpdateDynamicShortcut(newShortcut); 1490 } 1491 } 1492 packageShortcutsChanged(packageName, userId); 1493 1494 verifyStates(); 1495 1496 return true; 1497 } 1498 1499 @Override 1500 public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1501 @UserIdInt int userId) { 1502 verifyCaller(packageName, userId); 1503 1504 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1505 final int size = newShortcuts.size(); 1506 1507 synchronized (mLock) { 1508 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1509 1510 ps.ensureImmutableShortcutsNotIncluded(newShortcuts); 1511 1512 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE); 1513 1514 // Throttling. 1515 if (!ps.tryApiCall()) { 1516 return false; 1517 } 1518 1519 for (int i = 0; i < size; i++) { 1520 final ShortcutInfo source = newShortcuts.get(i); 1521 fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); 1522 1523 final ShortcutInfo target = ps.findShortcutById(source.getId()); 1524 if (target != null) { 1525 if (target.isEnabled() != source.isEnabled()) { 1526 Slog.w(TAG, 1527 "ShortcutInfo.enabled cannot be changed with updateShortcuts()"); 1528 } 1529 1530 final boolean replacingIcon = (source.getIcon() != null); 1531 if (replacingIcon) { 1532 removeIcon(userId, target); 1533 } 1534 1535 if (source.getActivity() != null && 1536 !source.getActivity().equals(target.getActivity())) { 1537 // TODO When activity is changing, check the dynamic count. 1538 } 1539 1540 // Note copyNonNullFieldsFrom() does the "udpatable with?" check too. 1541 target.copyNonNullFieldsFrom(source); 1542 1543 if (replacingIcon) { 1544 saveIconAndFixUpShortcut(userId, target); 1545 } 1546 } 1547 } 1548 } 1549 packageShortcutsChanged(packageName, userId); 1550 1551 verifyStates(); 1552 1553 return true; 1554 } 1555 1556 @Override 1557 public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, 1558 @UserIdInt int userId) { 1559 verifyCaller(packageName, userId); 1560 1561 final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); 1562 final int size = newShortcuts.size(); 1563 1564 synchronized (mLock) { 1565 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1566 1567 ps.ensureImmutableShortcutsNotIncluded(newShortcuts); 1568 1569 ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_ADD); 1570 1571 // Throttling. 1572 if (!ps.tryApiCall()) { 1573 return false; 1574 } 1575 for (int i = 0; i < size; i++) { 1576 final ShortcutInfo newShortcut = newShortcuts.get(i); 1577 1578 // Validate the shortcut. 1579 fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false); 1580 1581 // Add it. 1582 ps.addOrUpdateDynamicShortcut(newShortcut); 1583 } 1584 } 1585 packageShortcutsChanged(packageName, userId); 1586 1587 verifyStates(); 1588 1589 return true; 1590 } 1591 1592 @Override 1593 public void disableShortcuts(String packageName, List shortcutIds, 1594 String disabledMessage, int disabledMessageResId, @UserIdInt int userId) { 1595 verifyCaller(packageName, userId); 1596 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 1597 1598 synchronized (mLock) { 1599 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1600 1601 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds); 1602 1603 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 1604 ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)), 1605 disabledMessage, disabledMessageResId, 1606 /* overrideImmutable=*/ false); 1607 } 1608 } 1609 packageShortcutsChanged(packageName, userId); 1610 1611 verifyStates(); 1612 } 1613 1614 @Override 1615 public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) { 1616 verifyCaller(packageName, userId); 1617 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 1618 1619 synchronized (mLock) { 1620 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1621 1622 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds); 1623 1624 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 1625 ps.enableWithId((String) shortcutIds.get(i)); 1626 } 1627 } 1628 packageShortcutsChanged(packageName, userId); 1629 1630 verifyStates(); 1631 } 1632 1633 @Override 1634 public void removeDynamicShortcuts(String packageName, List shortcutIds, 1635 @UserIdInt int userId) { 1636 verifyCaller(packageName, userId); 1637 Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); 1638 1639 synchronized (mLock) { 1640 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1641 1642 ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds); 1643 1644 for (int i = shortcutIds.size() - 1; i >= 0; i--) { 1645 ps.deleteDynamicWithId( 1646 Preconditions.checkStringNotEmpty((String) shortcutIds.get(i))); 1647 } 1648 } 1649 packageShortcutsChanged(packageName, userId); 1650 1651 verifyStates(); 1652 } 1653 1654 @Override 1655 public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { 1656 verifyCaller(packageName, userId); 1657 1658 synchronized (mLock) { 1659 getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(); 1660 } 1661 packageShortcutsChanged(packageName, userId); 1662 1663 verifyStates(); 1664 } 1665 1666 @Override 1667 public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName, 1668 @UserIdInt int userId) { 1669 verifyCaller(packageName, userId); 1670 synchronized (mLock) { 1671 return getShortcutsWithQueryLocked( 1672 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 1673 ShortcutInfo::isDynamic); 1674 } 1675 } 1676 1677 @Override 1678 public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName, 1679 @UserIdInt int userId) { 1680 verifyCaller(packageName, userId); 1681 synchronized (mLock) { 1682 return getShortcutsWithQueryLocked( 1683 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 1684 ShortcutInfo::isManifestShortcut); 1685 } 1686 } 1687 1688 @Override 1689 public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName, 1690 @UserIdInt int userId) { 1691 verifyCaller(packageName, userId); 1692 synchronized (mLock) { 1693 return getShortcutsWithQueryLocked( 1694 packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, 1695 ShortcutInfo::isPinned); 1696 } 1697 } 1698 1699 private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName, 1700 @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) { 1701 1702 final ArrayList<ShortcutInfo> ret = new ArrayList<>(); 1703 1704 getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags); 1705 1706 return new ParceledListSlice<>(ret); 1707 } 1708 1709 @Override 1710 public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId) 1711 throws RemoteException { 1712 verifyCaller(packageName, userId); 1713 1714 return mMaxDynamicShortcuts; 1715 } 1716 1717 @Override 1718 public int getRemainingCallCount(String packageName, @UserIdInt int userId) { 1719 verifyCaller(packageName, userId); 1720 1721 synchronized (mLock) { 1722 return mMaxUpdatesPerInterval 1723 - getPackageShortcutsLocked(packageName, userId).getApiCallCount(); 1724 } 1725 } 1726 1727 @Override 1728 public long getRateLimitResetTime(String packageName, @UserIdInt int userId) { 1729 verifyCaller(packageName, userId); 1730 1731 synchronized (mLock) { 1732 return getNextResetTimeLocked(); 1733 } 1734 } 1735 1736 @Override 1737 public int getIconMaxDimensions(String packageName, int userId) { 1738 verifyCaller(packageName, userId); 1739 1740 synchronized (mLock) { 1741 return mMaxIconDimension; 1742 } 1743 } 1744 1745 @Override 1746 public void reportShortcutUsed(String packageName, String shortcutId, int userId) { 1747 verifyCaller(packageName, userId); 1748 1749 Preconditions.checkNotNull(shortcutId); 1750 1751 if (DEBUG) { 1752 Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d", 1753 shortcutId, packageName, userId)); 1754 } 1755 1756 synchronized (mLock) { 1757 final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); 1758 if (ps.findShortcutById(shortcutId) == null) { 1759 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s", 1760 packageName, shortcutId)); 1761 return; 1762 } 1763 } 1764 1765 final long token = injectClearCallingIdentity(); 1766 try { 1767 mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId); 1768 } finally { 1769 injectRestoreCallingIdentity(token); 1770 } 1771 } 1772 1773 /** 1774 * Reset all throttling, for developer options and command line. Only system/shell can call it. 1775 */ 1776 @Override 1777 public void resetThrottling() { 1778 enforceSystemOrShell(); 1779 1780 resetThrottlingInner(getCallingUserId()); 1781 } 1782 1783 void resetThrottlingInner(@UserIdInt int userId) { 1784 synchronized (mLock) { 1785 getUserShortcutsLocked(userId).resetThrottling(); 1786 } 1787 scheduleSaveUser(userId); 1788 Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId); 1789 } 1790 1791 void resetAllThrottlingInner() { 1792 synchronized (mLock) { 1793 mRawLastResetTime = injectCurrentTimeMillis(); 1794 } 1795 scheduleSaveBaseState(); 1796 Slog.i(TAG, "ShortcutManager: throttling counter reset for all users"); 1797 } 1798 1799 void resetPackageThrottling(String packageName, int userId) { 1800 synchronized (mLock) { 1801 getPackageShortcutsLocked(packageName, userId) 1802 .resetRateLimitingForCommandLineNoSaving(); 1803 saveUserLocked(userId); 1804 } 1805 } 1806 1807 @Override 1808 public void onApplicationActive(String packageName, int userId) { 1809 if (DEBUG) { 1810 Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId); 1811 } 1812 enforceResetThrottlingPermission(); 1813 resetPackageThrottling(packageName, userId); 1814 } 1815 1816 // We override this method in unit tests to do a simpler check. 1817 boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) { 1818 return hasShortcutHostPermissionInner(callingPackage, userId); 1819 } 1820 1821 // This method is extracted so we can directly call this method from unit tests, 1822 // even when hasShortcutPermission() is overridden. 1823 @VisibleForTesting 1824 boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { 1825 synchronized (mLock) { 1826 final long start = injectElapsedRealtime(); 1827 1828 final ShortcutUser user = getUserShortcutsLocked(userId); 1829 1830 final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); 1831 1832 // Default launcher from package manager. 1833 final long startGetHomeActivitiesAsUser = injectElapsedRealtime(); 1834 final ComponentName defaultLauncher = injectPackageManagerInternal() 1835 .getHomeActivitiesAsUser(allHomeCandidates, userId); 1836 logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser); 1837 1838 ComponentName detected; 1839 if (defaultLauncher != null) { 1840 detected = defaultLauncher; 1841 if (DEBUG) { 1842 Slog.v(TAG, "Default launcher from PM: " + detected); 1843 } 1844 } else { 1845 detected = user.getDefaultLauncherComponent(); 1846 1847 // TODO: Make sure it's still enabled. 1848 if (DEBUG) { 1849 Slog.v(TAG, "Cached launcher: " + detected); 1850 } 1851 } 1852 1853 if (detected == null) { 1854 // If we reach here, that means it's the first check since the user was created, 1855 // and there's already multiple launchers and there's no default set. 1856 // Find the system one with the highest priority. 1857 // (We need to check the priority too because of FallbackHome in Settings.) 1858 // If there's no system launcher yet, then no one can access shortcuts, until 1859 // the user explicitly 1860 final int size = allHomeCandidates.size(); 1861 1862 int lastPriority = Integer.MIN_VALUE; 1863 for (int i = 0; i < size; i++) { 1864 final ResolveInfo ri = allHomeCandidates.get(i); 1865 if (!ri.activityInfo.applicationInfo.isSystemApp()) { 1866 continue; 1867 } 1868 if (DEBUG) { 1869 Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d", 1870 ri.activityInfo.getComponentName(), ri.priority)); 1871 } 1872 if (ri.priority < lastPriority) { 1873 continue; 1874 } 1875 detected = ri.activityInfo.getComponentName(); 1876 lastPriority = ri.priority; 1877 } 1878 } 1879 logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start); 1880 1881 if (detected != null) { 1882 if (DEBUG) { 1883 Slog.v(TAG, "Detected launcher: " + detected); 1884 } 1885 user.setDefaultLauncherComponent(detected); 1886 return detected.getPackageName().equals(callingPackage); 1887 } else { 1888 // Default launcher not found. 1889 return false; 1890 } 1891 } 1892 } 1893 1894 // === House keeping === 1895 1896 private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId) { 1897 synchronized (mLock) { 1898 forEachLoadedUserLocked(user -> 1899 cleanUpPackageLocked(packageName, user.getUserId(), packageUserId)); 1900 } 1901 } 1902 1903 /** 1904 * Remove all the information associated with a package. This will really remove all the 1905 * information, including the restore information (i.e. it'll remove packages even if they're 1906 * shadow). 1907 * 1908 * This is called when an app is uninstalled, or an app gets "clear data"ed. 1909 */ 1910 @VisibleForTesting 1911 void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) { 1912 final boolean wasUserLoaded = isUserLoadedLocked(owningUserId); 1913 1914 final ShortcutUser user = getUserShortcutsLocked(owningUserId); 1915 boolean doNotify = false; 1916 1917 // First, remove the package from the package list (if the package is a publisher). 1918 if (packageUserId == owningUserId) { 1919 if (user.removePackage(packageName) != null) { 1920 doNotify = true; 1921 } 1922 } 1923 1924 // Also remove from the launcher list (if the package is a launcher). 1925 user.removeLauncher(packageUserId, packageName); 1926 1927 // Then remove pinned shortcuts from all launchers. 1928 user.forAllLaunchers(l -> l.cleanUpPackage(packageName, packageUserId)); 1929 1930 // Now there may be orphan shortcuts because we removed pinned shortcuts at the previous 1931 // step. Remove them too. 1932 user.forAllPackages(p -> p.refreshPinnedFlags()); 1933 1934 scheduleSaveUser(owningUserId); 1935 1936 if (doNotify) { 1937 notifyListeners(packageName, owningUserId); 1938 } 1939 1940 if (!wasUserLoaded) { 1941 // Note this will execute the scheduled save. 1942 unloadUserLocked(owningUserId); 1943 } 1944 } 1945 1946 /** 1947 * Entry point from {@link LauncherApps}. 1948 */ 1949 private class LocalService extends ShortcutServiceInternal { 1950 1951 @Override 1952 public List<ShortcutInfo> getShortcuts(int launcherUserId, 1953 @NonNull String callingPackage, long changedSince, 1954 @Nullable String packageName, @Nullable List<String> shortcutIds, 1955 @Nullable ComponentName componentName, 1956 int queryFlags, int userId) { 1957 final ArrayList<ShortcutInfo> ret = new ArrayList<>(); 1958 final boolean cloneKeyFieldOnly = 1959 ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0); 1960 final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO 1961 : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER; 1962 if (packageName == null) { 1963 shortcutIds = null; // LauncherAppsService already threw for it though. 1964 } 1965 1966 synchronized (mLock) { 1967 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 1968 .attemptToRestoreIfNeededAndSave(); 1969 1970 if (packageName != null) { 1971 getShortcutsInnerLocked(launcherUserId, 1972 callingPackage, packageName, shortcutIds, changedSince, 1973 componentName, queryFlags, userId, ret, cloneFlag); 1974 } else { 1975 final List<String> shortcutIdsF = shortcutIds; 1976 getUserShortcutsLocked(userId).forAllPackages(p -> { 1977 getShortcutsInnerLocked(launcherUserId, 1978 callingPackage, p.getPackageName(), shortcutIdsF, changedSince, 1979 componentName, queryFlags, userId, ret, cloneFlag); 1980 }); 1981 } 1982 } 1983 // Resolve all strings if needed. 1984 if (!cloneKeyFieldOnly) { 1985 final long token = injectClearCallingIdentity(); 1986 try { 1987 for (int i = ret.size() - 1; i >= 0; i--) { 1988 try { 1989 ret.get(i).resolveStringsRequiringCrossUser(mContext); 1990 } catch (NameNotFoundException e) { 1991 continue; 1992 } 1993 } 1994 } finally { 1995 injectRestoreCallingIdentity(token); 1996 } 1997 } 1998 return ret; 1999 } 2000 2001 private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage, 2002 @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince, 2003 @Nullable ComponentName componentName, int queryFlags, 2004 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) { 2005 final ArraySet<String> ids = shortcutIds == null ? null 2006 : new ArraySet<>(shortcutIds); 2007 2008 final ShortcutPackage p = getUserShortcutsLocked(userId) 2009 .getPackageShortcutsIfExists(packageName); 2010 if (p == null) { 2011 return; // No need to instantiate ShortcutPackage. 2012 } 2013 2014 p.findAll(ret, 2015 (ShortcutInfo si) -> { 2016 if (si.getLastChangedTimestamp() < changedSince) { 2017 return false; 2018 } 2019 if (ids != null && !ids.contains(si.getId())) { 2020 return false; 2021 } 2022 if (componentName != null) { 2023 if (si.getActivity() != null 2024 && !si.getActivity().equals(componentName)) { 2025 return false; 2026 } 2027 } 2028 if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0) 2029 && si.isDynamic()) { 2030 return true; 2031 } 2032 if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0) 2033 && si.isPinned()) { 2034 return true; 2035 } 2036 if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0) 2037 && si.isManifestShortcut()) { 2038 return true; 2039 } 2040 return false; 2041 }, cloneFlag, callingPackage, launcherUserId); 2042 } 2043 2044 @Override 2045 public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage, 2046 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2047 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2048 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); 2049 2050 synchronized (mLock) { 2051 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2052 .attemptToRestoreIfNeededAndSave(); 2053 2054 final ShortcutInfo si = getShortcutInfoLocked( 2055 launcherUserId, callingPackage, packageName, shortcutId, userId); 2056 return si != null && si.isPinned(); 2057 } 2058 } 2059 2060 private ShortcutInfo getShortcutInfoLocked( 2061 int launcherUserId, @NonNull String callingPackage, 2062 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2063 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2064 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); 2065 2066 final ShortcutPackage p = getUserShortcutsLocked(userId) 2067 .getPackageShortcutsIfExists(packageName); 2068 if (p == null) { 2069 return null; 2070 } 2071 2072 final ArrayList<ShortcutInfo> list = new ArrayList<>(1); 2073 p.findAll(list, 2074 (ShortcutInfo si) -> shortcutId.equals(si.getId()), 2075 /* clone flags=*/ 0, callingPackage, launcherUserId); 2076 return list.size() == 0 ? null : list.get(0); 2077 } 2078 2079 @Override 2080 public void pinShortcuts(int launcherUserId, 2081 @NonNull String callingPackage, @NonNull String packageName, 2082 @NonNull List<String> shortcutIds, int userId) { 2083 // Calling permission must be checked by LauncherAppsImpl. 2084 Preconditions.checkStringNotEmpty(packageName, "packageName"); 2085 Preconditions.checkNotNull(shortcutIds, "shortcutIds"); 2086 2087 synchronized (mLock) { 2088 final ShortcutLauncher launcher = 2089 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId); 2090 launcher.attemptToRestoreIfNeededAndSave(); 2091 2092 launcher.pinShortcuts(userId, packageName, shortcutIds); 2093 } 2094 packageShortcutsChanged(packageName, userId); 2095 2096 verifyStates(); 2097 } 2098 2099 @Override 2100 public Intent createShortcutIntent(int launcherUserId, 2101 @NonNull String callingPackage, 2102 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2103 // Calling permission must be checked by LauncherAppsImpl. 2104 Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty"); 2105 Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty"); 2106 2107 synchronized (mLock) { 2108 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2109 .attemptToRestoreIfNeededAndSave(); 2110 2111 // Make sure the shortcut is actually visible to the launcher. 2112 final ShortcutInfo si = getShortcutInfoLocked( 2113 launcherUserId, callingPackage, packageName, shortcutId, userId); 2114 // "si == null" should suffice here, but check the flags too just to make sure. 2115 if (si == null || !si.isEnabled() || !si.isAlive()) { 2116 return null; 2117 } 2118 return si.getIntent(); 2119 } 2120 } 2121 2122 @Override 2123 public void addListener(@NonNull ShortcutChangeListener listener) { 2124 synchronized (mLock) { 2125 mListeners.add(Preconditions.checkNotNull(listener)); 2126 } 2127 } 2128 2129 @Override 2130 public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage, 2131 @NonNull String packageName, @NonNull String shortcutId, int userId) { 2132 Preconditions.checkNotNull(callingPackage, "callingPackage"); 2133 Preconditions.checkNotNull(packageName, "packageName"); 2134 Preconditions.checkNotNull(shortcutId, "shortcutId"); 2135 2136 synchronized (mLock) { 2137 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2138 .attemptToRestoreIfNeededAndSave(); 2139 2140 final ShortcutPackage p = getUserShortcutsLocked(userId) 2141 .getPackageShortcutsIfExists(packageName); 2142 if (p == null) { 2143 return 0; 2144 } 2145 2146 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId); 2147 return (shortcutInfo != null && shortcutInfo.hasIconResource()) 2148 ? shortcutInfo.getIconResourceId() : 0; 2149 } 2150 } 2151 2152 @Override 2153 public ParcelFileDescriptor getShortcutIconFd(int launcherUserId, 2154 @NonNull String callingPackage, @NonNull String packageName, 2155 @NonNull String shortcutId, int userId) { 2156 Preconditions.checkNotNull(callingPackage, "callingPackage"); 2157 Preconditions.checkNotNull(packageName, "packageName"); 2158 Preconditions.checkNotNull(shortcutId, "shortcutId"); 2159 2160 synchronized (mLock) { 2161 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) 2162 .attemptToRestoreIfNeededAndSave(); 2163 2164 final ShortcutPackage p = getUserShortcutsLocked(userId) 2165 .getPackageShortcutsIfExists(packageName); 2166 if (p == null) { 2167 return null; 2168 } 2169 2170 final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId); 2171 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) { 2172 return null; 2173 } 2174 try { 2175 if (shortcutInfo.getBitmapPath() == null) { 2176 Slog.w(TAG, "null bitmap detected in getShortcutIconFd()"); 2177 return null; 2178 } 2179 return ParcelFileDescriptor.open( 2180 new File(shortcutInfo.getBitmapPath()), 2181 ParcelFileDescriptor.MODE_READ_ONLY); 2182 } catch (FileNotFoundException e) { 2183 Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath()); 2184 return null; 2185 } 2186 } 2187 } 2188 2189 @Override 2190 public boolean hasShortcutHostPermission(int launcherUserId, 2191 @NonNull String callingPackage) { 2192 return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId); 2193 } 2194 2195 /** 2196 * Called by AM when the system locale changes *within the AM lock. ABSOLUTELY do not take 2197 * any locks in this method. 2198 */ 2199 @Override 2200 public void onSystemLocaleChangedNoLock() { 2201 // DO NOT HOLD ANY LOCKS HERE. 2202 2203 // We want to reset throttling for all packages for all users. But we can't just do so 2204 // here because: 2205 // - We can't load/save users that are locked. 2206 // - Even for loaded users, resetting the counters would require us to hold mLock. 2207 // 2208 // So we use a "pull" model instead. In here, we just increment the "locale change 2209 // sequence number". Each ShortcutUser has the "last known locale change sequence". 2210 // 2211 // This allows ShortcutUser's to detect the system locale change, so they can reset 2212 // counters. 2213 2214 // Ignore all callback during system boot. 2215 if (mBootCompleted.get()) { 2216 mLocaleChangeSequenceNumber.incrementAndGet(); 2217 if (DEBUG) { 2218 Slog.d(TAG, "onSystemLocaleChangedNoLock: " + mLocaleChangeSequenceNumber.get()); 2219 } 2220 postToHandler(() -> scheduleSaveBaseState()); 2221 } 2222 } 2223 } 2224 2225 /** 2226 * Package event callbacks. 2227 */ 2228 @VisibleForTesting 2229 final PackageMonitor mPackageMonitor = new PackageMonitor() { 2230 @Override 2231 public void onPackageAdded(String packageName, int uid) { 2232 handlePackageAdded(packageName, getChangingUserId()); 2233 } 2234 2235 @Override 2236 public void onPackageUpdateFinished(String packageName, int uid) { 2237 handlePackageUpdateFinished(packageName, getChangingUserId()); 2238 } 2239 2240 @Override 2241 public void onPackageRemoved(String packageName, int uid) { 2242 handlePackageRemoved(packageName, getChangingUserId()); 2243 } 2244 2245 @Override 2246 public void onPackageDataCleared(String packageName, int uid) { 2247 handlePackageDataCleared(packageName, getChangingUserId()); 2248 } 2249 }; 2250 2251 /** 2252 * Called when a user is unlocked. 2253 * - Check all known packages still exist, and otherwise perform cleanup. 2254 * - If a package still exists, check the version code. If it's been updated, may need to 2255 * update timestamps of its shortcuts. 2256 */ 2257 @VisibleForTesting 2258 void checkPackageChanges(@UserIdInt int ownerUserId) { 2259 if (DEBUG) { 2260 Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId); 2261 } 2262 2263 final long start = injectElapsedRealtime(); 2264 try { 2265 final ArrayList<PackageWithUser> gonePackages = new ArrayList<>(); 2266 2267 synchronized (mLock) { 2268 final ShortcutUser user = getUserShortcutsLocked(ownerUserId); 2269 2270 // Find packages that have been uninstalled. 2271 user.forAllPackageItems(spi -> { 2272 if (spi.getPackageInfo().isShadow()) { 2273 return; // Don't delete shadow information. 2274 } 2275 if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) { 2276 gonePackages.add(PackageWithUser.of(spi)); 2277 } 2278 }); 2279 if (gonePackages.size() > 0) { 2280 for (int i = gonePackages.size() - 1; i >= 0; i--) { 2281 final PackageWithUser pu = gonePackages.get(i); 2282 cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId); 2283 } 2284 } 2285 2286 // Then for each installed app, publish manifest shortcuts when needed. 2287 forInstalledApplications(ownerUserId, ai -> { 2288 user.handlePackageAddedOrUpdated(ai.packageName); 2289 }); 2290 } 2291 } finally { 2292 logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start); 2293 } 2294 } 2295 2296 private void handlePackageAdded(String packageName, @UserIdInt int userId) { 2297 if (DEBUG) { 2298 Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId)); 2299 } 2300 synchronized (mLock) { 2301 final ShortcutUser user = getUserShortcutsLocked(userId); 2302 user.attemptToRestoreIfNeededAndSave(this, packageName, userId); 2303 user.handlePackageAddedOrUpdated(packageName); 2304 } 2305 } 2306 2307 private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) { 2308 if (DEBUG) { 2309 Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d", 2310 packageName, userId)); 2311 } 2312 synchronized (mLock) { 2313 final ShortcutUser user = getUserShortcutsLocked(userId); 2314 user.attemptToRestoreIfNeededAndSave(this, packageName, userId); 2315 2316 if (isPackageInstalled(packageName, userId)) { 2317 user.handlePackageAddedOrUpdated(packageName); 2318 } 2319 } 2320 } 2321 2322 private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) { 2323 if (DEBUG) { 2324 Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, 2325 packageUserId)); 2326 } 2327 cleanUpPackageForAllLoadedUsers(packageName, packageUserId); 2328 } 2329 2330 private void handlePackageDataCleared(String packageName, int packageUserId) { 2331 if (DEBUG) { 2332 Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName, 2333 packageUserId)); 2334 } 2335 cleanUpPackageForAllLoadedUsers(packageName, packageUserId); 2336 } 2337 2338 // === PackageManager interaction === 2339 2340 @Nullable 2341 PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) { 2342 return injectPackageInfo(packageName, userId, true); 2343 } 2344 2345 @Nullable 2346 PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) { 2347 return injectPackageInfo(packageName, userId, false); 2348 } 2349 2350 int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) { 2351 final long token = injectClearCallingIdentity(); 2352 try { 2353 return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS 2354 , userId); 2355 } catch (RemoteException e) { 2356 // Shouldn't happen. 2357 Slog.wtf(TAG, "RemoteException", e); 2358 return -1; 2359 } finally { 2360 injectRestoreCallingIdentity(token); 2361 } 2362 } 2363 2364 @Nullable 2365 @VisibleForTesting 2366 PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId, 2367 boolean getSignatures) { 2368 final long start = injectElapsedRealtime(); 2369 final long token = injectClearCallingIdentity(); 2370 try { 2371 return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS 2372 | (getSignatures ? PackageManager.GET_SIGNATURES : 0) 2373 , userId); 2374 } catch (RemoteException e) { 2375 // Shouldn't happen. 2376 Slog.wtf(TAG, "RemoteException", e); 2377 return null; 2378 } finally { 2379 injectRestoreCallingIdentity(token); 2380 2381 logDurationStat( 2382 (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO), 2383 start); 2384 } 2385 } 2386 2387 @Nullable 2388 @VisibleForTesting 2389 ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) { 2390 final long start = injectElapsedRealtime(); 2391 final long token = injectClearCallingIdentity(); 2392 try { 2393 return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId); 2394 } catch (RemoteException e) { 2395 // Shouldn't happen. 2396 Slog.wtf(TAG, "RemoteException", e); 2397 return null; 2398 } finally { 2399 injectRestoreCallingIdentity(token); 2400 2401 logDurationStat(Stats.GET_APPLICATION_INFO, start); 2402 } 2403 } 2404 2405 @Nullable 2406 @VisibleForTesting 2407 PackageInfo injectGetActivitiesWithMetadata(String packageName, @UserIdInt int userId) { 2408 final long start = injectElapsedRealtime(); 2409 final long token = injectClearCallingIdentity(); 2410 try { 2411 return mIPackageManager.getPackageInfo(packageName, 2412 PACKAGE_MATCH_FLAGS | PackageManager.GET_ACTIVITIES 2413 | PackageManager.GET_META_DATA, userId); 2414 } catch (RemoteException e) { 2415 // Shouldn't happen. 2416 Slog.wtf(TAG, "RemoteException", e); 2417 return null; 2418 } finally { 2419 injectRestoreCallingIdentity(token); 2420 2421 logDurationStat(Stats.GET_ACTIVITIES_WITH_METADATA, start); 2422 } 2423 } 2424 2425 @Nullable 2426 @VisibleForTesting 2427 List<ApplicationInfo> injectInstalledApplications(@UserIdInt int userId) { 2428 final long start = injectElapsedRealtime(); 2429 final long token = injectClearCallingIdentity(); 2430 try { 2431 final ParceledListSlice<ApplicationInfo> parceledList = 2432 mIPackageManager.getInstalledApplications(PACKAGE_MATCH_FLAGS, userId); 2433 if (parceledList == null) { 2434 return Collections.emptyList(); 2435 } 2436 return parceledList.getList(); 2437 } catch (RemoteException e) { 2438 // Shouldn't happen. 2439 Slog.wtf(TAG, "RemoteException", e); 2440 return null; 2441 } finally { 2442 injectRestoreCallingIdentity(token); 2443 2444 logDurationStat(Stats.GET_INSTALLED_APPLICATIONS, start); 2445 } 2446 } 2447 2448 private void forInstalledApplications(@UserIdInt int userId, 2449 Consumer<ApplicationInfo> callback) { 2450 final List<ApplicationInfo> list = injectInstalledApplications(userId); 2451 for (int i = list.size() - 1; i >= 0; i--) { 2452 final ApplicationInfo ai = list.get(i); 2453 2454 if ((ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 2455 callback.accept(ai); 2456 } 2457 } 2458 } 2459 2460 private boolean isApplicationFlagSet(String packageName, int userId, int flags) { 2461 final ApplicationInfo ai = injectApplicationInfo(packageName, userId); 2462 return (ai != null) && ((ai.flags & flags) == flags); 2463 } 2464 2465 boolean isPackageInstalled(String packageName, int userId) { 2466 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED); 2467 } 2468 2469 @Nullable 2470 XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) { 2471// TODO Doesn't seem like ACROSS_USER is needed, but double check. 2472 return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key); 2473 } 2474 2475 // === Backup & restore === 2476 2477 boolean shouldBackupApp(String packageName, int userId) { 2478 return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP); 2479 } 2480 2481 boolean shouldBackupApp(PackageInfo pi) { 2482 return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; 2483 } 2484 2485 @Override 2486 public byte[] getBackupPayload(@UserIdInt int userId) { 2487 enforceSystem(); 2488 if (DEBUG) { 2489 Slog.d(TAG, "Backing up user " + userId); 2490 } 2491 synchronized (mLock) { 2492 final ShortcutUser user = getUserShortcutsLocked(userId); 2493 if (user == null) { 2494 Slog.w(TAG, "Can't backup: user not found: id=" + userId); 2495 return null; 2496 } 2497 2498 user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave()); 2499 2500 // Then save. 2501 final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024); 2502 try { 2503 saveUserInternalLocked(userId, os, /* forBackup */ true); 2504 } catch (XmlPullParserException|IOException e) { 2505 // Shouldn't happen. 2506 Slog.w(TAG, "Backup failed.", e); 2507 return null; 2508 } 2509 return os.toByteArray(); 2510 } 2511 } 2512 2513 @Override 2514 public void applyRestore(byte[] payload, @UserIdInt int userId) { 2515 enforceSystem(); 2516 if (DEBUG) { 2517 Slog.d(TAG, "Restoring user " + userId); 2518 } 2519 final ShortcutUser user; 2520 final ByteArrayInputStream is = new ByteArrayInputStream(payload); 2521 try { 2522 user = loadUserInternal(userId, is, /* fromBackup */ true); 2523 } catch (XmlPullParserException|IOException e) { 2524 Slog.w(TAG, "Restoration failed.", e); 2525 return; 2526 } 2527 synchronized (mLock) { 2528 mUsers.put(userId, user); 2529 2530 // Then purge all the save images. 2531 final File bitmapPath = getUserBitmapFilePath(userId); 2532 final boolean success = FileUtils.deleteContents(bitmapPath); 2533 if (!success) { 2534 Slog.w(TAG, "Failed to delete " + bitmapPath); 2535 } 2536 2537 saveUserLocked(userId); 2538 } 2539 } 2540 2541 // === Dump === 2542 2543 @Override 2544 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2545 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2546 != PackageManager.PERMISSION_GRANTED) { 2547 pw.println("Permission Denial: can't dump UserManager from from pid=" 2548 + Binder.getCallingPid() 2549 + ", uid=" + Binder.getCallingUid() 2550 + " without permission " 2551 + android.Manifest.permission.DUMP); 2552 return; 2553 } 2554 dumpInner(pw, args); 2555 } 2556 2557 @VisibleForTesting 2558 void dumpInner(PrintWriter pw, String[] args) { 2559 synchronized (mLock) { 2560 final long now = injectCurrentTimeMillis(); 2561 pw.print("Now: ["); 2562 pw.print(now); 2563 pw.print("] "); 2564 pw.print(formatTime(now)); 2565 2566 pw.print(" Raw last reset: ["); 2567 pw.print(mRawLastResetTime); 2568 pw.print("] "); 2569 pw.print(formatTime(mRawLastResetTime)); 2570 2571 final long last = getLastResetTimeLocked(); 2572 pw.print(" Last reset: ["); 2573 pw.print(last); 2574 pw.print("] "); 2575 pw.print(formatTime(last)); 2576 2577 final long next = getNextResetTimeLocked(); 2578 pw.print(" Next reset: ["); 2579 pw.print(next); 2580 pw.print("] "); 2581 pw.print(formatTime(next)); 2582 2583 pw.print(" Locale change seq#: "); 2584 pw.print(mLocaleChangeSequenceNumber.get()); 2585 pw.println(); 2586 2587 pw.print(" Config:"); 2588 pw.print(" Max icon dim: "); 2589 pw.println(mMaxIconDimension); 2590 pw.print(" Icon format: "); 2591 pw.println(mIconPersistFormat); 2592 pw.print(" Icon quality: "); 2593 pw.println(mIconPersistQuality); 2594 pw.print(" saveDelayMillis: "); 2595 pw.println(mSaveDelayMillis); 2596 pw.print(" resetInterval: "); 2597 pw.println(mResetInterval); 2598 pw.print(" maxUpdatesPerInterval: "); 2599 pw.println(mMaxUpdatesPerInterval); 2600 pw.print(" maxDynamicShortcuts: "); 2601 pw.println(mMaxDynamicShortcuts); 2602 pw.println(); 2603 2604 pw.println(" Stats:"); 2605 synchronized (mStatLock) { 2606 final String p = " "; 2607 dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()"); 2608 dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check"); 2609 2610 dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()"); 2611 dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)"); 2612 dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo"); 2613 dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps"); 2614 dumpStatLS(pw, p, Stats.GET_ACTIVITIES_WITH_METADATA, "getActivities+metadata"); 2615 dumpStatLS(pw, p, Stats.GET_INSTALLED_APPLICATIONS, "getInstalledApplications"); 2616 dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges"); 2617 } 2618 2619 for (int i = 0; i < mUsers.size(); i++) { 2620 pw.println(); 2621 mUsers.valueAt(i).dump(pw, " "); 2622 } 2623 2624 pw.println(); 2625 pw.println(" UID state:"); 2626 2627 for (int i = 0; i < mUidState.size(); i++) { 2628 final int uid = mUidState.keyAt(i); 2629 final int state = mUidState.valueAt(i); 2630 pw.print(" UID="); 2631 pw.print(uid); 2632 pw.print(" state="); 2633 pw.print(state); 2634 if (isProcessStateForeground(state)) { 2635 pw.print(" [FG]"); 2636 } 2637 pw.print(" last FG="); 2638 pw.print(mUidLastForegroundElapsedTime.get(uid)); 2639 pw.println(); 2640 } 2641 } 2642 } 2643 2644 static String formatTime(long time) { 2645 Time tobj = new Time(); 2646 tobj.set(time); 2647 return tobj.format("%Y-%m-%d %H:%M:%S"); 2648 } 2649 2650 private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) { 2651 pw.print(prefix); 2652 final int count = mCountStats[statId]; 2653 final long dur = mDurationStats[statId]; 2654 pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms", 2655 label, count, dur, 2656 (count == 0 ? 0 : ((double) dur) / count))); 2657 } 2658 2659 // === Shell support === 2660 2661 @Override 2662 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 2663 String[] args, ResultReceiver resultReceiver) throws RemoteException { 2664 2665 enforceShell(); 2666 2667 (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver); 2668 } 2669 2670 static class CommandException extends Exception { 2671 public CommandException(String message) { 2672 super(message); 2673 } 2674 } 2675 2676 /** 2677 * Handle "adb shell cmd". 2678 */ 2679 private class MyShellCommand extends ShellCommand { 2680 2681 private int mUserId = UserHandle.USER_SYSTEM; 2682 2683 private void parseOptions(boolean takeUser) 2684 throws CommandException { 2685 String opt; 2686 while ((opt = getNextOption()) != null) { 2687 switch (opt) { 2688 case "--user": 2689 if (takeUser) { 2690 mUserId = UserHandle.parseUserArg(getNextArgRequired()); 2691 break; 2692 } 2693 // fallthrough 2694 default: 2695 throw new CommandException("Unknown option: " + opt); 2696 } 2697 } 2698 } 2699 2700 @Override 2701 public int onCommand(String cmd) { 2702 if (cmd == null) { 2703 return handleDefaultCommands(cmd); 2704 } 2705 final PrintWriter pw = getOutPrintWriter(); 2706 try { 2707 switch (cmd) { 2708 case "reset-package-throttling": 2709 handleResetPackageThrottling(); 2710 break; 2711 case "reset-throttling": 2712 handleResetThrottling(); 2713 break; 2714 case "reset-all-throttling": 2715 handleResetAllThrottling(); 2716 break; 2717 case "override-config": 2718 handleOverrideConfig(); 2719 break; 2720 case "reset-config": 2721 handleResetConfig(); 2722 break; 2723 case "clear-default-launcher": 2724 handleClearDefaultLauncher(); 2725 break; 2726 case "get-default-launcher": 2727 handleGetDefaultLauncher(); 2728 break; 2729 case "refresh-default-launcher": 2730 handleRefreshDefaultLauncher(); 2731 break; 2732 case "unload-user": 2733 handleUnloadUser(); 2734 break; 2735 case "clear-shortcuts": 2736 handleClearShortcuts(); 2737 break; 2738 default: 2739 return handleDefaultCommands(cmd); 2740 } 2741 } catch (CommandException e) { 2742 pw.println("Error: " + e.getMessage()); 2743 return 1; 2744 } 2745 pw.println("Success"); 2746 return 0; 2747 } 2748 2749 @Override 2750 public void onHelp() { 2751 final PrintWriter pw = getOutPrintWriter(); 2752 pw.println("Usage: cmd shortcut COMMAND [options ...]"); 2753 pw.println(); 2754 pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE"); 2755 pw.println(" Reset throttling for a package"); 2756 pw.println(); 2757 pw.println("cmd shortcut reset-throttling [--user USER_ID]"); 2758 pw.println(" Reset throttling for all packages and users"); 2759 pw.println(); 2760 pw.println("cmd shortcut reset-all-throttling"); 2761 pw.println(" Reset the throttling state for all users"); 2762 pw.println(); 2763 pw.println("cmd shortcut override-config CONFIG"); 2764 pw.println(" Override the configuration for testing (will last until reboot)"); 2765 pw.println(); 2766 pw.println("cmd shortcut reset-config"); 2767 pw.println(" Reset the configuration set with \"update-config\""); 2768 pw.println(); 2769 pw.println("cmd shortcut clear-default-launcher [--user USER_ID]"); 2770 pw.println(" Clear the cached default launcher"); 2771 pw.println(); 2772 pw.println("cmd shortcut get-default-launcher [--user USER_ID]"); 2773 pw.println(" Show the cached default launcher"); 2774 pw.println(); 2775 pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]"); 2776 pw.println(" Refresh the cached default launcher"); 2777 pw.println(); 2778 pw.println("cmd shortcut unload-user [--user USER_ID]"); 2779 pw.println(" Unload a user from the memory"); 2780 pw.println(" (This should not affect any observable behavior)"); 2781 pw.println(); 2782 pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE"); 2783 pw.println(" Remove all shortcuts from a package, including pinned shortcuts"); 2784 pw.println(); 2785 } 2786 2787 private void handleResetThrottling() throws CommandException { 2788 parseOptions(/* takeUser =*/ true); 2789 2790 Slog.i(TAG, "cmd: handleResetThrottling"); 2791 2792 resetThrottlingInner(mUserId); 2793 } 2794 2795 private void handleResetAllThrottling() { 2796 Slog.i(TAG, "cmd: handleResetAllThrottling"); 2797 2798 resetAllThrottlingInner(); 2799 } 2800 2801 private void handleResetPackageThrottling() throws CommandException { 2802 parseOptions(/* takeUser =*/ true); 2803 2804 final String packageName = getNextArgRequired(); 2805 2806 Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName); 2807 2808 resetPackageThrottling(packageName, mUserId); 2809 } 2810 2811 private void handleOverrideConfig() throws CommandException { 2812 final String config = getNextArgRequired(); 2813 2814 Slog.i(TAG, "cmd: handleOverrideConfig: " + config); 2815 2816 synchronized (mLock) { 2817 if (!updateConfigurationLocked(config)) { 2818 throw new CommandException("override-config failed. See logcat for details."); 2819 } 2820 } 2821 } 2822 2823 private void handleResetConfig() { 2824 Slog.i(TAG, "cmd: handleResetConfig"); 2825 2826 synchronized (mLock) { 2827 loadConfigurationLocked(); 2828 } 2829 } 2830 2831 private void clearLauncher() { 2832 synchronized (mLock) { 2833 getUserShortcutsLocked(mUserId).setDefaultLauncherComponent(null); 2834 } 2835 } 2836 2837 private void showLauncher() { 2838 synchronized (mLock) { 2839 // This ensures to set the cached launcher. Package name doesn't matter. 2840 hasShortcutHostPermissionInner("-", mUserId); 2841 2842 getOutPrintWriter().println("Launcher: " 2843 + getUserShortcutsLocked(mUserId).getDefaultLauncherComponent()); 2844 } 2845 } 2846 2847 private void handleClearDefaultLauncher() throws CommandException { 2848 parseOptions(/* takeUser =*/ true); 2849 2850 clearLauncher(); 2851 } 2852 2853 private void handleGetDefaultLauncher() throws CommandException { 2854 parseOptions(/* takeUser =*/ true); 2855 2856 showLauncher(); 2857 } 2858 2859 private void handleRefreshDefaultLauncher() throws CommandException { 2860 parseOptions(/* takeUser =*/ true); 2861 2862 clearLauncher(); 2863 showLauncher(); 2864 } 2865 2866 private void handleUnloadUser() throws CommandException { 2867 parseOptions(/* takeUser =*/ true); 2868 2869 Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId); 2870 2871 ShortcutService.this.handleCleanupUser(mUserId); 2872 } 2873 2874 private void handleClearShortcuts() throws CommandException { 2875 parseOptions(/* takeUser =*/ true); 2876 final String packageName = getNextArgRequired(); 2877 2878 Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName); 2879 2880 ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId); 2881 } 2882 } 2883 2884 // === Unit test support === 2885 2886 // Injection point. 2887 @VisibleForTesting 2888 long injectCurrentTimeMillis() { 2889 return System.currentTimeMillis(); 2890 } 2891 2892 @VisibleForTesting 2893 long injectElapsedRealtime() { 2894 return SystemClock.elapsedRealtime(); 2895 } 2896 2897 // Injection point. 2898 @VisibleForTesting 2899 int injectBinderCallingUid() { 2900 return getCallingUid(); 2901 } 2902 2903 private int getCallingUserId() { 2904 return UserHandle.getUserId(injectBinderCallingUid()); 2905 } 2906 2907 // Injection point. 2908 @VisibleForTesting 2909 long injectClearCallingIdentity() { 2910 return Binder.clearCallingIdentity(); 2911 } 2912 2913 // Injection point. 2914 @VisibleForTesting 2915 void injectRestoreCallingIdentity(long token) { 2916 Binder.restoreCallingIdentity(token); 2917 } 2918 2919 final void wtf(String message) { 2920 wtf( message, /* exception= */ null); 2921 } 2922 2923 // Injection point. 2924 void wtf(String message, Exception e) { 2925 Slog.wtf(TAG, message, e); 2926 } 2927 2928 @VisibleForTesting 2929 File injectSystemDataPath() { 2930 return Environment.getDataSystemDirectory(); 2931 } 2932 2933 @VisibleForTesting 2934 File injectUserDataPath(@UserIdInt int userId) { 2935 return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER); 2936 } 2937 2938 @VisibleForTesting 2939 boolean injectIsLowRamDevice() { 2940 return ActivityManager.isLowRamDeviceStatic(); 2941 } 2942 2943 @VisibleForTesting 2944 void injectRegisterUidObserver(IUidObserver observer, int which) { 2945 try { 2946 ActivityManagerNative.getDefault().registerUidObserver(observer, which); 2947 } catch (RemoteException shouldntHappen) { 2948 } 2949 } 2950 2951 @VisibleForTesting 2952 PackageManagerInternal injectPackageManagerInternal() { 2953 return mPackageManagerInternal; 2954 } 2955 2956 File getUserBitmapFilePath(@UserIdInt int userId) { 2957 return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS); 2958 } 2959 2960 @VisibleForTesting 2961 SparseArray<ShortcutUser> getShortcutsForTest() { 2962 return mUsers; 2963 } 2964 2965 @VisibleForTesting 2966 int getMaxDynamicShortcutsForTest() { 2967 return mMaxDynamicShortcuts; 2968 } 2969 2970 @VisibleForTesting 2971 int getMaxUpdatesPerIntervalForTest() { 2972 return mMaxUpdatesPerInterval; 2973 } 2974 2975 @VisibleForTesting 2976 long getResetIntervalForTest() { 2977 return mResetInterval; 2978 } 2979 2980 @VisibleForTesting 2981 int getMaxIconDimensionForTest() { 2982 return mMaxIconDimension; 2983 } 2984 2985 @VisibleForTesting 2986 CompressFormat getIconPersistFormatForTest() { 2987 return mIconPersistFormat; 2988 } 2989 2990 @VisibleForTesting 2991 int getIconPersistQualityForTest() { 2992 return mIconPersistQuality; 2993 } 2994 2995 @VisibleForTesting 2996 ShortcutPackage getPackageShortcutForTest(String packageName, int userId) { 2997 synchronized (mLock) { 2998 final ShortcutUser user = mUsers.get(userId); 2999 if (user == null) return null; 3000 3001 return user.getAllPackagesForTest().get(packageName); 3002 } 3003 } 3004 3005 @VisibleForTesting 3006 ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) { 3007 synchronized (mLock) { 3008 final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId); 3009 if (pkg == null) return null; 3010 3011 return pkg.findShortcutById(shortcutId); 3012 } 3013 } 3014 3015 /** 3016 * Control whether {@link #verifyStates} should be performed. We always perform it during unit 3017 * tests. 3018 */ 3019 @VisibleForTesting 3020 boolean injectShouldPerformVerification() { 3021 return DEBUG; 3022 } 3023 3024 /** 3025 * Check various internal states and throws if there's any inconsistency. 3026 * This is normally only enabled during unit tests. 3027 */ 3028 final void verifyStates() { 3029 if (injectShouldPerformVerification()) { 3030 verifyStatesInner(); 3031 } 3032 } 3033 3034 private void verifyStatesInner() { 3035 synchronized (this) { 3036 forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates)); 3037 } 3038 } 3039} 3040