Loading core/java/android/content/Intent.java +16 −0 Original line number Diff line number Diff line Loading @@ -2218,6 +2218,22 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE"; /** * Broadcast Action: preferred activities have changed *explicitly*. * * <p>Note there are cases where a preferred activity is invalidated *implicitly*, e.g. * when an app is installed or uninstalled, but in such cases this broadcast will *not* * be sent. * * {@link #EXTRA_USER_HANDLE} contains the user ID in question. * * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PREFERRED_ACTIVITY_CHANGED = "android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED"; /** * Broadcast Action: The current system wallpaper has changed. See * {@link android.app.WallpaperManager} for retrieving the new wallpaper. Loading core/res/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" /> <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" /> <protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" /> <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" /> <protected-broadcast android:name="android.intent.action.UID_REMOVED" /> <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" /> <protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" /> Loading services/core/java/com/android/server/pm/PackageManagerService.java +24 −0 Original line number Diff line number Diff line Loading @@ -16688,9 +16688,28 @@ public class PackageManagerService extends IPackageManager.Stub { filter.dump(new LogPrinter(Log.INFO, TAG), " "); pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); } } private void postPreferredActivityChangedBroadcast(int userId) { mHandler.post(() -> { final IActivityManager am = ActivityManagerNative.getDefault(); if (am == null) { return; } final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); try { am.broadcastIntent(null, intent, null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, userId); } catch (RemoteException e) { } }); } @Override public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) { Loading Loading @@ -16842,6 +16861,9 @@ public class PackageManagerService extends IPackageManager.Stub { changed = true; } } if (changed) { postPreferredActivityChangedBroadcast(userId); } return changed; } Loading Loading @@ -16955,6 +16977,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter( new PersistentPreferredActivity(filter, activity)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); } } Loading Loading @@ -16997,6 +17020,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (changed) { scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); } } } services/core/java/com/android/server/pm/ShortcutService.java +40 −16 Original line number Diff line number Diff line Loading @@ -385,6 +385,12 @@ public class ShortcutService extends IShortcutService.Stub { mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL, packageFilter, null, mHandler); final IntentFilter preferedActivityFilter = new IntentFilter(); preferedActivityFilter.addAction(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED); preferedActivityFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL, preferedActivityFilter, null, mHandler); final IntentFilter localeFilter = new IntentFilter(); localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED); localeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); Loading Loading @@ -1923,7 +1929,12 @@ public class ShortcutService extends IShortcutService.Stub { // We override this method in unit tests to do a simpler check. boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) { final long start = injectElapsedRealtime(); try { return hasShortcutHostPermissionInner(callingPackage, userId); } finally { logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start); } } // This method is extracted so we can directly call this method from unit tests, Loading @@ -1931,15 +1942,22 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { synchronized (mLock) { final long start = injectElapsedRealtime(); final ShortcutUser user = getUserShortcutsLocked(userId); // Always trust the in-memory cache. final ComponentName cached = user.getCachedLauncher(); if (cached != null) { if (cached.getPackageName().equals(callingPackage)) { return true; } } // If the cached one doesn't match, then go ahead final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); // Default launcher from package manager. final long startGetHomeActivitiesAsUser = injectElapsedRealtime(); final ComponentName defaultLauncher = injectPackageManagerInternal() final ComponentName defaultLauncher = mPackageManagerInternal .getHomeActivitiesAsUser(allHomeCandidates, userId); logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser); Loading @@ -1950,7 +1968,7 @@ public class ShortcutService extends IShortcutService.Stub { Slog.v(TAG, "Default launcher from PM: " + detected); } } else { detected = user.getDefaultLauncherComponent(); detected = user.getLastKnownLauncher(); if (detected != null) { if (injectIsActivityEnabledAndExported(detected, userId)) { Loading @@ -1960,7 +1978,7 @@ public class ShortcutService extends IShortcutService.Stub { } else { Slog.w(TAG, "Cached launcher " + detected + " no longer exists"); detected = null; user.setDefaultLauncherComponent(null); user.clearLauncher(); } } } Loading Loading @@ -1991,13 +2009,13 @@ public class ShortcutService extends IShortcutService.Stub { lastPriority = ri.priority; } } logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start); // Update the cache. user.setLauncher(detected); if (detected != null) { if (DEBUG) { Slog.v(TAG, "Detected launcher: " + detected); } user.setDefaultLauncherComponent(detected); return detected.getPackageName().equals(callingPackage); } else { // Default launcher not found. Loading Loading @@ -2358,6 +2376,17 @@ public class ShortcutService extends IShortcutService.Stub { return; } // Whenever we get one of those package broadcasts, or get // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache. synchronized (mLock) { final ShortcutUser user = getUserShortcutsLocked(userId); user.clearLauncher(); } if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) { // Nothing farther to do. return; } final Uri intentUri = intent.getData(); final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart() : null; Loading Loading @@ -3260,7 +3289,7 @@ public class ShortcutService extends IShortcutService.Stub { private void clearLauncher() { synchronized (mLock) { getUserShortcutsLocked(mUserId).setDefaultLauncherComponent(null); getUserShortcutsLocked(mUserId).forceClearLauncher(); } } Loading @@ -3270,7 +3299,7 @@ public class ShortcutService extends IShortcutService.Stub { hasShortcutHostPermissionInner("-", mUserId); getOutPrintWriter().println("Launcher: " + getUserShortcutsLocked(mUserId).getDefaultLauncherComponent()); + getUserShortcutsLocked(mUserId).getLastKnownLauncher()); } } Loading Loading @@ -3394,11 +3423,6 @@ public class ShortcutService extends IShortcutService.Stub { } } @VisibleForTesting PackageManagerInternal injectPackageManagerInternal() { return mPackageManagerInternal; } File getUserBitmapFilePath(@UserIdInt int userId) { return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS); } Loading Loading @@ -3482,7 +3506,7 @@ public class ShortcutService extends IShortcutService.Stub { } private void verifyStatesInner() { synchronized (this) { synchronized (mLock) { forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates)); } } Loading services/core/java/com/android/server/pm/ShortcutUser.java +49 −12 Original line number Diff line number Diff line Loading @@ -103,8 +103,15 @@ class ShortcutUser { private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>(); /** Default launcher that can access the launcher apps APIs. */ private ComponentName mDefaultLauncherComponent; /** * Last known launcher. It's used when the default launcher isn't set in PM -- i.e. * when getHomeActivitiesAsUser() return null. We need it so that in this situation the * previously default launcher can still access shortcuts. */ private ComponentName mLastKnownLauncher; /** In-memory-cached default launcher. */ private ComponentName mCachedLauncher; private String mKnownLocales; Loading Loading @@ -304,8 +311,7 @@ class ShortcutUser { ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME, mLastAppScanTime); ShortcutService.writeTagValue(out, TAG_LAUNCHER, mDefaultLauncherComponent); ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher); // Can't use forEachPackageItem due to the checked exceptions. { Loading Loading @@ -364,7 +370,7 @@ class ShortcutUser { if (depth == outerDepth + 1) { switch (tag) { case TAG_LAUNCHER: { ret.mDefaultLauncherComponent = ShortcutService.parseComponentNameAttribute( ret.mLastKnownLauncher = ShortcutService.parseComponentNameAttribute( parser, ATTR_VALUE); continue; } Loading @@ -389,18 +395,44 @@ class ShortcutUser { return ret; } public ComponentName getDefaultLauncherComponent() { return mDefaultLauncherComponent; public ComponentName getLastKnownLauncher() { return mLastKnownLauncher; } public void setLauncher(ComponentName launcherComponent) { setLauncher(launcherComponent, /* allowPurgeLastKnown */ false); } public void setDefaultLauncherComponent(ComponentName launcherComponent) { if (Objects.equal(mDefaultLauncherComponent, launcherComponent)) { /** Clears the launcher information without clearing the last known one */ public void clearLauncher() { setLauncher(null); } /** * Clears the launcher information *with(* clearing the last known one; we do this witl * "cmd shortcut clear-default-launcher". */ public void forceClearLauncher() { setLauncher(null, /* allowPurgeLastKnown */ true); } private void setLauncher(ComponentName launcherComponent, boolean allowPurgeLastKnown) { mCachedLauncher = launcherComponent; // Always update the in-memory cache. if (Objects.equal(mLastKnownLauncher, launcherComponent)) { return; } mDefaultLauncherComponent = launcherComponent; if (!allowPurgeLastKnown && launcherComponent == null) { return; } mLastKnownLauncher = launcherComponent; mService.scheduleSaveUser(mUserId); } public ComponentName getCachedLauncher() { return mCachedLauncher; } public void resetThrottling() { for (int i = mPackages.size() - 1; i >= 0; i--) { mPackages.valueAt(i).resetThrottling(); Loading @@ -422,8 +454,13 @@ class ShortcutUser { prefix += prefix + " "; pw.print(prefix); pw.print("Default launcher: "); pw.print(mDefaultLauncherComponent); pw.print("Cached launcher: "); pw.print(mCachedLauncher); pw.println(); pw.print(prefix); pw.print("Last known launcher: "); pw.print(mLastKnownLauncher); pw.println(); for (int i = 0; i < mLaunchers.size(); i++) { Loading Loading
core/java/android/content/Intent.java +16 −0 Original line number Diff line number Diff line Loading @@ -2218,6 +2218,22 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE"; /** * Broadcast Action: preferred activities have changed *explicitly*. * * <p>Note there are cases where a preferred activity is invalidated *implicitly*, e.g. * when an app is installed or uninstalled, but in such cases this broadcast will *not* * be sent. * * {@link #EXTRA_USER_HANDLE} contains the user ID in question. * * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PREFERRED_ACTIVITY_CHANGED = "android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED"; /** * Broadcast Action: The current system wallpaper has changed. See * {@link android.app.WallpaperManager} for retrieving the new wallpaper. Loading
core/res/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" /> <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" /> <protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" /> <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" /> <protected-broadcast android:name="android.intent.action.UID_REMOVED" /> <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" /> <protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" /> Loading
services/core/java/com/android/server/pm/PackageManagerService.java +24 −0 Original line number Diff line number Diff line Loading @@ -16688,9 +16688,28 @@ public class PackageManagerService extends IPackageManager.Stub { filter.dump(new LogPrinter(Log.INFO, TAG), " "); pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); } } private void postPreferredActivityChangedBroadcast(int userId) { mHandler.post(() -> { final IActivityManager am = ActivityManagerNative.getDefault(); if (am == null) { return; } final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); try { am.broadcastIntent(null, intent, null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, userId); } catch (RemoteException e) { } }); } @Override public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) { Loading Loading @@ -16842,6 +16861,9 @@ public class PackageManagerService extends IPackageManager.Stub { changed = true; } } if (changed) { postPreferredActivityChangedBroadcast(userId); } return changed; } Loading Loading @@ -16955,6 +16977,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter( new PersistentPreferredActivity(filter, activity)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); } } Loading Loading @@ -16997,6 +17020,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (changed) { scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); } } }
services/core/java/com/android/server/pm/ShortcutService.java +40 −16 Original line number Diff line number Diff line Loading @@ -385,6 +385,12 @@ public class ShortcutService extends IShortcutService.Stub { mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL, packageFilter, null, mHandler); final IntentFilter preferedActivityFilter = new IntentFilter(); preferedActivityFilter.addAction(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED); preferedActivityFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL, preferedActivityFilter, null, mHandler); final IntentFilter localeFilter = new IntentFilter(); localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED); localeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); Loading Loading @@ -1923,7 +1929,12 @@ public class ShortcutService extends IShortcutService.Stub { // We override this method in unit tests to do a simpler check. boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) { final long start = injectElapsedRealtime(); try { return hasShortcutHostPermissionInner(callingPackage, userId); } finally { logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start); } } // This method is extracted so we can directly call this method from unit tests, Loading @@ -1931,15 +1942,22 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) { synchronized (mLock) { final long start = injectElapsedRealtime(); final ShortcutUser user = getUserShortcutsLocked(userId); // Always trust the in-memory cache. final ComponentName cached = user.getCachedLauncher(); if (cached != null) { if (cached.getPackageName().equals(callingPackage)) { return true; } } // If the cached one doesn't match, then go ahead final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); // Default launcher from package manager. final long startGetHomeActivitiesAsUser = injectElapsedRealtime(); final ComponentName defaultLauncher = injectPackageManagerInternal() final ComponentName defaultLauncher = mPackageManagerInternal .getHomeActivitiesAsUser(allHomeCandidates, userId); logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser); Loading @@ -1950,7 +1968,7 @@ public class ShortcutService extends IShortcutService.Stub { Slog.v(TAG, "Default launcher from PM: " + detected); } } else { detected = user.getDefaultLauncherComponent(); detected = user.getLastKnownLauncher(); if (detected != null) { if (injectIsActivityEnabledAndExported(detected, userId)) { Loading @@ -1960,7 +1978,7 @@ public class ShortcutService extends IShortcutService.Stub { } else { Slog.w(TAG, "Cached launcher " + detected + " no longer exists"); detected = null; user.setDefaultLauncherComponent(null); user.clearLauncher(); } } } Loading Loading @@ -1991,13 +2009,13 @@ public class ShortcutService extends IShortcutService.Stub { lastPriority = ri.priority; } } logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start); // Update the cache. user.setLauncher(detected); if (detected != null) { if (DEBUG) { Slog.v(TAG, "Detected launcher: " + detected); } user.setDefaultLauncherComponent(detected); return detected.getPackageName().equals(callingPackage); } else { // Default launcher not found. Loading Loading @@ -2358,6 +2376,17 @@ public class ShortcutService extends IShortcutService.Stub { return; } // Whenever we get one of those package broadcasts, or get // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache. synchronized (mLock) { final ShortcutUser user = getUserShortcutsLocked(userId); user.clearLauncher(); } if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) { // Nothing farther to do. return; } final Uri intentUri = intent.getData(); final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart() : null; Loading Loading @@ -3260,7 +3289,7 @@ public class ShortcutService extends IShortcutService.Stub { private void clearLauncher() { synchronized (mLock) { getUserShortcutsLocked(mUserId).setDefaultLauncherComponent(null); getUserShortcutsLocked(mUserId).forceClearLauncher(); } } Loading @@ -3270,7 +3299,7 @@ public class ShortcutService extends IShortcutService.Stub { hasShortcutHostPermissionInner("-", mUserId); getOutPrintWriter().println("Launcher: " + getUserShortcutsLocked(mUserId).getDefaultLauncherComponent()); + getUserShortcutsLocked(mUserId).getLastKnownLauncher()); } } Loading Loading @@ -3394,11 +3423,6 @@ public class ShortcutService extends IShortcutService.Stub { } } @VisibleForTesting PackageManagerInternal injectPackageManagerInternal() { return mPackageManagerInternal; } File getUserBitmapFilePath(@UserIdInt int userId) { return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS); } Loading Loading @@ -3482,7 +3506,7 @@ public class ShortcutService extends IShortcutService.Stub { } private void verifyStatesInner() { synchronized (this) { synchronized (mLock) { forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates)); } } Loading
services/core/java/com/android/server/pm/ShortcutUser.java +49 −12 Original line number Diff line number Diff line Loading @@ -103,8 +103,15 @@ class ShortcutUser { private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>(); /** Default launcher that can access the launcher apps APIs. */ private ComponentName mDefaultLauncherComponent; /** * Last known launcher. It's used when the default launcher isn't set in PM -- i.e. * when getHomeActivitiesAsUser() return null. We need it so that in this situation the * previously default launcher can still access shortcuts. */ private ComponentName mLastKnownLauncher; /** In-memory-cached default launcher. */ private ComponentName mCachedLauncher; private String mKnownLocales; Loading Loading @@ -304,8 +311,7 @@ class ShortcutUser { ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME, mLastAppScanTime); ShortcutService.writeTagValue(out, TAG_LAUNCHER, mDefaultLauncherComponent); ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher); // Can't use forEachPackageItem due to the checked exceptions. { Loading Loading @@ -364,7 +370,7 @@ class ShortcutUser { if (depth == outerDepth + 1) { switch (tag) { case TAG_LAUNCHER: { ret.mDefaultLauncherComponent = ShortcutService.parseComponentNameAttribute( ret.mLastKnownLauncher = ShortcutService.parseComponentNameAttribute( parser, ATTR_VALUE); continue; } Loading @@ -389,18 +395,44 @@ class ShortcutUser { return ret; } public ComponentName getDefaultLauncherComponent() { return mDefaultLauncherComponent; public ComponentName getLastKnownLauncher() { return mLastKnownLauncher; } public void setLauncher(ComponentName launcherComponent) { setLauncher(launcherComponent, /* allowPurgeLastKnown */ false); } public void setDefaultLauncherComponent(ComponentName launcherComponent) { if (Objects.equal(mDefaultLauncherComponent, launcherComponent)) { /** Clears the launcher information without clearing the last known one */ public void clearLauncher() { setLauncher(null); } /** * Clears the launcher information *with(* clearing the last known one; we do this witl * "cmd shortcut clear-default-launcher". */ public void forceClearLauncher() { setLauncher(null, /* allowPurgeLastKnown */ true); } private void setLauncher(ComponentName launcherComponent, boolean allowPurgeLastKnown) { mCachedLauncher = launcherComponent; // Always update the in-memory cache. if (Objects.equal(mLastKnownLauncher, launcherComponent)) { return; } mDefaultLauncherComponent = launcherComponent; if (!allowPurgeLastKnown && launcherComponent == null) { return; } mLastKnownLauncher = launcherComponent; mService.scheduleSaveUser(mUserId); } public ComponentName getCachedLauncher() { return mCachedLauncher; } public void resetThrottling() { for (int i = mPackages.size() - 1; i >= 0; i--) { mPackages.valueAt(i).resetThrottling(); Loading @@ -422,8 +454,13 @@ class ShortcutUser { prefix += prefix + " "; pw.print(prefix); pw.print("Default launcher: "); pw.print(mDefaultLauncherComponent); pw.print("Cached launcher: "); pw.print(mCachedLauncher); pw.println(); pw.print(prefix); pw.print("Last known launcher: "); pw.print(mLastKnownLauncher); pw.println(); for (int i = 0; i < mLaunchers.size(); i++) { Loading