/*
* 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 com.android.internal.app.ResolverActivity;
import com.android.server.AttributeCache;
import com.android.server.am.ActivityStack.ActivityState;
import android.app.Activity;
import android.app.ActivityOptions;
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.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.IApplicationToken;
import android.view.WindowManager;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
/**
* An entry in the history stack, representing an activity.
*/
final class ActivityRecord {
final ActivityManagerService service; // owner
final ActivityStack stack; // owner
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid 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
final boolean fullscreen; // covers the full screen?
final boolean noDisplay; // activity is not displayed?
final boolean componentSpecified; // did caller specifiy an explicit component?
final boolean isHomeActivity; // do we consider this to be a home activity?
final String baseDir; // where activity source (resources etc) located
final String resDir; // where public activity source (public resources etc) located
final String dataDir; // where activity data should go
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 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.
ThumbnailHolder thumbHolder; // where our thumbnails should go.
long launchTime; // when we starting 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
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<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
ArrayList newIntents; // any pending new intents for single-top mode
ActivityOptions pendingOptions; // most recently given options
HashSet<ConnectionRecord> 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
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 configDestroy; // need to destroy due to config change?
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 waitingVisible; // true if waiting for a new act to become vis
boolean nowVisible; // is this activity's window visible?
boolean thumbnailNeeded;// has someone requested a thumbnail?
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
String stringName; // for caching of toString().
private boolean inHistory; // are we in the history stack?
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(" 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());
pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
if (!resDir.equals(baseDir)) {
pw.print(prefix); pw.print("resDir="); pw.println(resDir);
}
pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
pw.print(" isHomeActivity="); pw.println(isHomeActivity);
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);
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 (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<PendingIntentRecord> 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<newIntents.size(); i++) {
Intent intent = (Intent)newIntents.get(i);
pw.print(prefix); pw.print(" - ");
if (intent == null) {
pw.println("null");
} else {
pw.println(intent.toShortString(false, true, false, true));
}
}
}
if (pendingOptions != null) {
pw.print(prefix); pw.print("pendingOptions="); pw.println(pendingOptions);
}
if (uriPermissions != null) {
if (uriPermissions.readUriPermissions != null) {
pw.print(prefix); pw.print("readUriPermissions=");
pw.println(uriPermissions.readUriPermissions);
}
if (uriPermissions.writeUriPermissions != null) {
pw.print(prefix); pw.print("writeUriPermissions=");
pw.println(uriPermissions.writeUriPermissions);
}
}
pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed);
pw.print(" haveState="); pw.print(haveState);
pw.print(" icicle="); pw.println(icicle);
pw.print(prefix); pw.print("state="); pw.print(state);
pw.print(" stopped="); pw.print(stopped);
pw.print(" delayedResume="); pw.print(delayedResume);
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
pw.print(" visible="); pw.print(visible);
pw.print(" sleeping="); pw.print(sleeping);
pw.print(" idle="); pw.println(idle);
pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
pw.print(" noDisplay="); pw.print(noDisplay);
pw.print(" immersive="); pw.print(immersive);
pw.print(" launchMode="); pw.println(launchMode);
pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
pw.print(" forceNewConfig="); pw.println(forceNewConfig);
pw.print(prefix); pw.print("thumbHolder: ");
pw.print(Integer.toHexString(System.identityHashCode(thumbHolder)));
if (thumbHolder != null) {
pw.print(" bm="); pw.print(thumbHolder.lastThumbnail);
pw.print(" desc="); pw.print(thumbHolder.lastDescription);
}
pw.println();
if (launchTime != 0 || startTime != 0) {
pw.print(prefix); pw.print("launchTime=");
if (launchTime == 0) pw.print("0");
else TimeUtils.formatDuration(launchTime, now, pw);
pw.print(" startTime=");
if (startTime == 0) pw.print("0");
else TimeUtils.formatDuration(startTime, now, pw);
pw.println();
}
if (lastVisibleTime != 0 || waitingVisible || nowVisible) {
pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
pw.print(" nowVisible="); pw.print(nowVisible);
pw.print(" lastVisibleTime=");
if (lastVisibleTime == 0) pw.print("0");
else TimeUtils.formatDuration(lastVisibleTime, now, pw);
pw.println();
}
if (configDestroy || configChangeFlags != 0) {
pw.print(prefix); pw.print("configDestroy="); pw.print(configDestroy);
pw.print(" configChangeFlags=");
pw.println(Integer.toHexString(configChangeFlags));
}
if (connections != null) {
pw.print(prefix); pw.print("connections="); pw.println(connections);
}
}
static class Token extends IApplicationToken.Stub {
final WeakReference<ActivityRecord> weakActivity;
Token(ActivityRecord activity) {
weakActivity = new WeakReference<ActivityRecord>(activity);
}
@Override public void windowsDrawn() throws RemoteException {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsDrawn();
}
}
@Override public void windowsVisible() throws RemoteException {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsVisible();
}
}
@Override public void windowsGone() throws RemoteException {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsGone();
}
}
@Override public boolean keyDispatchingTimedOut() throws RemoteException {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
return activity.keyDispatchingTimedOut();
}
return false;
}
@Override public long getKeyDispatchingTimeout() throws RemoteException {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
return activity.getKeyDispatchingTimeout();
}
return 0;
}
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 forToken(IBinder token) {
try {
return token != null ? ((Token)token).weakActivity.get() : null;
} catch (ClassCastException e) {
Slog.w(ActivityManagerService.TAG, "Bad activity token: " + token, e);
return null;
}
}
ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller,
int _launchedFromUid, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified) {
service = _service;
stack = _stack;
appToken = new Token(this);
info = aInfo;
launchedFromUid = _launchedFromUid;
userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
componentSpecified = _componentSpecified;
configuration = _configuration;
resultTo = _resultTo;
resultWho = _resultWho;
requestCode = _reqCode;
state = ActivityState.INITIALIZING;
frontOfTask = false;
launchFailed = false;
stopped = false;
delayedResume = false;
finishing = false;
configDestroy = false;
keysPaused = false;
inHistory = false;
visible = true;
waitingVisible = false;
nowVisible = false;
thumbnailNeeded = false;
idle = false;
hasBeenLaunched = false;
// 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 (aInfo.targetActivity == null
|| 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;
baseDir = aInfo.applicationInfo.sourceDir;
resDir = aInfo.applicationInfo.publicSourceDir;
dataDir = aInfo.applicationInfo.dataDir;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;
if (nonLocalizedLabel == null && labelRes == 0) {
ApplicationInfo app = aInfo.applicationInfo;
nonLocalizedLabel = app.nonLocalizedLabel;
labelRes = app.labelRes;
}
icon = aInfo.getIconResource();
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(userId, packageName,
realTheme, com.android.internal.R.styleable.Window);
fullscreen = ent != null && !ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsFloating, false)
&& !ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
noDisplay = ent != null && ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
if (!_componentSpecified || _launchedFromUid == Process.myUid()
|| _launchedFromUid == 0) {
// If we know the system has determined the component, then
// we can consider this to be a home activity...
if (Intent.ACTION_MAIN.equals(_intent.getAction()) &&
_intent.hasCategory(Intent.CATEGORY_HOME) &&
_intent.getCategories().size() == 1 &&
_intent.getData() == null &&
_intent.getType() == null &&
(intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
!ResolverActivity.class.getName().equals(realActivity.getClassName())) {
// This sure looks like a home activity!
// Note the last check is so we don't count the resolver
// activity as being home... really, we don't care about
// doing anything special with something that comes from
// the core framework package.
isHomeActivity = true;
} else {
isHomeActivity = false;
}
} else {
isHomeActivity = false;
}
immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
} else {
realActivity = null;
taskAffinity = null;
stateNotNeeded = false;
baseDir = null;
resDir = null;
dataDir = null;
processName = null;
packageName = null;
fullscreen = true;
noDisplay = false;
isHomeActivity = false;
immersive = false;
}
}
void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
if (inHistory && !finishing) {
if (task != null) {
task.numActivities--;
}
if (newTask != null) {
newTask.numActivities++;
}
}
if (newThumbHolder == null) {
newThumbHolder = newTask;
}
task = newTask;
if (!isRoot && (intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// This is the start of a new sub-task.
if (thumbHolder == null) {
thumbHolder = new ThumbnailHolder();
}
} else {
thumbHolder = newThumbHolder;
}
}
void putInHistory() {
if (!inHistory) {
inHistory = true;
if (task != null && !finishing) {
task.numActivities++;
}
}
}
void takeFromHistory() {
if (inHistory) {
inHistory = false;
if (task != null && !finishing) {
task.numActivities--;
}
clearOptionsLocked();
}
}
boolean isInHistory() {
return inHistory;
}
void makeFinishing() {
if (!finishing) {
finishing = true;
if (task != null && inHistory) {
task.numActivities--;
}
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(Intent 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) {
boolean sent = false;
// 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.
if ((state == ActivityState.RESUMED || (service.mSleeping
&& stack.topRunningActivityLocked(null) == this))
&& app != null && app.thread != null) {
try {
ArrayList<Intent> ar = new ArrayList<Intent>();
intent = new Intent(intent);
ar.add(intent);
service.grantUriPermissionFromIntentLocked(callingUid, packageName,
intent, getUriPermissionsLocked());
app.thread.scheduleNewIntent(ar, appToken);
sent = true;
} catch (RemoteException e) {
Slog.w(ActivityManagerService.TAG,
"Exception thrown sending new intent to " + this, e);
} catch (NullPointerException e) {
Slog.w(ActivityManagerService.TAG,
"Exception thrown sending new intent to " + this, e);
}
}
if (!sent) {
addNewIntentLocked(new Intent(intent));
}
}
void updateOptionsLocked(Bundle options) {
if (options != null) {
if (pendingOptions != null) {
pendingOptions.abort();
}
pendingOptions = new ActivityOptions(options);
}
}
void updateOptionsLocked(ActivityOptions options) {
if (options != null) {
if (pendingOptions != null) {
pendingOptions.abort();
}
pendingOptions = options;
}
}
void applyOptionsLocked() {
if (pendingOptions != null) {
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_SCALE_UP:
service.mWindowManager.overridePendingAppTransitionScaleUp(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getStartWidth(), pendingOptions.getStartHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
pendingOptions.getStartX()+pendingOptions.getStartWidth(),
pendingOptions.getStartY()+pendingOptions.getStartHeight()));
}
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;
}
pendingOptions = 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 updateThumbnail(Bitmap newThumbnail, CharSequence description) {
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// This is a logical break in the task; it repre
}
if (thumbHolder != null) {
if (newThumbnail != null) {
if (ActivityManagerService.DEBUG_THUMBNAILS) Slog.i(ActivityManagerService.TAG,
"Setting thumbnail of " + this + " holder " + thumbHolder
+ " to " + newThumbnail);
thumbHolder.lastThumbnail = newThumbnail;
}
thumbHolder.lastDescription = description;
}
}
void startLaunchTickingLocked() {
if (ActivityManagerService.IS_USER_BUILD) {
return;
}
if (launchTickTime == 0) {
launchTickTime = SystemClock.uptimeMillis();
continueLaunchTickingLocked();
}
}
boolean continueLaunchTickingLocked() {
if (launchTickTime != 0) {
Message msg = stack.mHandler.obtainMessage(ActivityStack.LAUNCH_TICK_MSG);
msg.obj = this;
stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG);
stack.mHandler.sendMessageDelayed(msg, ActivityStack.LAUNCH_TICK);
return true;
}
return false;
}
void finishLaunchTickingLocked() {
launchTickTime = 0;
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 windowsDrawn() {
synchronized(service) {
if (launchTime != 0) {
final long curTime = SystemClock.uptimeMillis();
final long thisTime = curTime - launchTime;
final long totalTime = stack.mInitialStartTime != 0
? (curTime - stack.mInitialStartTime) : thisTime;
if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
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(ActivityManagerService.TAG, sb.toString());
}
stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
if (totalTime > 0) {
service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
}
launchTime = 0;
stack.mInitialStartTime = 0;
}
startTime = 0;
finishLaunchTickingLocked();
}
}
public void windowsVisible() {
synchronized(service) {
stack.reportActivityVisibleLocked(this);
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG, "windowsVisible(): " + 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.
stack.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 N = stack.mWaitingVisibleActivities.size();
if (N > 0) {
for (int i=0; i<N; i++) {
ActivityRecord r = (ActivityRecord)
stack.mWaitingVisibleActivities.get(i);
r.waitingVisible = false;
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG,
"Was waiting for visible: " + r);
}
stack.mWaitingVisibleActivities.clear();
Message msg = Message.obtain();
msg.what = ActivityStack.IDLE_NOW_MSG;
stack.mHandler.sendMessage(msg);
}
}
service.scheduleAppGcsLocked();
}
}
}
public void windowsGone() {
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG, "windowsGone(): " + this);
nowVisible = false;
}
private ActivityRecord getWaitingHistoryRecordLocked() {
// First find the real culprit... if we are waiting
// for another app to start, then we have paused dispatching
// for this activity.
ActivityRecord r = this;
if (r.waitingVisible) {
// Hmmm, who might we be waiting for?
r = stack.mResumedActivity;
if (r == null) {
r = stack.mPausingActivity;
}
// Both of those null? Fall back to 'this' again
if (r == null) {
r = this;
}
}
return r;
}
public boolean keyDispatchingTimedOut() {
// TODO: Unify this code with ActivityManagerService.inputDispatchingTimedOut().
ActivityRecord r;
ProcessRecord anrApp = null;
synchronized(service) {
r = getWaitingHistoryRecordLocked();
if (r != null && r.app != null) {
if (r.app.debugging) {
return false;
}
if (service.mDidDexOpt) {
// Give more time since we were dexopting.
service.mDidDexOpt = false;
return false;
}
if (r.app.instrumentationClass == null) {
anrApp = r.app;
} else {
Bundle info = new Bundle();
info.putString("shortMsg", "keyDispatchingTimedOut");
info.putString("longMsg", "Timed out while dispatching key event");
service.finishInstrumentationLocked(
r.app, Activity.RESULT_CANCELED, info);
}
}
}
if (anrApp != null) {
service.appNotResponding(anrApp, r, this, false, "keyDispatchingTimedOut");
}
return true;
}
/** Returns the key dispatching timeout for this application token. */
public long getKeyDispatchingTimeout() {
synchronized(service) {
ActivityRecord r = getWaitingHistoryRecordLocked();
if (r != null && r.app != null
&& (r.app.instrumentationClass != null || r.app.usingWrapper)) {
return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
}
return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
}
}
/**
* 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 && !stack.mGoingToSleepActivities.contains(this)) {
stack.mGoingToSleepActivities.add(this);
}
sleeping = _sleeping;
} catch (RemoteException e) {
Slog.w(ActivityStack.TAG, "Exception thrown when sleeping: "
+ intent.getComponent(), e);
}
}
}
public String toString() {
if (stringName != null) {
return stringName;
}
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());
sb.append('}');
return stringName = sb.toString();
}
}