ActivityStarter-startActivityMayWait

###源码位置com.android.server.am.ActivityStarter.java

class ActivityStarter {}
这个类没有继承别的类,也没有实现任何接口
这个类的定义如下:
/**
 * Controller for interpreting how and then launching activities.
 *
 * This class collects all the logic for determining how an intent and flags should be turned into
 * an activity and associated task and stack.
 */
 解释如何发布活动的控制器。
 这个类收集所有逻辑,以确定如何根据intent和flags转换为一个对应的Activity,并且和堆栈关联在一起。
 大致是个帮助类。

查看startActivityMayWait方法,这是个很长的方法,需要很大的勇气。
先解释一下为什么叫MayWait,因为调用startActivity是可能要等待结果的startActivityForResult,那就要挂起调用者
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,
Bundle bOptions, boolean ignoreTargetSecurity, int userId,
IActivityContainer iContainer, TaskRecord inTask) {

    // Refuse possible leaked file descriptors
    //拒绝可能泄露的文件描述符
    if (intent != null && intent.hasFileDescriptors()) {
        throw new IllegalArgumentException("File descriptors passed in Intent");
    }
    mSupervisor.mActivityMetricsLogger.notifyActivityLaunching();
    boolean componentSpecified = intent.getComponent() != null;

    // Save a copy in case ephemeral needs it
    //保存一个副本以备临时需要
    final Intent ephemeralIntent = new Intent(intent);
    // Don't modify the client's object!
    //不要修改客户端的对象!
    intent = new Intent(intent);

    ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
    if (rInfo == null) {
        UserInfo userInfo = mSupervisor.getUserInfo(userId);
        if (userInfo != null && userInfo.isManagedProfile()) {
            // Special case for managed profiles, if attempting to launch non-cryto aware
            // app in a locked managed profile from an unlocked parent allow it to resolve
            // as user will be sent via confirm credentials to unlock the profile.
            UserManager userManager = UserManager.get(mService.mContext);
            boolean profileLockedAndParentUnlockingOrUnlocked = false;
            long token = Binder.clearCallingIdentity();
            try {
                UserInfo parent = userManager.getProfileParent(userId);
                profileLockedAndParentUnlockingOrUnlocked = (parent != null)
                        && userManager.isUserUnlockingOrUnlocked(parent.id)
                        && !userManager.isUserUnlockingOrUnlocked(userId);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
            if (profileLockedAndParentUnlockingOrUnlocked) {
                rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                        PackageManager.MATCH_DIRECT_BOOT_AWARE
                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
            }
        }
    }
    // Collect information about the target of the Intent.
    //收集intent有关的信息
    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

    ActivityOptions options = ActivityOptions.fromBundle(bOptions);
    ActivityStackSupervisor.ActivityContainer container =
            (ActivityStackSupervisor.ActivityContainer)iContainer;
    synchronized (mService) {
        if (container != null && container.mParentActivity != null &&
                container.mParentActivity.state != RESUMED) {
            // Cannot start a child activity if the parent is not resumed.
            return ActivityManager.START_CANCELED;
        }
        final int realCallingPid = Binder.getCallingPid();
        final int realCallingUid = Binder.getCallingUid();
        int callingPid;
        if (callingUid >= 0) {
            callingPid = -1;
        } else if (caller == null) {
            callingPid = realCallingPid;
            callingUid = realCallingUid;
        } else {
            callingPid = callingUid = -1;
        }

        final ActivityStack stack;
        if (container == null || container.mStack.isOnHomeDisplay()) {
            stack = mSupervisor.mFocusedStack;
        } else {
            stack = container.mStack;
        }
        stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0;
        if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                "Starting activity when config will change = " + stack.mConfigWillChange);

        final long origId = Binder.clearCallingIdentity();

        if (aInfo != null &&
                (aInfo.applicationInfo.privateFlags
                        & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
            // This may be a heavy-weight process!  Check to see if we already
            // have another, different heavy-weight process running.
            if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
                final ProcessRecord heavy = mService.mHeavyWeightProcess;
                if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
                        || !heavy.processName.equals(aInfo.processName))) {
                    int appCallingUid = callingUid;
                    if (caller != null) {
                        ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
                        if (callerApp != null) {
                            appCallingUid = callerApp.info.uid;
                        } else {
                            Slog.w(TAG, "Unable to find app for caller " + caller
                                    + " (pid=" + callingPid + ") when starting: "
                                    + intent.toString());
                            ActivityOptions.abort(options);
                            return ActivityManager.START_PERMISSION_DENIED;
                        }
                    }

                    IIntentSender target = mService.getIntentSenderLocked(
                            ActivityManager.INTENT_SENDER_ACTIVITY, "android",
                            appCallingUid, userId, null, null, 0, new Intent[] { intent },
                            new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
                                    | PendingIntent.FLAG_ONE_SHOT, null);

                    Intent newIntent = new Intent();
                    if (requestCode >= 0) {
                        // Caller is requesting a result.
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
                    }
                    newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
                            new IntentSender(target));
                    if (heavy.activities.size() > 0) {
                        ActivityRecord hist = heavy.activities.get(0);
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
                                hist.packageName);
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
                                hist.task.taskId);
                    }
                    newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
                            aInfo.packageName);
                    newIntent.setFlags(intent.getFlags());
                    newIntent.setClassName("android",
                            HeavyWeightSwitcherActivity.class.getName());
                    intent = newIntent;
                    resolvedType = null;
                    caller = null;
                    callingUid = Binder.getCallingUid();
                    callingPid = Binder.getCallingPid();
                    componentSpecified = true;
                    rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId);
                    aInfo = rInfo != null ? rInfo.activityInfo : null;
                    if (aInfo != null) {
                        aInfo = mService.getActivityInfoForUser(aInfo, userId);
                    }
                }
            }
        }

        final ActivityRecord[] outRecord = new ActivityRecord[1];

        //重点看这里
        int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                aInfo, rInfo, voiceSession, voiceInteractor,
                resultTo, resultWho, requestCode, callingPid,
                callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                options, ignoreTargetSecurity, componentSpecified, outRecord, container,
                inTask);

        Binder.restoreCallingIdentity(origId);

        if (stack.mConfigWillChange) {
            // If the caller also wants to switch to a new configuration,
            // do so now.  This allows a clean switch, as we are waiting
            // for the current activity to pause (so we will not destroy
            // it), and have not yet started the next activity.
            mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                    "updateConfiguration()");
            stack.mConfigWillChange = false;
            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                    "Updating to new configuration after starting activity.");
            mService.updateConfigurationLocked(config, null, false);
        }

        if (outResult != null) {
            outResult.result = res;
            if (res == ActivityManager.START_SUCCESS) {
                mSupervisor.mWaitingActivityLaunched.add(outResult);
                do {
                    try {
                        mService.wait();
                    } catch (InterruptedException e) {
                    }
                } while (outResult.result != START_TASK_TO_FRONT
                        && !outResult.timeout && outResult.who == null);
                if (outResult.result == START_TASK_TO_FRONT) {
                    res = START_TASK_TO_FRONT;
                }
            }
            if (res == START_TASK_TO_FRONT) {
                ActivityRecord r = stack.topRunningActivityLocked();
                if (r.nowVisible && r.state == RESUMED) {
                    outResult.timeout = false;
                    outResult.who = new ComponentName(r.info.packageName, r.info.name);
                    outResult.totalTime = 0;
                    outResult.thisTime = 0;
                } else {
                    outResult.thisTime = SystemClock.uptimeMillis();
                    mSupervisor.mWaitingActivityVisible.add(outResult);
                    do {
                        try {
                            mService.wait();
                        } catch (InterruptedException e) {
                        }
                    } while (!outResult.timeout && outResult.who == null);
                }
            }
        }

        final ActivityRecord launchedActivity = mReusedActivity != null
                ? mReusedActivity : outRecord[0];
        mSupervisor.mActivityMetricsLogger.notifyActivityLaunched(res, launchedActivity);
        return res;
    }
}

