1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.ActivityManager.START_SUCCESS;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
22 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
23 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
24 
25 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
26 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
27 
28 import android.app.ActivityOptions;
29 import android.app.IApplicationThread;
30 import android.content.ComponentName;
31 import android.content.ContentResolver;
32 import android.content.Intent;
33 import android.content.pm.ActivityInfo;
34 import android.content.pm.ApplicationInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.ResolveInfo;
37 import android.os.Binder;
38 import android.os.Handler;
39 import android.os.IBinder;
40 import android.os.Looper;
41 import android.os.Message;
42 import android.os.UserHandle;
43 import android.provider.Settings;
44 import android.util.Slog;
45 import android.util.proto.ProtoOutputStream;
46 import android.view.RemoteAnimationAdapter;
47 
48 import com.android.internal.annotations.VisibleForTesting;
49 import com.android.internal.util.ArrayUtils;
50 import com.android.server.am.ActivityManagerService;
51 import com.android.server.am.PendingIntentRecord;
52 import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
53 import com.android.server.wm.ActivityStarter.DefaultFactory;
54 import com.android.server.wm.ActivityStarter.Factory;
55 
56 import java.io.PrintWriter;
57 import java.util.ArrayList;
58 import java.util.List;
59 
60 /**
61  * Controller for delegating activity launches.
62  *
63  * This class' main objective is to take external activity start requests and prepare them into
64  * a series of discrete activity launches that can be handled by an {@link ActivityStarter}. It is
65  * also responsible for handling logic that happens around an activity launch, but doesn't
66  * necessarily influence the activity start. Examples include power hint management, processing
67  * through the pending activity list, and recording home activity launches.
68  */
69 public class ActivityStartController {
70     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStartController" : TAG_ATM;
71 
72     private static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 1;
73 
74     private final ActivityTaskManagerService mService;
75     private final ActivityStackSupervisor mSupervisor;
76 
77     /** Last home activity record we attempted to start. */
78     private ActivityRecord mLastHomeActivityStartRecord;
79 
80     /** Temporary array to capture start activity results */
81     private ActivityRecord[] tmpOutRecord = new ActivityRecord[1];
82 
83     /** The result of the last home activity we attempted to start. */
84     private int mLastHomeActivityStartResult;
85 
86     /** A list of activities that are waiting to launch. */
87     private final ArrayList<ActivityStackSupervisor.PendingActivityLaunch>
88             mPendingActivityLaunches = new ArrayList<>();
89 
90     private final Factory mFactory;
91 
92     private final Handler mHandler;
93 
94     private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry;
95 
96     boolean mCheckedForSetup = false;
97 
98     private final class StartHandler extends Handler {
StartHandler(Looper looper)99         public StartHandler(Looper looper) {
100             super(looper, null, true);
101         }
102 
103         @Override
handleMessage(Message msg)104         public void handleMessage(Message msg) {
105             switch(msg.what) {
106                 case DO_PENDING_ACTIVITY_LAUNCHES_MSG:
107                     synchronized (mService.mGlobalLock) {
108                         doPendingActivityLaunches(true);
109                     }
110                     break;
111             }
112         }
113     }
114 
115     /**
116      * TODO(b/64750076): Capture information necessary for dump and
117      * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
118      * around
119      */
120     private ActivityStarter mLastStarter;
121 
ActivityStartController(ActivityTaskManagerService service)122     ActivityStartController(ActivityTaskManagerService service) {
123         this(service, service.mStackSupervisor,
124                 new DefaultFactory(service, service.mStackSupervisor,
125                     new ActivityStartInterceptor(service, service.mStackSupervisor)));
126     }
127 
128     @VisibleForTesting
ActivityStartController(ActivityTaskManagerService service, ActivityStackSupervisor supervisor, Factory factory)129     ActivityStartController(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
130             Factory factory) {
131         mService = service;
132         mSupervisor = supervisor;
133         mHandler = new StartHandler(mService.mH.getLooper());
134         mFactory = factory;
135         mFactory.setController(this);
136         mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
137                 service.mH);
138     }
139 
140     /**
141      * @return A starter to configure and execute starting an activity. It is valid until after
142      *         {@link ActivityStarter#execute} is invoked. At that point, the starter should be
143      *         considered invalid and no longer modified or used.
144      */
obtainStarter(Intent intent, String reason)145     ActivityStarter obtainStarter(Intent intent, String reason) {
146         return mFactory.obtain().setIntent(intent).setReason(reason);
147     }
148 
onExecutionComplete(ActivityStarter starter)149     void onExecutionComplete(ActivityStarter starter) {
150         if (mLastStarter == null) {
151             mLastStarter = mFactory.obtain();
152         }
153 
154         mLastStarter.set(starter);
155         mFactory.recycle(starter);
156     }
157 
158     /**
159      * TODO(b/64750076): usage of this doesn't seem right. We're making decisions based off the
160      * last starter for an arbitrary task record. Re-evaluate whether we can remove.
161      */
postStartActivityProcessingForLastStarter(ActivityRecord r, int result, ActivityStack targetStack)162     void postStartActivityProcessingForLastStarter(ActivityRecord r, int result,
163             ActivityStack targetStack) {
164         if (mLastStarter == null) {
165             return;
166         }
167 
168         mLastStarter.postStartActivityProcessing(r, result, targetStack);
169     }
170 
startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId)171     void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
172         final ActivityOptions options = ActivityOptions.makeBasic();
173         options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
174         if (!ActivityRecord.isResolverActivity(aInfo.name)) {
175             // The resolver activity shouldn't be put in home stack because when the foreground is
176             // standard type activity, the resolver activity should be put on the top of current
177             // foreground instead of bring home stack to front.
178             options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
179         }
180         options.setLaunchDisplayId(displayId);
181         mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
182                 .setOutActivity(tmpOutRecord)
183                 .setCallingUid(0)
184                 .setActivityInfo(aInfo)
185                 .setActivityOptions(options.toBundle())
186                 .execute();
187         mLastHomeActivityStartRecord = tmpOutRecord[0];
188         final ActivityDisplay display =
189                 mService.mRootActivityContainer.getActivityDisplay(displayId);
190         final ActivityStack homeStack = display != null ? display.getHomeStack() : null;
191         if (homeStack != null && homeStack.mInResumeTopActivity) {
192             // If we are in resume section already, home activity will be initialized, but not
193             // resumed (to avoid recursive resume) and will stay that way until something pokes it
194             // again. We need to schedule another resume.
195             mSupervisor.scheduleResumeTopActivities();
196         }
197     }
198 
199     /**
200      * Starts the "new version setup screen" if appropriate.
201      */
startSetupActivity()202     void startSetupActivity() {
203         // Only do this once per boot.
204         if (mCheckedForSetup) {
205             return;
206         }
207 
208         // We will show this screen if the current one is a different
209         // version than the last one shown, and we are not running in
210         // low-level factory test mode.
211         final ContentResolver resolver = mService.mContext.getContentResolver();
212         if (mService.mFactoryTest != FACTORY_TEST_LOW_LEVEL
213                 && Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
214             mCheckedForSetup = true;
215 
216             // See if we should be showing the platform update setup UI.
217             final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
218             final List<ResolveInfo> ris =
219                     mService.mContext.getPackageManager().queryIntentActivities(intent,
220                             PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA
221                                     | ActivityManagerService.STOCK_PM_FLAGS);
222             if (!ris.isEmpty()) {
223                 final ResolveInfo ri = ris.get(0);
224                 String vers = ri.activityInfo.metaData != null
225                         ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
226                         : null;
227                 if (vers == null && ri.activityInfo.applicationInfo.metaData != null) {
228                     vers = ri.activityInfo.applicationInfo.metaData.getString(
229                             Intent.METADATA_SETUP_VERSION);
230                 }
231                 String lastVers = Settings.Secure.getString(
232                         resolver, Settings.Secure.LAST_SETUP_SHOWN);
233                 if (vers != null && !vers.equals(lastVers)) {
234                     intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
235                     intent.setComponent(new ComponentName(
236                             ri.activityInfo.packageName, ri.activityInfo.name));
237                     obtainStarter(intent, "startSetupActivity")
238                             .setCallingUid(0)
239                             .setActivityInfo(ri.activityInfo)
240                             .execute();
241                 }
242             }
243         }
244     }
245 
246     /**
247      * If {@code validateIncomingUser} is true, check {@code targetUserId} against the real calling
248      * user ID inferred from {@code realCallingUid}, then return the resolved user-id, taking into
249      * account "current user", etc.
250      *
251      * If {@code validateIncomingUser} is false, it skips the above check, but instead
252      * ensures {@code targetUserId} is a real user ID and not a special user ID such as
253      * {@link android.os.UserHandle#USER_ALL}, etc.
254      */
checkTargetUser(int targetUserId, boolean validateIncomingUser, int realCallingPid, int realCallingUid, String reason)255     int checkTargetUser(int targetUserId, boolean validateIncomingUser,
256             int realCallingPid, int realCallingUid, String reason) {
257         if (validateIncomingUser) {
258             return mService.handleIncomingUser(
259                     realCallingPid, realCallingUid, targetUserId, reason);
260         } else {
261             mService.mAmInternal.ensureNotSpecialUser(targetUserId);
262             return targetUserId;
263         }
264     }
265 
startActivityInPackage(int uid, int realCallingPid, int realCallingUid, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, SafeActivityOptions options, int userId, TaskRecord inTask, String reason, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart)266     final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
267             String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
268             String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
269             int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
270             PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
271 
272         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
273                 reason);
274 
275         // TODO: Switch to user app stacks here.
276         return obtainStarter(intent, reason)
277                 .setCallingUid(uid)
278                 .setRealCallingPid(realCallingPid)
279                 .setRealCallingUid(realCallingUid)
280                 .setCallingPackage(callingPackage)
281                 .setResolvedType(resolvedType)
282                 .setResultTo(resultTo)
283                 .setResultWho(resultWho)
284                 .setRequestCode(requestCode)
285                 .setStartFlags(startFlags)
286                 .setActivityOptions(options)
287                 .setMayWait(userId)
288                 .setInTask(inTask)
289                 .setOriginatingPendingIntent(originatingPendingIntent)
290                 .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
291                 .execute();
292     }
293 
294     /**
295      * Start intents as a package.
296      *
297      * @param uid Make a call as if this UID did.
298      * @param callingPackage Make a call as if this package did.
299      * @param intents Intents to start.
300      * @param userId Start the intents on this user.
301      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
302      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
303      *        null if not originated by PendingIntent
304      */
startActivitiesInPackage(int uid, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart)305     final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
306             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
307             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
308             boolean allowBackgroundActivityStart) {
309         return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
310             callingPackage, intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
311             originatingPendingIntent, allowBackgroundActivityStart);
312     }
313 
314     /**
315      * Start intents as a package.
316      *
317      * @param uid Make a call as if this UID did.
318      * @param realCallingPid PID of the real caller.
319      * @param realCallingUid UID of the real caller.
320      * @param callingPackage Make a call as if this package did.
321      * @param intents Intents to start.
322      * @param userId Start the intents on this user.
323      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
324      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
325      *        null if not originated by PendingIntent
326      */
startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart)327     final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
328             String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
329             SafeActivityOptions options, int userId, boolean validateIncomingUser,
330             PendingIntentRecord originatingPendingIntent,
331             boolean allowBackgroundActivityStart) {
332 
333         final String reason = "startActivityInPackage";
334 
335         userId = checkTargetUser(userId, validateIncomingUser, Binder.getCallingPid(),
336                 Binder.getCallingUid(), reason);
337 
338         // TODO: Switch to user app stacks here.
339         return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage, intents,
340                 resolvedTypes, resultTo, options, userId, reason, originatingPendingIntent,
341                 allowBackgroundActivityStart);
342     }
343 
startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid, int incomingRealCallingUid, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, String reason, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart)344     int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
345             int incomingRealCallingUid, String callingPackage, Intent[] intents,
346             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
347             int userId, String reason, PendingIntentRecord originatingPendingIntent,
348             boolean allowBackgroundActivityStart) {
349         if (intents == null) {
350             throw new NullPointerException("intents is null");
351         }
352         if (resolvedTypes == null) {
353             throw new NullPointerException("resolvedTypes is null");
354         }
355         if (intents.length != resolvedTypes.length) {
356             throw new IllegalArgumentException("intents are length different than resolvedTypes");
357         }
358 
359         final int realCallingPid = incomingRealCallingPid != 0
360                 ? incomingRealCallingPid
361                 : Binder.getCallingPid();
362         final int realCallingUid = incomingRealCallingUid != -1
363                 ? incomingRealCallingUid
364                 : Binder.getCallingUid();
365 
366         int callingPid;
367         if (callingUid >= 0) {
368             callingPid = -1;
369         } else if (caller == null) {
370             callingPid = realCallingPid;
371             callingUid = realCallingUid;
372         } else {
373             callingPid = callingUid = -1;
374         }
375         final long origId = Binder.clearCallingIdentity();
376         try {
377             intents = ArrayUtils.filterNotNull(intents, Intent[]::new);
378             final ActivityStarter[] starters = new ActivityStarter[intents.length];
379             // Do not hold WM global lock on this loop because when resolving intent, it may
380             // potentially acquire activity manager lock that leads to deadlock.
381             for (int i = 0; i < intents.length; i++) {
382                 Intent intent = intents[i];
383 
384                 // Refuse possible leaked file descriptors.
385                 if (intent.hasFileDescriptors()) {
386                     throw new IllegalArgumentException("File descriptors passed in Intent");
387                 }
388 
389                 // Don't modify the client's object!
390                 intent = new Intent(intent);
391 
392                 // Collect information about the target of the Intent.
393                 ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i],
394                         0 /* startFlags */, null /* profilerInfo */, userId,
395                         ActivityStarter.computeResolveFilterUid(
396                                 callingUid, realCallingUid, UserHandle.USER_NULL));
397                 aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
398 
399                 if (aInfo != null && (aInfo.applicationInfo.privateFlags
400                         & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
401                     throw new IllegalArgumentException("FLAG_CANT_SAVE_STATE not supported here");
402                 }
403 
404                 final boolean top = i == intents.length - 1;
405                 final SafeActivityOptions checkedOptions = top
406                         ? options
407                         : null;
408                 starters[i] = obtainStarter(intent, reason)
409                         .setCaller(caller)
410                         .setResolvedType(resolvedTypes[i])
411                         .setActivityInfo(aInfo)
412                         .setResultTo(resultTo)
413                         .setRequestCode(-1)
414                         .setCallingPid(callingPid)
415                         .setCallingUid(callingUid)
416                         .setCallingPackage(callingPackage)
417                         .setRealCallingPid(realCallingPid)
418                         .setRealCallingUid(realCallingUid)
419                         .setActivityOptions(checkedOptions)
420                         .setComponentSpecified(intent.getComponent() != null)
421 
422                         // Top activity decides on animation being run, so we allow only for the
423                         // top one as otherwise an activity below might consume it.
424                         .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
425                         .setOriginatingPendingIntent(originatingPendingIntent)
426                         .setAllowBackgroundActivityStart(allowBackgroundActivityStart);
427             }
428 
429             final ActivityRecord[] outActivity = new ActivityRecord[1];
430             // Lock the loop to ensure the activities launched in a sequence.
431             synchronized (mService.mGlobalLock) {
432                 for (int i = 0; i < starters.length; i++) {
433                     final int startResult = starters[i].setOutActivity(outActivity).execute();
434                     if (startResult < START_SUCCESS) {
435                         // Abort by error result and recycle unused starters.
436                         for (int j = i + 1; j < starters.length; j++) {
437                             mFactory.recycle(starters[j]);
438                         }
439                         return startResult;
440                     }
441                     resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
442                 }
443             }
444         } finally {
445             Binder.restoreCallingIdentity(origId);
446         }
447 
448         return START_SUCCESS;
449     }
450 
schedulePendingActivityLaunches(long delayMs)451     void schedulePendingActivityLaunches(long delayMs) {
452         mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
453         Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
454         mHandler.sendMessageDelayed(msg, delayMs);
455     }
456 
doPendingActivityLaunches(boolean doResume)457     void doPendingActivityLaunches(boolean doResume) {
458         while (!mPendingActivityLaunches.isEmpty()) {
459             final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
460             final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
461             final ActivityStarter starter = obtainStarter(null /* intent */,
462                     "pendingActivityLaunch");
463             try {
464                 starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
465                         resume, pal.r.pendingOptions, null);
466             } catch (Exception e) {
467                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
468                 pal.sendErrorResult(e.getMessage());
469             }
470         }
471     }
472 
addPendingActivityLaunch(PendingActivityLaunch launch)473     void addPendingActivityLaunch(PendingActivityLaunch launch) {
474         mPendingActivityLaunches.add(launch);
475     }
476 
clearPendingActivityLaunches(String packageName)477     boolean clearPendingActivityLaunches(String packageName) {
478         final int pendingLaunches = mPendingActivityLaunches.size();
479 
480         for (int palNdx = pendingLaunches - 1; palNdx >= 0; --palNdx) {
481             final PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
482             final ActivityRecord r = pal.r;
483             if (r != null && r.packageName.equals(packageName)) {
484                 mPendingActivityLaunches.remove(palNdx);
485             }
486         }
487         return mPendingActivityLaunches.size() < pendingLaunches;
488     }
489 
registerRemoteAnimationForNextActivityStart(String packageName, RemoteAnimationAdapter adapter)490     void registerRemoteAnimationForNextActivityStart(String packageName,
491             RemoteAnimationAdapter adapter) {
492         mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
493     }
494 
getPendingRemoteAnimationRegistry()495     PendingRemoteAnimationRegistry getPendingRemoteAnimationRegistry() {
496         return mPendingRemoteAnimationRegistry;
497     }
498 
dump(PrintWriter pw, String prefix, String dumpPackage)499     void dump(PrintWriter pw, String prefix, String dumpPackage) {
500         pw.print(prefix);
501         pw.print("mLastHomeActivityStartResult=");
502         pw.println(mLastHomeActivityStartResult);
503 
504         if (mLastHomeActivityStartRecord != null) {
505             pw.print(prefix);
506             pw.println("mLastHomeActivityStartRecord:");
507             mLastHomeActivityStartRecord.dump(pw, prefix + "  ");
508         }
509 
510         final boolean dumpPackagePresent = dumpPackage != null;
511 
512         if (mLastStarter != null) {
513             final boolean dump = !dumpPackagePresent
514                     || mLastStarter.relatedToPackage(dumpPackage)
515                     || (mLastHomeActivityStartRecord != null
516                             && dumpPackage.equals(mLastHomeActivityStartRecord.packageName));
517 
518             if (dump) {
519                 pw.print(prefix);
520                 mLastStarter.dump(pw, prefix + "  ");
521 
522                 if (dumpPackagePresent) {
523                     return;
524                 }
525             }
526         }
527 
528         if (dumpPackagePresent) {
529             pw.print(prefix);
530             pw.println("(nothing)");
531         }
532     }
533 
writeToProto(ProtoOutputStream proto, long fieldId)534     public void writeToProto(ProtoOutputStream proto, long fieldId) {
535         for (PendingActivityLaunch activity: mPendingActivityLaunches) {
536             activity.r.writeIdentifierToProto(proto, fieldId);
537         }
538     }
539 }
540