summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2010-09-09 21:25:35 -0700
committerDianne Hackborn <hackbod@google.com>2010-09-09 21:25:35 -0700
commit0dad364adb9e9cbc2f7fa115602552f4897387ad (patch)
tree2f7d9825ab2261d3349250b4d09c94acc005f035 /services
parentee455f5a9572bc0d23c3328f6c22da91dc109a50 (diff)
downloadframeworks_base-0dad364adb9e9cbc2f7fa115602552f4897387ad.zip
frameworks_base-0dad364adb9e9cbc2f7fa115602552f4897387ad.tar.gz
frameworks_base-0dad364adb9e9cbc2f7fa115602552f4897387ad.tar.bz2
Add toast when an app intercepts the launch of another app.
The activity manager looks for cases where one app launches immediately after another. If this happens, a brief toast is shown telling the user when app is actually running and what was originally starting. Change-Id: If94cf5bd393dd0bc0f09789dae044fde1386c481
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java25
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java25
-rw-r--r--services/java/com/android/server/am/ActivityStack.java46
-rw-r--r--services/java/com/android/server/am/LaunchWarningWindow.java36
4 files changed, 118 insertions, 14 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3142ee4..483941d 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -752,6 +752,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean mWaitingUpdate = false;
boolean mDidUpdate = false;
boolean mOnBattery = false;
+ boolean mLaunchWarningShown = false;
Context mContext;
@@ -2902,6 +2903,30 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
+ if (!mLaunchWarningShown) {
+ mLaunchWarningShown = true;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ final Dialog d = new LaunchWarningWindow(mContext, cur, next);
+ d.show();
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ d.dismiss();
+ mLaunchWarningShown = false;
+ }
+ }
+ }, 4000);
+ }
+ }
+ });
+ }
+ }
+
final void decPersistentCountLocked(ProcessRecord app) {
app.persistentActivities--;
if (app.persistentActivities > 0) {
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 1687db1..9358469 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -34,6 +34,7 @@ import android.os.SystemClock;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.TimeUtils;
import android.view.IApplicationToken;
import java.io.PrintWriter;
@@ -68,7 +69,8 @@ class ActivityRecord extends IApplicationToken.Stub {
int icon; // resource identifier of activity's icon.
int theme; // resource identifier of activity's theme.
TaskRecord task; // the task this is in.
- long startTime; // when we starting launching this activity
+ long launchTime; // when we starting launching this activity
+ long startTime; // last time this activity was started
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
Configuration configuration; // configuration activity was last running in
ActivityRecord resultTo; // who started this entry, so will get our reply
@@ -165,6 +167,11 @@ class ActivityRecord extends IApplicationToken.Stub {
pw.print(" frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
pw.print(" idle="); pw.println(idle);
+ if (launchTime != 0 || startTime != 0) {
+ pw.print(prefix); pw.print("launchTime=");
+ TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime=");
+ TimeUtils.formatDuration(startTime, pw); pw.println("");
+ }
if (waitingVisible || nowVisible) {
pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
pw.print(" nowVisible="); pw.println(nowVisible);
@@ -417,9 +424,9 @@ class ActivityRecord extends IApplicationToken.Stub {
public void windowsVisible() {
synchronized(service) {
- if (startTime != 0) {
+ if (launchTime != 0) {
final long curTime = SystemClock.uptimeMillis();
- final long thisTime = curTime - startTime;
+ final long thisTime = curTime - launchTime;
final long totalTime = stack.mInitialStartTime != 0
? (curTime - stack.mInitialStartTime) : thisTime;
if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
@@ -428,22 +435,24 @@ class ActivityRecord extends IApplicationToken.Stub {
thisTime, totalTime);
StringBuilder sb = service.mStringBuilder;
sb.setLength(0);
- sb.append("Displayed activity ");
+ sb.append("Displayed ");
sb.append(shortComponentName);
sb.append(": ");
- sb.append(thisTime);
- sb.append(" ms (total ");
+ TimeUtils.formatDuration(thisTime, sb);
+ sb.append(" (total ");
+ TimeUtils.formatDuration(totalTime, sb);
sb.append(totalTime);
- sb.append(" ms)");
+ sb.append(")");
Log.i(ActivityManagerService.TAG, sb.toString());
}
stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
if (totalTime > 0) {
service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
}
- startTime = 0;
+ launchTime = 0;
stack.mInitialStartTime = 0;
}
+ startTime = 0;
stack.reportActivityVisibleLocked(this);
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG, "windowsVisible(): " + this);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 5f79b85..a0c21dd 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -102,6 +102,10 @@ public class ActivityStack {
// 30 minutes.
static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+ // How long between activity launches that we consider safe to not warn
+ // the user about an unexpected activity being launched on top.
+ static final long START_WARN_TIME = 5*1000;
+
// Set to false to disable the preview that is shown while a new activity
// is being started.
static final boolean SHOW_APP_STARTING_PREVIEW = true;
@@ -213,6 +217,13 @@ public class ActivityStack {
ActivityRecord mResumedActivity = null;
/**
+ * This is the last activity that has been started. It is only used to
+ * identify when multiple activities are started at once so that the user
+ * can be warned they may not be in the activity they think they are.
+ */
+ ActivityRecord mLastStartedActivity = null;
+
+ /**
* Set when we know we are going to be calling updateConfiguration()
* soon, so want to skip intermediate config checks.
*/
@@ -586,10 +597,10 @@ public class ActivityStack {
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid);
- if (r.startTime == 0) {
- r.startTime = SystemClock.uptimeMillis();
+ if (r.launchTime == 0) {
+ r.launchTime = SystemClock.uptimeMillis();
if (mInitialStartTime == 0) {
- mInitialStartTime = r.startTime;
+ mInitialStartTime = r.launchTime;
}
} else if (mInitialStartTime == 0) {
mInitialStartTime = SystemClock.uptimeMillis();
@@ -1090,6 +1101,31 @@ public class ActivityStack {
return false;
}
+ // Okay we are now going to start a switch, to 'next'. We may first
+ // have to pause the current activity, but this is an important point
+ // where we have decided to go to 'next' so keep track of that.
+ if (mLastStartedActivity != null) {
+ long now = SystemClock.uptimeMillis();
+ final boolean inTime = mLastStartedActivity.startTime != 0
+ && (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
+ final int lastUid = mLastStartedActivity.info.applicationInfo.uid;
+ final int nextUid = next.info.applicationInfo.uid;
+ if (inTime && lastUid != nextUid
+ && lastUid != next.launchedFromUid
+ && mService.checkPermission(
+ android.Manifest.permission.STOP_APP_SWITCHES,
+ -1, next.launchedFromUid)
+ != PackageManager.PERMISSION_GRANTED) {
+ mService.showLaunchWarningLocked(mLastStartedActivity, next);
+ } else {
+ next.startTime = now;
+ mLastStartedActivity = next;
+ }
+ } else {
+ next.startTime = SystemClock.uptimeMillis();
+ mLastStartedActivity = next;
+ }
+
// We need to start pausing the current activity so the top one
// can be resumed...
if (mResumedActivity != null) {
@@ -1314,7 +1350,6 @@ public class ActivityStack {
if (!newTask) {
// If starting in an existing task, find where that is...
- ActivityRecord next = null;
boolean startIt = true;
for (int i = NH-1; i >= 0; i--) {
ActivityRecord p = (ActivityRecord)mHistory.get(i);
@@ -1342,14 +1377,13 @@ public class ActivityStack {
if (p.fullscreen) {
startIt = false;
}
- next = p;
}
}
// Place a new activity at top of stack, so it is next to interact
// with the user.
if (addPos < 0) {
- addPos = mHistory.size();
+ addPos = NH;
}
// If we are not placing the new activity frontmost, we do not want
diff --git a/services/java/com/android/server/am/LaunchWarningWindow.java b/services/java/com/android/server/am/LaunchWarningWindow.java
new file mode 100644
index 0000000..4130e33
--- /dev/null
+++ b/services/java/com/android/server/am/LaunchWarningWindow.java
@@ -0,0 +1,36 @@
+package com.android.server.am;
+
+import com.android.internal.R;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class LaunchWarningWindow extends Dialog {
+ public LaunchWarningWindow(Context context, ActivityRecord cur, ActivityRecord next) {
+ super(context, R.style.Theme_Toast);
+
+ requestWindowFeature(Window.FEATURE_LEFT_ICON);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+
+ setContentView(R.layout.launch_warning);
+ setTitle(context.getText(R.string.launch_warning_title));
+ getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
+ R.drawable.ic_dialog_alert);
+ ImageView icon = (ImageView)findViewById(R.id.replace_app_icon);
+ icon.setImageDrawable(next.info.applicationInfo.loadIcon(context.getPackageManager()));
+ TextView text = (TextView)findViewById(R.id.replace_message);
+ text.setText(context.getResources().getString(R.string.launch_warning_replace,
+ next.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
+ icon = (ImageView)findViewById(R.id.original_app_icon);
+ icon.setImageDrawable(cur.info.applicationInfo.loadIcon(context.getPackageManager()));
+ text = (TextView)findViewById(R.id.original_message);
+ text.setText(context.getResources().getString(R.string.launch_warning_original,
+ cur.info.applicationInfo.loadLabel(context.getPackageManager()).toString()));
+ }
+}