/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.am; import static android.app.ActivityManager.StackId; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_THUMBNAILS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_THUMBNAILS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import android.app.ActivityManager.TaskDescription; import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.ResultInfo; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.Message; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.service.voice.IVoiceInteractionSession; import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.util.TimeUtils; import android.view.AppTransitionAnimationSpec; import android.view.IApplicationToken; import android.view.WindowManager; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; import com.android.server.am.ActivityStack.ActivityState; import com.android.server.am.ActivityStackSupervisor.ActivityContainer; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Objects; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; /** * An entry in the history stack, representing an activity. */ final class ActivityRecord { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_AM; private static final String TAG_STATES = TAG + POSTFIX_STATES; private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; private static final String TAG_THUMBNAILS = TAG + POSTFIX_THUMBNAILS; private static final boolean SHOW_ACTIVITY_START_TIME = true; final public static String RECENTS_PACKAGE_NAME = "com.android.systemui.recents"; private static final String ATTR_ID = "id"; private static final String TAG_INTENT = "intent"; private static final String ATTR_USERID = "user_id"; private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle"; private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid"; private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package"; private static final String ATTR_RESOLVEDTYPE = "resolved_type"; private static final String ATTR_COMPONENTSPECIFIED = "component_specified"; static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_"; final ActivityManagerService service; // owner final IApplicationToken.Stub appToken; // window manager token final ActivityInfo info; // all about me final ApplicationInfo appInfo; // information about activity's app final int launchedFromUid; // always the uid who started the activity. final String launchedFromPackage; // always the package who started the activity. final int userId; // Which user is this running for? final Intent intent; // the original intent that generated us final ComponentName realActivity; // the intent component, or target of an alias. final String shortComponentName; // the short component name of the intent final String resolvedType; // as per original caller; final String packageName; // the package implementing intent's component final String processName; // process where this component wants to run final String taskAffinity; // as per ActivityInfo.taskAffinity final boolean stateNotNeeded; // As per ActivityInfo.flags boolean fullscreen; // covers the full screen? final boolean noDisplay; // activity is not displayed? final boolean componentSpecified; // did caller specify an explicit component? final boolean rootVoiceInteraction; // was this the root activity of a voice interaction? static final int APPLICATION_ACTIVITY_TYPE = 0; static final int HOME_ACTIVITY_TYPE = 1; static final int RECENTS_ACTIVITY_TYPE = 2; int mActivityType; CharSequence nonLocalizedLabel; // the label information from the package mgr. int labelRes; // the label information from the package mgr. int icon; // resource identifier of activity's icon. int logo; // resource identifier of activity's logo. int theme; // resource identifier of activity's theme. int realTheme; // actual theme resource we will use, never 0. int windowFlags; // custom window flags for preview window. TaskRecord task; // the task this is in. long createTime = System.currentTimeMillis(); long displayStartTime; // when we started launching this activity long fullyDrawnStartTime; // when we started launching this activity long startTime; // last time this activity was started long lastVisibleTime; // last time this activity became visible long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity long pauseTime; // last time we started pausing the activity long launchTickTime; // base time for launch tick messages Configuration configuration; // configuration activity was last running in // Overridden configuration by the activity task // WARNING: Reference points to {@link TaskRecord#mOverrideConfig}, so its internal state // should never be altered directly. Configuration taskConfigOverride; CompatibilityInfo compat;// last used compatibility mode ActivityRecord resultTo; // who started this entry, so will get our reply final String resultWho; // additional identifier for use by resultTo. final int requestCode; // code given by requester (resultTo) ArrayList results; // pending ActivityResult objs we have received HashSet> pendingResults; // all pending intents for this act ArrayList newIntents; // any pending new intents for single-top mode ActivityOptions pendingOptions; // most recently given options ActivityOptions returningOptions; // options that are coming back via convertToTranslucent AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity HashSet connections; // All ConnectionRecord we hold UriPermissionOwner uriPermissions; // current special URI access perms. ProcessRecord app; // if non-null, hosting application ActivityState state; // current state we are in Bundle icicle; // last saved activity state PersistableBundle persistentState; // last persistently saved activity state boolean frontOfTask; // is this the root activity of its task? boolean launchFailed; // set if a launched failed, to abort on 2nd try boolean haveState; // have we gotten the last activity state? boolean stopped; // is activity pause finished? boolean delayedResume; // not yet resumed because of stopped app switches? boolean finishing; // activity in pending finish list? boolean deferRelaunchUntilPaused; // relaunch of activity is being deferred until pause is // completed boolean preserveWindowOnDeferredRelaunch; // activity windows are preserved on deferred relaunch int configChangeFlags; // which config values have changed boolean keysPaused; // has key dispatching been paused for it? int launchMode; // the launch mode activity attribute. boolean visible; // does this activity's window need to be shown? boolean sleeping; // have we told the activity to sleep? boolean nowVisible; // is this activity's window visible? boolean idle; // has the activity gone idle? boolean hasBeenLaunched;// has this activity ever been launched? boolean frozenBeforeDestroy;// has been frozen but not yet destroyed. boolean immersive; // immersive mode (don't interrupt if possible) boolean forceNewConfig; // force re-create with new config next time int launchCount; // count of launches since last state long lastLaunchTime; // time of last launch of this activity ComponentName requestedVrComponent; // the requested component for handling VR mode. ArrayList mChildContainers = new ArrayList<>(); String stringName; // for caching of toString(). private boolean inHistory; // are we in the history stack? final ActivityStackSupervisor mStackSupervisor; static final int STARTING_WINDOW_NOT_SHOWN = 0; static final int STARTING_WINDOW_SHOWN = 1; static final int STARTING_WINDOW_REMOVED = 2; int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN; boolean mTaskOverlay = false; // Task is always on-top of other activities in the task. boolean mUpdateTaskThumbnailWhenHidden; ActivityContainer mInitialActivityContainer; TaskDescription taskDescription; // the recents information for this activity boolean mLaunchTaskBehind; // this activity is actively being launched with // ActivityOptions.setLaunchTaskBehind, will be cleared once launch is completed. // These configurations are collected from application's resources based on size-sensitive // qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800 // and drawable-sw400dp will be added to both as 400. private int[] mVerticalSizeConfigurations; private int[] mHorizontalSizeConfigurations; private int[] mSmallestSizeConfigurations; boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session IVoiceInteractionSession voiceSession; // Voice interaction session for this activity private static String startingWindowStateToString(int state) { switch (state) { case STARTING_WINDOW_NOT_SHOWN: return "STARTING_WINDOW_NOT_SHOWN"; case STARTING_WINDOW_SHOWN: return "STARTING_WINDOW_SHOWN"; case STARTING_WINDOW_REMOVED: return "STARTING_WINDOW_REMOVED"; default: return "unknown state=" + state; } } void dump(PrintWriter pw, String prefix) { final long now = SystemClock.uptimeMillis(); pw.print(prefix); pw.print("packageName="); pw.print(packageName); pw.print(" processName="); pw.println(processName); pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid); pw.print(" launchedFromPackage="); pw.print(launchedFromPackage); pw.print(" userId="); pw.println(userId); pw.print(prefix); pw.print("app="); pw.println(app); pw.print(prefix); pw.println(intent.toInsecureStringWithClip()); pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask); pw.print(" task="); pw.println(task); pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity); pw.print(prefix); pw.print("realActivity="); pw.println(realActivity.flattenToShortString()); if (appInfo != null) { pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir); if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) { pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir); } pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir); if (appInfo.splitSourceDirs != null) { pw.print(prefix); pw.print("splitDir="); pw.println(Arrays.toString(appInfo.splitSourceDirs)); } } pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded); pw.print(" componentSpecified="); pw.print(componentSpecified); pw.print(" mActivityType="); pw.println(mActivityType); if (rootVoiceInteraction) { pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction); } pw.print(prefix); pw.print("compat="); pw.print(compat); pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes)); pw.print(" icon=0x"); pw.print(Integer.toHexString(icon)); pw.print(" theme=0x"); pw.println(Integer.toHexString(theme)); pw.print(prefix); pw.print("config="); pw.println(configuration); pw.print(prefix); pw.print("taskConfigOverride="); pw.println(taskConfigOverride); if (resultTo != null || resultWho != null) { pw.print(prefix); pw.print("resultTo="); pw.print(resultTo); pw.print(" resultWho="); pw.print(resultWho); pw.print(" resultCode="); pw.println(requestCode); } if (taskDescription != null) { final String iconFilename = taskDescription.getIconFilename(); if (iconFilename != null || taskDescription.getLabel() != null || taskDescription.getPrimaryColor() != 0) { pw.print(prefix); pw.print("taskDescription:"); pw.print(" iconFilename="); pw.print(taskDescription.getIconFilename()); pw.print(" label=\""); pw.print(taskDescription.getLabel()); pw.print("\""); pw.print(" color="); pw.println(Integer.toHexString(taskDescription.getPrimaryColor())); } if (iconFilename == null && taskDescription.getIcon() != null) { pw.print(prefix); pw.println("taskDescription contains Bitmap"); } } if (results != null) { pw.print(prefix); pw.print("results="); pw.println(results); } if (pendingResults != null && pendingResults.size() > 0) { pw.print(prefix); pw.println("Pending Results:"); for (WeakReference wpir : pendingResults) { PendingIntentRecord pir = wpir != null ? wpir.get() : null; pw.print(prefix); pw.print(" - "); if (pir == null) { pw.println("null"); } else { pw.println(pir); pir.dump(pw, prefix + " "); } } } if (newIntents != null && newIntents.size() > 0) { pw.print(prefix); pw.println("Pending New Intents:"); for (int i=0; i weakActivity; private final ActivityManagerService mService; Token(ActivityRecord activity, ActivityManagerService service) { weakActivity = new WeakReference<>(activity); mService = service; } @Override public void windowsDrawn() { synchronized (mService) { ActivityRecord r = tokenToActivityRecordLocked(this); if (r != null) { r.windowsDrawnLocked(); } } } @Override public void windowsVisible() { synchronized (mService) { ActivityRecord r = tokenToActivityRecordLocked(this); if (r != null) { r.windowsVisibleLocked(); } } } @Override public void windowsGone() { synchronized (mService) { ActivityRecord r = tokenToActivityRecordLocked(this); if (r != null) { if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + r); r.nowVisible = false; return; } } } @Override public boolean keyDispatchingTimedOut(String reason) { ActivityRecord r; ActivityRecord anrActivity; ProcessRecord anrApp; synchronized (mService) { r = tokenToActivityRecordLocked(this); if (r == null) { return false; } anrActivity = r.getWaitingHistoryRecordLocked(); anrApp = r != null ? r.app : null; } return mService.inputDispatchingTimedOut(anrApp, anrActivity, r, false, reason); } @Override public long getKeyDispatchingTimeout() { synchronized (mService) { ActivityRecord r = tokenToActivityRecordLocked(this); if (r == null) { return 0; } r = r.getWaitingHistoryRecordLocked(); return ActivityManagerService.getInputDispatchingTimeoutLocked(r); } } private static final ActivityRecord tokenToActivityRecordLocked(Token token) { if (token == null) { return null; } ActivityRecord r = token.weakActivity.get(); if (r == null || r.task == null || r.task.stack == null) { return null; } return r; } @Override public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("Token{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); sb.append(weakActivity.get()); sb.append('}'); return sb.toString(); } } static ActivityRecord forTokenLocked(IBinder token) { try { return Token.tokenToActivityRecordLocked((Token)token); } catch (ClassCastException e) { Slog.w(TAG, "Bad activity token: " + token, e); return null; } } boolean isResolverActivity() { return ResolverActivity.class.getName().equals(realActivity.getClassName()); } ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified, boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor, ActivityContainer container, ActivityOptions options, ActivityRecord sourceRecord) { service = _service; appToken = new Token(this, service); info = aInfo; launchedFromUid = _launchedFromUid; launchedFromPackage = _launchedFromPackage; userId = UserHandle.getUserId(aInfo.applicationInfo.uid); intent = _intent; shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; componentSpecified = _componentSpecified; rootVoiceInteraction = _rootVoiceInteraction; configuration = _configuration; taskConfigOverride = Configuration.EMPTY; resultTo = _resultTo; resultWho = _resultWho; requestCode = _reqCode; state = ActivityState.INITIALIZING; frontOfTask = false; launchFailed = false; stopped = false; delayedResume = false; finishing = false; deferRelaunchUntilPaused = false; keysPaused = false; inHistory = false; visible = false; nowVisible = false; idle = false; hasBeenLaunched = false; mStackSupervisor = supervisor; mInitialActivityContainer = container; if (options != null) { pendingOptions = options; mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind(); PendingIntent usageReport = pendingOptions.getUsageTimeReport(); if (usageReport != null) { appTimeTracker = new AppTimeTracker(usageReport); } } // This starts out true, since the initial state of an activity // is that we have everything, and we shouldn't never consider it // lacking in state to be removed if it dies. haveState = true; if (aInfo != null) { // If the class name in the intent doesn't match that of the target, this is // probably an alias. We have to create a new ComponentName object to keep track // of the real activity name, so that FLAG_ACTIVITY_CLEAR_TOP is handled properly. if (aInfo.targetActivity == null || (aInfo.targetActivity.equals(_intent.getComponent().getClassName()) && (aInfo.launchMode == ActivityInfo.LAUNCH_MULTIPLE || aInfo.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP))) { realActivity = _intent.getComponent(); } else { realActivity = new ComponentName(aInfo.packageName, aInfo.targetActivity); } taskAffinity = aInfo.taskAffinity; stateNotNeeded = (aInfo.flags& ActivityInfo.FLAG_STATE_NOT_NEEDED) != 0; appInfo = aInfo.applicationInfo; nonLocalizedLabel = aInfo.nonLocalizedLabel; labelRes = aInfo.labelRes; if (nonLocalizedLabel == null && labelRes == 0) { ApplicationInfo app = aInfo.applicationInfo; nonLocalizedLabel = app.nonLocalizedLabel; labelRes = app.labelRes; } icon = aInfo.getIconResource(); logo = aInfo.getLogoResource(); theme = aInfo.getThemeResource(); realTheme = theme; if (realTheme == 0) { realTheme = aInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.HONEYCOMB ? android.R.style.Theme : android.R.style.Theme_Holo; } if ((aInfo.flags&ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { windowFlags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } if ((aInfo.flags&ActivityInfo.FLAG_MULTIPROCESS) != 0 && _caller != null && (aInfo.applicationInfo.uid == Process.SYSTEM_UID || aInfo.applicationInfo.uid == _caller.info.uid)) { processName = _caller.processName; } else { processName = aInfo.processName; } if (intent != null && (aInfo.flags & ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS) != 0) { intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); } packageName = aInfo.applicationInfo.packageName; launchMode = aInfo.launchMode; AttributeCache.Entry ent = AttributeCache.instance().get(packageName, realTheme, com.android.internal.R.styleable.Window, userId); final boolean translucent = ent != null && (ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsTranslucent, false) || (!ent.array.hasValue( com.android.internal.R.styleable.Window_windowIsTranslucent) && ent.array.getBoolean( com.android.internal.R.styleable.Window_windowSwipeToDismiss, false))); fullscreen = ent != null && !ent.array.getBoolean( com.android.internal.R.styleable.Window_windowIsFloating, false) && !translucent; noDisplay = ent != null && ent.array.getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); setActivityType(_componentSpecified, _launchedFromUid, _intent, sourceRecord); immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0; requestedVrComponent = (aInfo.requestedVrComponent == null) ? null : ComponentName.unflattenFromString(aInfo.requestedVrComponent); } else { realActivity = null; taskAffinity = null; stateNotNeeded = false; appInfo = null; processName = null; packageName = null; fullscreen = true; noDisplay = false; mActivityType = APPLICATION_ACTIVITY_TYPE; immersive = false; requestedVrComponent = null; } } private boolean isHomeIntent(Intent intent) { return Intent.ACTION_MAIN.equals(intent.getAction()) && intent.hasCategory(Intent.CATEGORY_HOME) && intent.getCategories().size() == 1 && intent.getData() == null && intent.getType() == null; } private boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) { if (uid == Process.myUid() || uid == 0) { // System process can launch home activity. return true; } // Resolver activity can launch home activity. return sourceRecord != null && sourceRecord.isResolverActivity(); } private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent, ActivityRecord sourceRecord) { if ((!componentSpecified || canLaunchHomeActivity(launchedFromUid, sourceRecord)) && isHomeIntent(intent) && !isResolverActivity()) { // This sure looks like a home activity! mActivityType = HOME_ACTIVITY_TYPE; } else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) { mActivityType = RECENTS_ACTIVITY_TYPE; } else { mActivityType = APPLICATION_ACTIVITY_TYPE; } } void setTask(TaskRecord newTask, TaskRecord taskToAffiliateWith) { if (task != null && task.removeActivity(this) && task != newTask && task.stack != null) { task.stack.removeTask(task, "setTask"); } task = newTask; setTaskToAffiliateWith(taskToAffiliateWith); } void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) { if (taskToAffiliateWith != null && launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE && launchMode != ActivityInfo.LAUNCH_SINGLE_TASK) { task.setTaskToAffiliateWith(taskToAffiliateWith); } } boolean changeWindowTranslucency(boolean toOpaque) { if (fullscreen == toOpaque) { return false; } // Keep track of the number of fullscreen activities in this task. task.numFullscreen += toOpaque ? +1 : -1; fullscreen = toOpaque; return true; } void putInHistory() { if (!inHistory) { inHistory = true; } } void takeFromHistory() { if (inHistory) { inHistory = false; if (task != null && !finishing) { task = null; } clearOptionsLocked(); } } boolean isInHistory() { return inHistory; } boolean isInStackLocked() { return task != null && task.stack != null && task.stack.isInStackLocked(this) != null; } boolean isHomeActivity() { return mActivityType == HOME_ACTIVITY_TYPE; } boolean isRecentsActivity() { return mActivityType == RECENTS_ACTIVITY_TYPE; } boolean isApplicationActivity() { return mActivityType == APPLICATION_ACTIVITY_TYPE; } boolean isPersistable() { return (info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || info.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS) && (intent == null || (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0); } boolean isFocusable() { return StackId.canReceiveKeys(task.stack.mStackId) || isAlwaysFocusable(); } boolean isResizeable() { return !isHomeActivity() && ActivityInfo.isResizeableMode(info.resizeMode); } boolean isResizeableOrForced() { return !isHomeActivity() && (isResizeable() || service.mForceResizableActivities); } boolean isNonResizableOrForced() { return !isHomeActivity() && info.resizeMode != RESIZE_MODE_RESIZEABLE && info.resizeMode != RESIZE_MODE_RESIZEABLE_AND_PIPABLE; } boolean supportsPictureInPicture() { return !isHomeActivity() && info.resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE; } boolean canGoInDockedStack() { return !isHomeActivity() && (isResizeableOrForced() || info.resizeMode == RESIZE_MODE_CROP_WINDOWS); } boolean isAlwaysFocusable() { return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0; } void makeFinishingLocked() { if (!finishing) { if (task != null && task.stack != null && this == task.stack.getVisibleBehindActivity()) { // A finishing activity should not remain as visible in the background mStackSupervisor.requestVisibleBehindLocked(this, false); } finishing = true; if (stopped) { clearOptionsLocked(); } } } UriPermissionOwner getUriPermissionsLocked() { if (uriPermissions == null) { uriPermissions = new UriPermissionOwner(service, this); } return uriPermissions; } void addResultLocked(ActivityRecord from, String resultWho, int requestCode, int resultCode, Intent resultData) { ActivityResult r = new ActivityResult(from, resultWho, requestCode, resultCode, resultData); if (results == null) { results = new ArrayList(); } results.add(r); } void removeResultsLocked(ActivityRecord from, String resultWho, int requestCode) { if (results != null) { for (int i=results.size()-1; i>=0; i--) { ActivityResult r = (ActivityResult)results.get(i); if (r.mFrom != from) continue; if (r.mResultWho == null) { if (resultWho != null) continue; } else { if (!r.mResultWho.equals(resultWho)) continue; } if (r.mRequestCode != requestCode) continue; results.remove(i); } } } void addNewIntentLocked(ReferrerIntent intent) { if (newIntents == null) { newIntents = new ArrayList<>(); } newIntents.add(intent); } /** * Deliver a new Intent to an existing activity, so that its onNewIntent() * method will be called at the proper time. */ final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) { // The activity now gets access to the data associated with this Intent. service.grantUriPermissionFromIntentLocked(callingUid, packageName, intent, getUriPermissionsLocked(), userId); // We want to immediately deliver the intent to the activity if // it is currently the top resumed activity... however, if the // device is sleeping, then all activities are stopped, so in that // case we will deliver it if this is the current top activity on its // stack. final ReferrerIntent rintent = new ReferrerIntent(intent, referrer); boolean unsent = true; if ((state == ActivityState.RESUMED || (service.isSleepingLocked() && task.stack != null && task.stack.topRunningActivityLocked() == this)) && app != null && app.thread != null) { try { ArrayList ar = new ArrayList<>(1); ar.add(rintent); app.thread.scheduleNewIntent(ar, appToken); unsent = false; } catch (RemoteException e) { Slog.w(TAG, "Exception thrown sending new intent to " + this, e); } catch (NullPointerException e) { Slog.w(TAG, "Exception thrown sending new intent to " + this, e); } } if (unsent) { addNewIntentLocked(rintent); } } void updateOptionsLocked(ActivityOptions options) { if (options != null) { if (pendingOptions != null) { pendingOptions.abort(); } pendingOptions = options; } } void applyOptionsLocked() { if (pendingOptions != null && pendingOptions.getAnimationType() != ActivityOptions.ANIM_SCENE_TRANSITION) { final int animationType = pendingOptions.getAnimationType(); switch (animationType) { case ActivityOptions.ANIM_CUSTOM: service.mWindowManager.overridePendingAppTransition( pendingOptions.getPackageName(), pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(), pendingOptions.getOnAnimationStartListener()); break; case ActivityOptions.ANIM_CLIP_REVEAL: service.mWindowManager.overridePendingAppTransitionClipReveal( pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getWidth(), pendingOptions.getHeight()); if (intent.getSourceBounds() == null) { intent.setSourceBounds(new Rect(pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getStartX()+pendingOptions.getWidth(), pendingOptions.getStartY()+pendingOptions.getHeight())); } break; case ActivityOptions.ANIM_SCALE_UP: service.mWindowManager.overridePendingAppTransitionScaleUp( pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getWidth(), pendingOptions.getHeight()); if (intent.getSourceBounds() == null) { intent.setSourceBounds(new Rect(pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getStartX()+pendingOptions.getWidth(), pendingOptions.getStartY()+pendingOptions.getHeight())); } break; case ActivityOptions.ANIM_THUMBNAIL_SCALE_UP: case ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN: boolean scaleUp = (animationType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP); service.mWindowManager.overridePendingAppTransitionThumb( pendingOptions.getThumbnail(), pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getOnAnimationStartListener(), scaleUp); if (intent.getSourceBounds() == null) { intent.setSourceBounds(new Rect(pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getStartX() + pendingOptions.getThumbnail().getWidth(), pendingOptions.getStartY() + pendingOptions.getThumbnail().getHeight())); } break; case ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP: case ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs(); if (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN && specs != null) { service.mWindowManager.overridePendingAppTransitionMultiThumb( specs, pendingOptions.getOnAnimationStartListener(), pendingOptions.getAnimationFinishedListener(), false); } else { service.mWindowManager.overridePendingAppTransitionAspectScaledThumb( pendingOptions.getThumbnail(), pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getWidth(), pendingOptions.getHeight(), pendingOptions.getOnAnimationStartListener(), (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP)); if (intent.getSourceBounds() == null) { intent.setSourceBounds(new Rect(pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getStartX() + pendingOptions.getWidth(), pendingOptions.getStartY() + pendingOptions.getHeight())); } } break; default: Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType); break; } pendingOptions = null; } } ActivityOptions getOptionsForTargetActivityLocked() { return pendingOptions != null ? pendingOptions.forTargetActivity() : null; } void clearOptionsLocked() { if (pendingOptions != null) { pendingOptions.abort(); pendingOptions = null; } } ActivityOptions takeOptionsLocked() { ActivityOptions opts = pendingOptions; pendingOptions = null; return opts; } void removeUriPermissionsLocked() { if (uriPermissions != null) { uriPermissions.removeUriPermissionsLocked(); uriPermissions = null; } } void pauseKeyDispatchingLocked() { if (!keysPaused) { keysPaused = true; service.mWindowManager.pauseKeyDispatching(appToken); } } void resumeKeyDispatchingLocked() { if (keysPaused) { keysPaused = false; service.mWindowManager.resumeKeyDispatching(appToken); } } void updateThumbnailLocked(Bitmap newThumbnail, CharSequence description) { if (newThumbnail != null) { if (DEBUG_THUMBNAILS) Slog.i(TAG_THUMBNAILS, "Setting thumbnail of " + this + " to " + newThumbnail); boolean thumbnailUpdated = task.setLastThumbnailLocked(newThumbnail); if (thumbnailUpdated && isPersistable()) { mStackSupervisor.mService.notifyTaskPersisterLocked(task, false); } } task.lastDescription = description; } void startLaunchTickingLocked() { if (ActivityManagerService.IS_USER_BUILD) { return; } if (launchTickTime == 0) { launchTickTime = SystemClock.uptimeMillis(); continueLaunchTickingLocked(); } } boolean continueLaunchTickingLocked() { if (launchTickTime == 0) { return false; } final ActivityStack stack = task.stack; if (stack == null) { return false; } Message msg = stack.mHandler.obtainMessage(ActivityStack.LAUNCH_TICK_MSG, this); stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG); stack.mHandler.sendMessageDelayed(msg, ActivityStack.LAUNCH_TICK); return true; } void finishLaunchTickingLocked() { launchTickTime = 0; final ActivityStack stack = task.stack; if (stack != null) { stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG); } } // IApplicationToken public boolean mayFreezeScreenLocked(ProcessRecord app) { // Only freeze the screen if this activity is currently attached to // an application, and that application is not blocked or unresponding. // In any other case, we can't count on getting the screen unfrozen, // so it is best to leave as-is. return app != null && !app.crashing && !app.notResponding; } public void startFreezingScreenLocked(ProcessRecord app, int configChanges) { if (mayFreezeScreenLocked(app)) { service.mWindowManager.startAppFreezingScreen(appToken, configChanges); } } public void stopFreezingScreenLocked(boolean force) { if (force || frozenBeforeDestroy) { frozenBeforeDestroy = false; service.mWindowManager.stopAppFreezingScreen(appToken, force); } } public void reportFullyDrawnLocked() { final long curTime = SystemClock.uptimeMillis(); if (displayStartTime != 0) { reportLaunchTimeLocked(curTime); } final ActivityStack stack = task.stack; if (fullyDrawnStartTime != 0 && stack != null) { final long thisTime = curTime - fullyDrawnStartTime; final long totalTime = stack.mFullyDrawnStartTime != 0 ? (curTime - stack.mFullyDrawnStartTime) : thisTime; if (SHOW_ACTIVITY_START_TIME) { Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); EventLog.writeEvent(EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME, userId, System.identityHashCode(this), shortComponentName, thisTime, totalTime); StringBuilder sb = service.mStringBuilder; sb.setLength(0); sb.append("Fully drawn "); sb.append(shortComponentName); sb.append(": "); TimeUtils.formatDuration(thisTime, sb); if (thisTime != totalTime) { sb.append(" (total "); TimeUtils.formatDuration(totalTime, sb); sb.append(")"); } Log.i(TAG, sb.toString()); } if (totalTime > 0) { //service.mUsageStatsService.noteFullyDrawnTime(realActivity, (int) totalTime); } stack.mFullyDrawnStartTime = 0; } fullyDrawnStartTime = 0; } private void reportLaunchTimeLocked(final long curTime) { final ActivityStack stack = task.stack; if (stack == null) { return; } final long thisTime = curTime - displayStartTime; final long totalTime = stack.mLaunchStartTime != 0 ? (curTime - stack.mLaunchStartTime) : thisTime; if (SHOW_ACTIVITY_START_TIME) { Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0); EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME, userId, System.identityHashCode(this), shortComponentName, thisTime, totalTime); StringBuilder sb = service.mStringBuilder; sb.setLength(0); sb.append("Displayed "); sb.append(shortComponentName); sb.append(": "); TimeUtils.formatDuration(thisTime, sb); if (thisTime != totalTime) { sb.append(" (total "); TimeUtils.formatDuration(totalTime, sb); sb.append(")"); } Log.i(TAG, sb.toString()); } mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime); if (totalTime > 0) { //service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime); } displayStartTime = 0; stack.mLaunchStartTime = 0; } void windowsDrawnLocked() { mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(); if (displayStartTime != 0) { reportLaunchTimeLocked(SystemClock.uptimeMillis()); } mStackSupervisor.sendWaitingVisibleReportLocked(this); startTime = 0; finishLaunchTickingLocked(); if (task != null) { task.hasBeenVisible = true; } } void windowsVisibleLocked() { mStackSupervisor.reportActivityVisibleLocked(this); if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this); if (!nowVisible) { nowVisible = true; lastVisibleTime = SystemClock.uptimeMillis(); if (!idle) { // Instead of doing the full stop routine here, let's just hide any activities // we now can, and let them stop when the normal idle happens. mStackSupervisor.processStoppingActivitiesLocked(false); } else { // If this activity was already idle, then we now need to make sure we perform // the full stop of any activities that are waiting to do so. This is because // we won't do that while they are still waiting for this one to become visible. final int size = mStackSupervisor.mWaitingVisibleActivities.size(); if (size > 0) { for (int i = 0; i < size; i++) { ActivityRecord r = mStackSupervisor.mWaitingVisibleActivities.get(i); if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "Was waiting for visible: " + r); } mStackSupervisor.mWaitingVisibleActivities.clear(); mStackSupervisor.scheduleIdleLocked(); } } service.scheduleAppGcsLocked(); } } ActivityRecord getWaitingHistoryRecordLocked() { // First find the real culprit... if this activity is waiting for // another activity to start or has stopped, then the key dispatching // timeout should not be caused by this. if (mStackSupervisor.mWaitingVisibleActivities.contains(this) || stopped) { final ActivityStack stack = mStackSupervisor.getFocusedStack(); // Try to use the one which is closest to top. ActivityRecord r = stack.mResumedActivity; if (r == null) { r = stack.mPausingActivity; } if (r != null) { return r; } } return this; } /** * This method will return true if the activity is either visible, is becoming visible, is * currently pausing, or is resumed. */ public boolean isInterestingToUserLocked() { return visible || nowVisible || state == ActivityState.PAUSING || state == ActivityState.RESUMED; } public void setSleeping(boolean _sleeping) { if (sleeping == _sleeping) { return; } if (app != null && app.thread != null) { try { app.thread.scheduleSleeping(appToken, _sleeping); if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) { mStackSupervisor.mGoingToSleepActivities.add(this); } sleeping = _sleeping; } catch (RemoteException e) { Slog.w(TAG, "Exception thrown when sleeping: " + intent.getComponent(), e); } } } static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); if (r == null) { return INVALID_TASK_ID; } final TaskRecord task = r.task; final int activityNdx = task.mActivities.indexOf(r); if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) { return INVALID_TASK_ID; } return task.taskId; } static ActivityRecord isInStackLocked(IBinder token) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); return (r != null) ? r.task.stack.isInStackLocked(r) : null; } static ActivityStack getStackLocked(IBinder token) { final ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { return r.task.stack; } return null; } final boolean isDestroyable() { if (finishing || app == null || state == ActivityState.DESTROYING || state == ActivityState.DESTROYED) { // This would be redundant. return false; } if (task == null || task.stack == null || this == task.stack.mResumedActivity || this == task.stack.mPausingActivity || !haveState || !stopped) { // We're not ready for this kind of thing. return false; } if (visible) { // The user would notice this! return false; } return true; } private static String createImageFilename(long createTime, int taskId) { return String.valueOf(taskId) + ACTIVITY_ICON_SUFFIX + createTime + TaskPersister.IMAGE_EXTENSION; } void setTaskDescription(TaskDescription _taskDescription) { Bitmap icon; if (_taskDescription.getIconFilename() == null && (icon = _taskDescription.getIcon()) != null) { final String iconFilename = createImageFilename(createTime, task.taskId); final File iconFile = new File(TaskPersister.getUserImagesDir(userId), iconFilename); final String iconFilePath = iconFile.getAbsolutePath(); service.mRecentTasks.saveImage(icon, iconFilePath); _taskDescription.setIconFilename(iconFilePath); } taskDescription = _taskDescription; } void setVoiceSessionLocked(IVoiceInteractionSession session) { voiceSession = session; pendingVoiceInteractionStart = false; } void clearVoiceSessionLocked() { voiceSession = null; pendingVoiceInteractionStart = false; } void showStartingWindow(ActivityRecord prev, boolean createIfNeeded) { final CompatibilityInfo compatInfo = service.compatibilityInfoForPackageLocked(info.applicationInfo); final boolean shown = service.mWindowManager.setAppStartingWindow( appToken, packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev != null ? prev.appToken : null, createIfNeeded); if (shown) { mStartingWindowState = STARTING_WINDOW_SHOWN; } } void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { out.attribute(null, ATTR_ID, String.valueOf(createTime)); out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid)); if (launchedFromPackage != null) { out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage); } if (resolvedType != null) { out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType); } out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified)); out.attribute(null, ATTR_USERID, String.valueOf(userId)); if (taskDescription != null) { taskDescription.saveToXml(out); } out.startTag(null, TAG_INTENT); intent.saveToXml(out); out.endTag(null, TAG_INTENT); if (isPersistable() && persistentState != null) { out.startTag(null, TAG_PERSISTABLEBUNDLE); persistentState.saveToXml(out); out.endTag(null, TAG_PERSISTABLEBUNDLE); } } static ActivityRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException { Intent intent = null; PersistableBundle persistentState = null; int launchedFromUid = 0; String launchedFromPackage = null; String resolvedType = null; boolean componentSpecified = false; int userId = 0; long createTime = -1; final int outerDepth = in.getDepth(); TaskDescription taskDescription = new TaskDescription(); for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) { final String attrName = in.getAttributeName(attrNdx); final String attrValue = in.getAttributeValue(attrNdx); if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "ActivityRecord: attribute name=" + attrName + " value=" + attrValue); if (ATTR_ID.equals(attrName)) { createTime = Long.valueOf(attrValue); } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) { launchedFromUid = Integer.parseInt(attrValue); } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) { launchedFromPackage = attrValue; } else if (ATTR_RESOLVEDTYPE.equals(attrName)) { resolvedType = attrValue; } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) { componentSpecified = Boolean.valueOf(attrValue); } else if (ATTR_USERID.equals(attrName)) { userId = Integer.parseInt(attrValue); } else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) { taskDescription.restoreFromXml(attrName, attrValue); } else { Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName); } } int event; while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) { if (event == XmlPullParser.START_TAG) { final String name = in.getName(); if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "ActivityRecord: START_TAG name=" + name); if (TAG_INTENT.equals(name)) { intent = Intent.restoreFromXml(in); if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "ActivityRecord: intent=" + intent); } else if (TAG_PERSISTABLEBUNDLE.equals(name)) { persistentState = PersistableBundle.restoreFromXml(in); if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "ActivityRecord: persistentState=" + persistentState); } else { Slog.w(TAG, "restoreActivity: unexpected name=" + name); XmlUtils.skipCurrentTag(in); } } } if (intent == null) { throw new XmlPullParserException("restoreActivity error intent=" + intent); } final ActivityManagerService service = stackSupervisor.mService; final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null, userId); if (aInfo == null) { throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent + " resolvedType=" + resolvedType); } final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid, launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(), null, null, 0, componentSpecified, false, stackSupervisor, null, null, null); r.persistentState = persistentState; r.taskDescription = taskDescription; r.createTime = createTime; return r; } private static String activityTypeToString(int type) { switch (type) { case APPLICATION_ACTIVITY_TYPE: return "APPLICATION_ACTIVITY_TYPE"; case HOME_ACTIVITY_TYPE: return "HOME_ACTIVITY_TYPE"; case RECENTS_ACTIVITY_TYPE: return "RECENTS_ACTIVITY_TYPE"; default: return Integer.toString(type); } } @Override public String toString() { if (stringName != null) { return stringName + " t" + (task == null ? INVALID_TASK_ID : task.taskId) + (finishing ? " f}" : "}"); } StringBuilder sb = new StringBuilder(128); sb.append("ActivityRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" u"); sb.append(userId); sb.append(' '); sb.append(intent.getComponent().flattenToShortString()); stringName = sb.toString(); return toString(); } }