重点

  int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                        aInfo, rInfo, voiceSession, voiceInteractor,
                        resultTo, resultWho, requestCode, callingPid,
                        callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                        options, ignoreTargetSecurity, componentSpecified, outRecord, container,
                        inTask);

  转到startActivityLocked方法

  查看startActivityLocked方法
  Locked意思是调用者需要保证加锁保护,不能重复调用,
  在startActivityMayWait中,是采用mService对象,
  也就是构造ActivityStackSupervisor时传进来的ActivityManagerService的对象。

 前面先做一系列检查的工作,比如权限,比如Intent防火墙检查。
 准备做好之后,就new一个ActivityRecord,用于存储Activity的各种状态和历史信息。
 然后,通过getFocusedStack方法获取当前获取焦点的ActivityStack。
 ActivityStackSupervisor中的mFocusedStack中保存了当前的前台ActivityStack。
 下面就准备切换新进程了,先判断一下是否可以切换,如果处于通话中界面等无法马上切换的情况。
 通过ActivityManagerService的checkAppSwitchAllowedLocked方法来做检查,
 如果当前不允许做进程切换,就先存到PendingActivityLaunch的列表中,等待以后有机会再调用。
 如果允许做切换,那么先检查一下当前是否有以前的等待任务,如果有就先执行它们,
 调用doPendingActivityLaunchesLocked方法去执行这个循环。

final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
          String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
          IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
          IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
          String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
          ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
          ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
          TaskRecord inTask) {
      int err = ActivityManager.START_SUCCESS;

      ProcessRecord callerApp = null;
      if (caller != null) {
          callerApp = mService.getRecordForAppLocked(caller);
          if (callerApp != null) {
              callingPid = callerApp.pid;
              callingUid = callerApp.info.uid;
          } else {
              Slog.w(TAG, "Unable to find app for caller " + caller
                      + " (pid=" + callingPid + ") when starting: "
                      + intent.toString());
              err = ActivityManager.START_PERMISSION_DENIED;
          }
      }

      final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;

      if (err == ActivityManager.START_SUCCESS) {
          Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
                  + "} from uid " + callingUid
                  + " on display " + (container == null ? (mSupervisor.mFocusedStack == null ?
                  Display.DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) :
                  (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
                          container.mActivityDisplay.mDisplayId)));
      }

      ActivityRecord sourceRecord = null;
      ActivityRecord resultRecord = null;
      if (resultTo != null) {
          sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
          if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                  "Will send result to " + resultTo + " " + sourceRecord);
          if (sourceRecord != null) {
              if (requestCode >= 0 && !sourceRecord.finishing) {
                  resultRecord = sourceRecord;
              }
          }
      }

      final int launchFlags = intent.getFlags();

      if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
          // Transfer the result target from the source activity to the new
          // one being started, including any failures.
          if (requestCode >= 0) {
              ActivityOptions.abort(options);
              return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
          }
          resultRecord = sourceRecord.resultTo;
          if (resultRecord != null && !resultRecord.isInStackLocked()) {
              resultRecord = null;
          }
          resultWho = sourceRecord.resultWho;
          requestCode = sourceRecord.requestCode;
          sourceRecord.resultTo = null;
          if (resultRecord != null) {
              resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
          }
          if (sourceRecord.launchedFromUid == callingUid) {
              // The new activity is being launched from the same uid as the previous
              // activity in the flow, and asking to forward its result back to the
              // previous.  In this case the activity is serving as a trampoline between
              // the two, so we also want to update its launchedFromPackage to be the
              // same as the previous activity.  Note that this is safe, since we know
              // these two packages come from the same uid; the caller could just as
              // well have supplied that same package name itself.  This specifially
              // deals with the case of an intent picker/chooser being launched in the app
              // flow to redirect to an activity picked by the user, where we want the final
              // activity to consider it to have been launched by the previous app activity.
              callingPackage = sourceRecord.launchedFromPackage;
          }
      }

      if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
          // We couldn't find a class that can handle the given Intent.
          // That's the end of that!
          err = ActivityManager.START_INTENT_NOT_RESOLVED;
      }

      if (err == ActivityManager.START_SUCCESS && aInfo == null) {
          // We couldn't find the specific class specified in the Intent.
          // Also the end of the line.
          err = ActivityManager.START_CLASS_NOT_FOUND;
      }

      if (err == ActivityManager.START_SUCCESS && sourceRecord != null
              && sourceRecord.task.voiceSession != null) {
          // If this activity is being launched as part of a voice session, we need
          // to ensure that it is safe to do so.  If the upcoming activity will also
          // be part of the voice session, we can only launch it if it has explicitly
          // said it supports the VOICE category, or it is a part of the calling app.
          if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0
                  && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
              try {
                  intent.addCategory(Intent.CATEGORY_VOICE);
                  if (!AppGlobals.getPackageManager().activitySupportsIntent(
                          intent.getComponent(), intent, resolvedType)) {
                      Slog.w(TAG,
                              "Activity being started in current voice task does not support voice: "
                                      + intent);
                      err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
                  }
              } catch (RemoteException e) {
                  Slog.w(TAG, "Failure checking voice capabilities", e);
                  err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
              }
          }
      }

      if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
          // If the caller is starting a new voice session, just make sure the target
          // is actually allowing it to run this way.
          try {
              if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
                      intent, resolvedType)) {
                  Slog.w(TAG,
                          "Activity being started in new voice task does not support: "
                                  + intent);
                  err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
              }
          } catch (RemoteException e) {
              Slog.w(TAG, "Failure checking voice capabilities", e);
              err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
          }
      }

      final ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;

      if (err != START_SUCCESS) {
          if (resultRecord != null) {
              resultStack.sendActivityResultLocked(
                      -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
          }
          ActivityOptions.abort(options);
          return err;
      }

      boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
              requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
              resultRecord, resultStack, options);
      abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
              callingPid, resolvedType, aInfo.applicationInfo);

      if (mService.mController != null) {
          try {
              // The Intent we give to the watcher has the extra data
              // stripped off, since it can contain private information.
              Intent watchIntent = intent.cloneFilter();
              abort |= !mService.mController.activityStarting(watchIntent,
                      aInfo.applicationInfo.packageName);
          } catch (RemoteException e) {
              mService.mController = null;
          }
      }

      mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
      mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid,
              options);
      intent = mInterceptor.mIntent;
      rInfo = mInterceptor.mRInfo;
      aInfo = mInterceptor.mAInfo;
      resolvedType = mInterceptor.mResolvedType;
      inTask = mInterceptor.mInTask;
      callingPid = mInterceptor.mCallingPid;
      callingUid = mInterceptor.mCallingUid;
      options = mInterceptor.mActivityOptions;
      if (abort) {
          if (resultRecord != null) {
              resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
                      RESULT_CANCELED, null);
          }
          // We pretend to the caller that it was really started, but
          // they will just get a cancel result.
          ActivityOptions.abort(options);
          return START_SUCCESS;
      }

      // If permissions need a review before any of the app components can run, we
      // launch the review activity and pass a pending intent to start the activity
      // we are to launching now after the review is completed.
      if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) {
          if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                  aInfo.packageName, userId)) {
              IIntentSender target = mService.getIntentSenderLocked(
                      ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                      callingUid, userId, null, null, 0, new Intent[]{intent},
                      new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                              | PendingIntent.FLAG_ONE_SHOT, null);

              final int flags = intent.getFlags();
              Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
              newIntent.setFlags(flags
                      | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
              newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
              newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
              if (resultRecord != null) {
                  newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
              }
              intent = newIntent;

              resolvedType = null;
              callingUid = realCallingUid;
              callingPid = realCallingPid;

              rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
              aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
                      null /*profilerInfo*/);

              if (DEBUG_PERMISSIONS_REVIEW) {
                  Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
                          true, false) + "} from uid " + callingUid + " on display "
                          + (container == null ? (mSupervisor.mFocusedStack == null ?
                          Display.DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) :
                          (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
                                  container.mActivityDisplay.mDisplayId)));
              }
          }
      }

      // If we have an ephemeral app, abort the process of launching the resolved intent.
      // Instead, launch the ephemeral installer. Once the installer is finished, it
      // starts either the intent we resolved here [on install error] or the ephemeral
      // app [on install success].
      if (rInfo != null && rInfo.ephemeralResolveInfo != null) {
          intent = buildEphemeralInstallerIntent(intent, ephemeralIntent,
                  rInfo.ephemeralResolveInfo.getPackageName(), callingPackage, resolvedType,
                  userId);
          resolvedType = null;
          callingUid = realCallingUid;
          callingPid = realCallingPid;

          aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
      }

      ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
              intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
              requestCode, componentSpecified, voiceSession != null, mSupervisor, container,
              options, sourceRecord);
      if (outActivity != null) {
          outActivity[0] = r;
      }

      if (r.appTimeTracker == null && sourceRecord != null) {
          // If the caller didn't specify an explicit time tracker, we want to continue
          // tracking under any it has.
          r.appTimeTracker = sourceRecord.appTimeTracker;
      }

      final ActivityStack stack = mSupervisor.mFocusedStack;
      if (voiceSession == null && (stack.mResumedActivity == null
              || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
          if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                  realCallingPid, realCallingUid, "Activity start")) {
              PendingActivityLaunch pal =  new PendingActivityLaunch(r,
                      sourceRecord, startFlags, stack, callerApp);
              mPendingActivityLaunches.add(pal);
              ActivityOptions.abort(options);
              return ActivityManager.START_SWITCHES_CANCELED;
          }
      }

      if (mService.mDidAppSwitch) {
          // This is the second allowed switch since we stopped switches,
          // so now just generally allow switches.  Use case: user presses
          // home (switches disabled, switch to home, mDidAppSwitch now true);
          // user taps a home icon (coming from home so allowed, we hit here
          // and now allow anyone to switch again).
          mService.mAppSwitchesAllowedTime = 0;
      } else {
          mService.mDidAppSwitch = true;
      }

      doPendingActivityLaunchesLocked(false);

      try {
          mService.mWindowManager.deferSurfaceLayout();
          err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                  true, options, inTask);
      } finally {
          mService.mWindowManager.continueSurfaceLayout();
      }
      postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);
      return err;
  }

###参考资料

####https://yq.aliyun.com/articles/62489