diff options
Diffstat (limited to 'services/java/com/android')
30 files changed, 4061 insertions, 2484 deletions
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index 6eea928..9966570 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -86,9 +86,12 @@ import java.util.Set; class AppWidgetServiceImpl { + private static final String KEYGUARD_HOST_PACKAGE = "com.android.keyguard"; + private static final int KEYGUARD_HOST_ID = 0x4b455947; private static final String TAG = "AppWidgetServiceImpl"; private static final String SETTINGS_FILENAME = "appwidgets.xml"; private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes + private static final int CURRENT_VERSION = 1; // Bump if the stored widgets need to be upgraded. private static boolean DBG = false; @@ -1581,7 +1584,7 @@ class AppWidgetServiceImpl { out.setOutput(stream, "utf-8"); out.startDocument(null, true); out.startTag(null, "gs"); - + out.attribute(null, "version", String.valueOf(CURRENT_VERSION)); int providerIndex = 0; N = mInstalledProviders.size(); for (int i = 0; i < N; i++) { @@ -1650,6 +1653,7 @@ class AppWidgetServiceImpl { @SuppressWarnings("unused") void readStateFromFileLocked(FileInputStream stream) { boolean success = false; + int version = 0; try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(stream, null); @@ -1661,7 +1665,14 @@ class AppWidgetServiceImpl { type = parser.next(); if (type == XmlPullParser.START_TAG) { String tag = parser.getName(); - if ("p".equals(tag)) { + if ("gs".equals(tag)) { + String attributeValue = parser.getAttributeValue(null, "version"); + try { + version = Integer.parseInt(attributeValue); + } catch (NumberFormatException e) { + version = 0; + } + } else if ("p".equals(tag)) { // TODO: do we need to check that this package has the same signature // as before? String pkg = parser.getAttributeValue(null, "pkg"); @@ -1800,6 +1811,8 @@ class AppWidgetServiceImpl { for (int i = mHosts.size() - 1; i >= 0; i--) { pruneHostLocked(mHosts.get(i)); } + // upgrade the database if needed + performUpgrade(version); } else { // failed reading, clean up Slog.w(TAG, "Failed to read state, clearing widgets and hosts."); @@ -1813,6 +1826,31 @@ class AppWidgetServiceImpl { } } + private void performUpgrade(int fromVersion) { + if (fromVersion < CURRENT_VERSION) { + Slog.v(TAG, "Upgrading widget database from " + fromVersion + " to " + CURRENT_VERSION + + " for user " + mUserId); + } + + int version = fromVersion; + + // Update 1: keyguard moved from package "android" to "com.android.keyguard" + if (version == 0) { + for (int i = 0; i < mHosts.size(); i++) { + Host host = mHosts.get(i); + if (host != null && "android".equals(host.packageName) + && host.hostId == KEYGUARD_HOST_ID) { + host.packageName = KEYGUARD_HOST_PACKAGE; + } + } + version = 1; + } + + if (version != CURRENT_VERSION) { + throw new IllegalStateException("Failed to upgrade widget database"); + } + } + static File getSettingsFile(int userId) { return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME); } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 0863bd6..75eb8a0 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -781,10 +781,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mFileManager = new InputMethodFileManager(mMethodMap, newUserId); final String defaultImiId = mSettings.getSelectedInputMethod(); final boolean needsToResetDefaultIme = TextUtils.isEmpty(defaultImiId); + // For secondary users, the list of enabled IMEs may not have been updated since the + // callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may + // not be empty even if the IME has been uninstalled by the primary user. + // Even in such cases, IMMS works fine because it will find the most applicable + // IME for that user. if (DEBUG) { Slog.d(TAG, "Switch user: " + newUserId + " current ime = " + defaultImiId); } - resetAllInternalStateLocked(false /* updateOnlyWhenLocaleChanged */, + resetAllInternalStateLocked(false /* updateOnlyWhenLocaleChanged */, needsToResetDefaultIme); } diff --git a/services/java/com/android/server/LockSettingsService.java b/services/java/com/android/server/LockSettingsService.java index e20a21f..f8e9ff7 100644 --- a/services/java/com/android/server/LockSettingsService.java +++ b/services/java/com/android/server/LockSettingsService.java @@ -49,6 +49,7 @@ import java.util.Arrays; */ public class LockSettingsService extends ILockSettings.Stub { + private static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"; private final DatabaseHelper mOpenHelper; private static final String TAG = "LockSettingsService"; @@ -99,29 +100,12 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private static final void checkWritePermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) { - throw new SecurityException("uid=" + callingUid - + " not authorized to write lock settings"); - } + private final void checkWritePermission(int userId) { + mContext.checkCallingOrSelfPermission(PERMISSION); } - private static final void checkPasswordReadPermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) { - throw new SecurityException("uid=" + callingUid - + " not authorized to read lock password"); - } - } - - private static final void checkReadPermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID - && UserHandle.getUserId(callingUid) != userId) { - throw new SecurityException("uid=" + callingUid - + " not authorized to read settings of user " + userId); - } + private final void checkPasswordReadPermission(int userId) { + mContext.checkCallingOrSelfPermission(PERMISSION); } @Override diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 2e0c977..c6aca2f 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -596,6 +596,26 @@ class MountService extends IMountService.Stub } }; + private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + waitForReady(); + String action = intent.getAction(); + // Since fstrim will be run on a daily basis we do not expect + // fstrim to be too long, so it is not interruptible. We will + // implement interruption only in case we see issues. + if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) { + try { + // This method runs on the handler thread, + // so it is safe to directly call into vold. + mConnector.execute("fstrim", "dotrim"); + } catch (NativeDaemonConnectorException ndce) { + Slog.e(TAG, "Failed to run fstrim!"); + } + } + } + }; + private final class MountServiceBinderListener implements IBinder.DeathRecipient { final IMountServiceListener mListener; @@ -1301,6 +1321,12 @@ class MountService extends IMountService.Stub mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler); } + // Watch for idle maintenance changes + IntentFilter idleMaintenanceFilter = new IntentFilter(); + idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START); + mContext.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL, + idleMaintenanceFilter, null, mHandler); + // Add OBB Action Handler to MountService thread. mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper()); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4631395..46f9015 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -725,7 +725,7 @@ class ServerThread extends Thread { } catch (Throwable e) { reportWtf("starting CertBlacklister", e); } - + if (context.getResources().getBoolean( com.android.internal.R.bool.config_dreamsSupported)) { try { @@ -862,7 +862,9 @@ class ServerThread extends Thread { public void run() { Slog.i(TAG, "Making services ready"); - if (!headless) startSystemUi(contextF); + if (!headless) { + startSystemUi(contextF); + } try { if (mountServiceF != null) mountServiceF.systemReady(); } catch (Throwable e) { diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java index 09daf56..9f86863 100644 --- a/services/java/com/android/server/accounts/AccountManagerService.java +++ b/services/java/com/android/server/accounts/AccountManagerService.java @@ -1472,7 +1472,10 @@ public class AccountManagerService int userId) { // Only allow the system process to read accounts of other users if (userId != UserHandle.getCallingUserId() - && Binder.getCallingUid() != android.os.Process.myUid()) { + && Binder.getCallingUid() != android.os.Process.myUid() + && mContext.checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("User " + UserHandle.getCallingUserId() + " trying to confirm account credentials for " + userId); } @@ -1735,7 +1738,10 @@ public class AccountManagerService final int callingUid = Binder.getCallingUid(); // Only allow the system process to read accounts of other users if (userId != UserHandle.getCallingUserId() - && callingUid != android.os.Process.myUid()) { + && callingUid != android.os.Process.myUid() + && mContext.checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("User " + UserHandle.getCallingUserId() + " trying to get account for " + userId); } diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index 5c24e67..10db70f 100644 --- a/services/java/com/android/server/am/ActiveServices.java +++ b/services/java/com/android/server/am/ActiveServices.java @@ -439,7 +439,7 @@ public class ActiveServices { ActivityRecord activity = null; if (token != null) { - activity = mAm.mMainStack.isInStackLocked(token); + activity = ActivityRecord.isInStackLocked(token); if (activity == null) { Slog.w(TAG, "Binding with unknown activity: " + token); return 0; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 6f092bf..bd85b77 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -31,12 +31,14 @@ import com.android.server.Watchdog; import com.android.server.am.ActivityStack.ActivityState; import com.android.server.pm.UserManagerService; import com.android.server.wm.AppTransition; +import com.android.server.wm.StackBox; import com.android.server.wm.WindowManagerService; import dalvik.system.Zygote; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManagerNative; import android.app.ActivityOptions; import android.app.ActivityThread; @@ -196,7 +198,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false; static final boolean DEBUG_MU = localLOGV || false; static final boolean DEBUG_IMMERSIVE = localLOGV || false; - static final boolean VALIDATE_TOKENS = false; + static final boolean VALIDATE_TOKENS = true; static final boolean SHOW_ACTIVITY_START_TIME = true; // Control over CPU and battery monitoring. @@ -206,6 +208,8 @@ public final class ActivityManagerService extends ActivityManagerNative static final long MONITOR_CPU_MAX_TIME = 0x0fffffff; // wait possibly forever for next cpu sample. static final boolean MONITOR_THREAD_CPU_USAGE = false; + static final int HOME_ACTIVITY_STACK = 0; + // The flags that are set for all calls we make to the package manager. static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES; @@ -268,10 +272,18 @@ public final class ActivityManagerService extends ActivityManagerNative static final int PENDING_ACTIVITY_RESULT_TIMEOUT = 2*2000; static final int MY_PID = Process.myPid(); - + static final String[] EMPTY_STRING_ARRAY = new String[0]; - public ActivityStack mMainStack; + /** All of the stacks in the system */ + final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>(); + + /** Identifier counter for all ActivityStacks */ + private int mLastStackId = 0; + + /** The current stack for manipulating */ + public ActivityStack mFocusedStack; + public ActivityStack mHomeStack; private final boolean mHeadless; @@ -285,9 +297,18 @@ public final class ActivityManagerService extends ActivityManagerNative * due to app switches being disabled. */ static class PendingActivityLaunch { - ActivityRecord r; - ActivityRecord sourceRecord; - int startFlags; + final ActivityRecord r; + final ActivityRecord sourceRecord; + final int startFlags; + final ActivityStack stack; + + public PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord, + int _startFlags, ActivityStack _stack) { + r = _r; + sourceRecord = _sourceRecord; + startFlags = _startFlags; + stack = _stack; + } } final ArrayList<PendingActivityLaunch> mPendingActivityLaunches @@ -328,7 +349,7 @@ public final class ActivityManagerService extends ActivityManagerNative /** * List of intents that were used to start the most recent tasks. */ - final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>(); + private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>(); public class PendingActivityExtras extends Binder implements Runnable { public final ActivityRecord activity; @@ -501,11 +522,6 @@ public final class ActivityManagerService extends ActivityManagerNative final CompatModePackages mCompatModePackages; /** - * Set of PendingResultRecord objects that are currently active. - */ - final HashSet mPendingResultRecords = new HashSet(); - - /** * Set of IntentSenderRecord objects that are currently active. */ final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords @@ -534,7 +550,8 @@ public final class ActivityManagerService extends ActivityManagerNative * broadcasts. Hash keys are the receiver IBinder, hash value is * a ReceiverList. */ - final HashMap mRegisteredReceivers = new HashMap(); + final HashMap<IBinder, ReceiverList> mRegisteredReceivers = + new HashMap<IBinder, ReceiverList>(); /** * Resolver for broadcast intents to registered receivers. @@ -596,13 +613,8 @@ public final class ActivityManagerService extends ActivityManagerNative * List of PendingThumbnailsRecord objects of clients who are still * waiting to receive all of the thumbnails for a task. */ - final ArrayList mPendingThumbnails = new ArrayList(); - - /** - * List of HistoryRecord objects that have been finished and must - * still report back to a pending thumbnail receiver. - */ - final ArrayList mCancelledThumbnails = new ArrayList(); + final ArrayList<PendingThumbnailsRecord> mPendingThumbnails = + new ArrayList<PendingThumbnailsRecord>(); final ProviderMap mProviderMap; @@ -753,7 +765,7 @@ public final class ActivityManagerService extends ActivityManagerNative * todo: Replace this with a TokenSpace class that generates non-repeating * integers that won't wrap. */ - int mCurTask = 1; + private int mCurTask = 0; /** * Current sequence id for oom_adj computation traversal. @@ -866,8 +878,9 @@ public final class ActivityManagerService extends ActivityManagerNative static ActivityManagerService mSelf; static ActivityThread mSystemThread; + private Looper mLooper; + private int mCurrentUserId = 0; - private int[] mCurrentUserArray = new int[] { 0 }; private UserManagerService mUserManager; private final class AppDeathRecipient implements IBinder.DeathRecipient { @@ -885,6 +898,7 @@ public final class ActivityManagerService extends ActivityManagerNative mAppThread = thread; } + @Override public void binderDied() { if (localLOGV) Slog.v( TAG, "Death received in " + this @@ -937,10 +951,11 @@ public final class ActivityManagerService extends ActivityManagerNative // if (localLOGV) Slog.v(TAG, "Handler started!"); //} + @Override public void handleMessage(Message msg) { switch (msg.what) { case SHOW_ERROR_MSG: { - HashMap data = (HashMap) msg.obj; + HashMap<String, Object> data = (HashMap<String, Object>) msg.obj; boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; synchronized (ActivityManagerService.this) { @@ -980,7 +995,7 @@ public final class ActivityManagerService extends ActivityManagerNative } break; case SHOW_NOT_RESPONDING_MSG: { synchronized (ActivityManagerService.this) { - HashMap data = (HashMap) msg.obj; + HashMap<String, Object> data = (HashMap<String, Object>) msg.obj; ProcessRecord proc = (ProcessRecord)data.get("app"); if (proc != null && proc.anrDialog != null) { Slog.e(TAG, "App already has anr dialog: " + proc); @@ -1406,7 +1421,7 @@ public final class ActivityManagerService extends ActivityManagerNative public static void setSystemProcess() { try { ActivityManagerService m = mSelf; - + ServiceManager.addService("activity", m, true); ServiceManager.addService("meminfo", new MemBinder(m)); ServiceManager.addService("gfxinfo", new GraphicsBinder(m)); @@ -1442,6 +1457,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void setWindowManager(WindowManagerService wm) { mWindowManager = wm; + wm.createStack(HOME_ACTIVITY_STACK, -1, StackBox.TASK_STACK_GOES_OVER, 1.0f); } public static final Context main(int factoryTest) { @@ -1465,8 +1481,12 @@ public final class ActivityManagerService extends ActivityManagerNative context.setTheme(android.R.style.Theme_Holo); m.mContext = context; m.mFactoryTest = factoryTest; - m.mMainStack = new ActivityStack(m, context, true, thr.mLooper); - + m.mLooper = thr.mLooper; + + m.mHomeStack = m.mFocusedStack + = new ActivityStack(m, context, true, thr.mLooper, HOME_ACTIVITY_STACK); + m.mStacks.add(m.mFocusedStack); + m.mBatteryStatsService.publish(context); m.mUsageStatsService.publish(context); m.mAppOpsService.publish(context); @@ -1477,14 +1497,14 @@ public final class ActivityManagerService extends ActivityManagerNative } m.startRunning(null, null, null, null); - + return context; } public static ActivityManagerService self() { return mSelf; } - + static class AThread extends Thread { ActivityManagerService mService; Looper mLooper; @@ -1494,6 +1514,7 @@ public final class ActivityManagerService extends ActivityManagerNative super("ActivityManager"); } + @Override public void run() { Looper.prepare(); @@ -1614,7 +1635,7 @@ public final class ActivityManagerService extends ActivityManagerNative private ActivityManagerService() { Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); - + mFgBroadcastQueue = new BroadcastQueue(this, "foreground", BROADCAST_FG_TIMEOUT); mBgBroadcastQueue = new BroadcastQueue(this, "background", BROADCAST_BG_TIMEOUT); mBroadcastQueues[0] = mFgBroadcastQueue; @@ -1652,7 +1673,7 @@ public final class ActivityManagerService extends ActivityManagerNative mConfigurationSeq = mConfiguration.seq = 1; mProcessStats.init(); - + mCompatModePackages = new CompatModePackages(this, systemDir); // Add ourself to the Watchdog monitors. @@ -1777,7 +1798,7 @@ public final class ActivityManagerService extends ActivityManagerNative (softIrq * 100) / total); } } - + long[] cpuSpeedTimes = mProcessStats.getLastCpuSpeedTimes(); final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics(); synchronized(bstats) { @@ -2296,7 +2317,7 @@ public final class ActivityManagerService extends ActivityManagerNative aInfo.applicationInfo.uid); if (app == null || app.instrumentationClass == null) { intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); - mMainStack.startActivityLocked(null, intent, null, aInfo, + mHomeStack.startActivityLocked(null, intent, null, aInfo, null, null, 0, 0, 0, null, 0, null, false, null); } } @@ -2374,7 +2395,7 @@ public final class ActivityManagerService extends ActivityManagerNative intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setComponent(new ComponentName( ri.activityInfo.packageName, ri.activityInfo.name)); - mMainStack.startActivityLocked(null, intent, null, ri.activityInfo, + mFocusedStack.startActivityLocked(null, intent, null, ri.activityInfo, null, null, 0, 0, 0, null, 0, null, false, null); } } @@ -2504,8 +2525,8 @@ public final class ActivityManagerService extends ActivityManagerNative } for (int i=0; i<N; i++) { PendingActivityLaunch pal = mPendingActivityLaunches.get(i); - mMainStack.startActivityUncheckedLocked(pal.r, pal.sourceRecord, - pal.startFlags, doResume && i == (N-1), null); + pal.stack.startActivityUncheckedLocked(pal.r, pal.sourceRecord, pal.startFlags, + doResume && i == (N-1), null); } mPendingActivityLaunches.clear(); } @@ -2519,6 +2540,7 @@ public final class ActivityManagerService extends ActivityManagerNative startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId()); } + @Override public final int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, @@ -2526,11 +2548,12 @@ public final class ActivityManagerService extends ActivityManagerNative enforceNotIsolatedCaller("startActivity"); userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "startActivity", null); - return mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, + return mFocusedStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, null, null, options, userId); } + @Override public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, @@ -2539,12 +2562,13 @@ public final class ActivityManagerService extends ActivityManagerNative userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "startActivityAndWait", null); WaitResult res = new WaitResult(); - mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, + mFocusedStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, res, null, options, UserHandle.getCallingUserId()); return res; } + @Override public final int startActivityWithConfig(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, Configuration config, @@ -2552,8 +2576,8 @@ public final class ActivityManagerService extends ActivityManagerNative enforceNotIsolatedCaller("startActivityWithConfig"); userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "startActivityWithConfig", null); - int ret = mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, - resultTo, resultWho, requestCode, startFlags, + int ret = mFocusedStack.startActivityMayWait(caller, -1, callingPackage, intent, + resolvedType, resultTo, resultWho, requestCode, startFlags, null, null, null, config, options, userId); return ret; } @@ -2578,8 +2602,8 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { // If this is coming from the currently resumed activity, it is // effectively saying that app switches are allowed at this point. - if (mMainStack.mResumedActivity != null - && mMainStack.mResumedActivity.info.applicationInfo.uid == + if (mFocusedStack.mResumedActivity != null + && mFocusedStack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) { mAppSwitchesAllowedTime = 0; } @@ -2597,7 +2621,7 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized (this) { - ActivityRecord r = mMainStack.isInStackLocked(callingActivity); + final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity); if (r == null) { ActivityOptions.abort(options); return false; @@ -2671,7 +2695,7 @@ public final class ActivityManagerService extends ActivityManagerNative } final long origId = Binder.clearCallingIdentity(); - int res = mMainStack.startActivityLocked(r.app.thread, intent, + int res = r.task.stack.startActivityLocked(r.app.thread, intent, r.resolvedType, aInfo, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0, options, false, null); @@ -2692,7 +2716,7 @@ public final class ActivityManagerService extends ActivityManagerNative userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "startActivityInPackage", null); - int ret = mMainStack.startActivityMayWait(null, uid, callingPackage, intent, resolvedType, + int ret = mFocusedStack.startActivityMayWait(null, uid, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, null, null, null, null, options, userId); return ret; @@ -2704,7 +2728,7 @@ public final class ActivityManagerService extends ActivityManagerNative enforceNotIsolatedCaller("startActivities"); userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "startActivity", null); - int ret = mMainStack.startActivities(caller, -1, callingPackage, intents, + int ret = mFocusedStack.startActivities(caller, -1, callingPackage, intents, resolvedTypes, resultTo, options, userId); return ret; } @@ -2715,7 +2739,7 @@ public final class ActivityManagerService extends ActivityManagerNative userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "startActivityInPackage", null); - int ret = mMainStack.startActivities(null, uid, callingPackage, intents, resolvedTypes, + int ret = mFocusedStack.startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, options, userId); return ret; } @@ -2751,7 +2775,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void setRequestedOrientation(IBinder token, int requestedOrientation) { synchronized (this) { - ActivityRecord r = mMainStack.isInStackLocked(token); + ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { return; } @@ -2763,7 +2787,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (config != null) { r.frozenBeforeDestroy = true; if (!updateConfigurationLocked(config, r, false, false)) { - mMainStack.resumeTopActivityLocked(null); + r.task.stack.resumeTopActivityLocked(null); } } Binder.restoreCallingIdentity(origId); @@ -2772,7 +2796,7 @@ public final class ActivityManagerService extends ActivityManagerNative public int getRequestedOrientation(IBinder token) { synchronized (this) { - ActivityRecord r = mMainStack.isInStackLocked(token); + ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } @@ -2789,6 +2813,7 @@ public final class ActivityManagerService extends ActivityManagerNative * * @return Returns true if the activity successfully finished, or false if it is still running. */ + @Override public final boolean finishActivity(IBinder token, int resultCode, Intent resultData) { // Refuse possible leaked file descriptors if (resultData != null && resultData.hasFileDescriptors() == true) { @@ -2796,9 +2821,13 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized(this) { + ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return true; + } if (mController != null) { // Find the first activity that is not finishing. - ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0); + ActivityRecord next = r.task.stack.topRunningActivityLocked(token, 0); if (next != null) { // ask watcher if this is allowed boolean resumeOK = true; @@ -2814,13 +2843,14 @@ public final class ActivityManagerService extends ActivityManagerNative } } final long origId = Binder.clearCallingIdentity(); - boolean res = mMainStack.requestFinishActivityLocked(token, resultCode, + boolean res = r.task.stack.requestFinishActivityLocked(token, resultCode, resultData, "app-request", true); Binder.restoreCallingIdentity(origId); return res; } } + @Override public final void finishHeavyWeightApp() { if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) != PackageManager.PERMISSION_GRANTED) { @@ -2842,11 +2872,8 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<activities.size(); i++) { ActivityRecord r = activities.get(i); if (!r.finishing) { - int index = mMainStack.indexOfTokenLocked(r.appToken); - if (index >= 0) { - mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED, - null, "finish-heavy", true); - } + r.task.stack.finishActivityLocked(r, Activity.RESULT_CANCELED, + null, "finish-heavy", true); } } @@ -2914,44 +2941,49 @@ public final class ActivityManagerService extends ActivityManagerNative } } + @Override public final void finishSubActivity(IBinder token, String resultWho, int requestCode) { synchronized(this) { final long origId = Binder.clearCallingIdentity(); - mMainStack.finishSubActivityLocked(token, resultWho, requestCode); + ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + r.task.stack.finishSubActivityLocked(r, resultWho, requestCode); + } Binder.restoreCallingIdentity(origId); } } + @Override public boolean finishActivityAffinity(IBinder token) { synchronized(this) { final long origId = Binder.clearCallingIdentity(); - boolean res = mMainStack.finishActivityAffinityLocked(token); + ActivityRecord r = ActivityRecord.isInStackLocked(token); + boolean res = false; + if (r != null) { + res = r.task.stack.finishActivityAffinityLocked(r); + } Binder.restoreCallingIdentity(origId); return res; } } + @Override public boolean willActivityBeVisible(IBinder token) { synchronized(this) { - int i; - for (i=mMainStack.mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - if (r.appToken == token) { - return true; - } - if (r.fullscreen && !r.finishing) { - return false; - } + ActivityStack stack = ActivityRecord.getStackLocked(token); + if (stack != null) { + return stack.willActivityBeVisibleLocked(token); } - return true; + return false; } } - + + @Override public void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim) { synchronized(this) { - ActivityRecord self = mMainStack.isInStackLocked(token); + ActivityRecord self = ActivityRecord.isInStackLocked(token); if (self == null) { return; } @@ -2985,20 +3017,37 @@ public final class ActivityManagerService extends ActivityManagerNative } // Just in case... - if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) { - if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG, - "App died while pausing: " + mMainStack.mPausingActivity); - mMainStack.mPausingActivity = null; - } - if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) { - mMainStack.mLastPausedActivity = null; - } + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + if (stack.mPausingActivity != null && stack.mPausingActivity.app == app) { + if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG, + "App died while pausing: " + stack.mPausingActivity); + stack.mPausingActivity = null; + } + if (stack.mLastPausedActivity != null && stack.mLastPausedActivity.app == app) { + stack.mLastPausedActivity = null; + } - // Remove this application's activities from active lists. - boolean hasVisibleActivities = mMainStack.removeHistoryRecordsForAppLocked(app); + // Remove this application's activities from active lists. + boolean hasVisibleActivities = stack.removeHistoryRecordsForAppLocked(app); + + if (!restarting) { + if (!stack.resumeTopActivityLocked(null)) { + // If there was nothing to resume, and we are not already + // restarting this process, but there is a visible activity that + // is hosted by the process... then make sure all visible + // activities are running, taking care of restarting this + // process. + if (hasVisibleActivities) { + stack.ensureActivitiesVisibleLocked(null, 0); + } + } + } + } app.activities.clear(); - + if (app.instrumentationClass != null) { Slog.w(TAG, "Crash of app " + app.processName + " running instrumentation " + app.instrumentationClass); @@ -3006,19 +3055,6 @@ public final class ActivityManagerService extends ActivityManagerNative info.putString("shortMsg", "Process crashed."); finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info); } - - if (!restarting) { - if (!mMainStack.resumeTopActivityLocked(null)) { - // If there was nothing to resume, and we are not already - // restarting this process, but there is a visible activity that - // is hosted by the process... then make sure all visible - // activities are running, taking care of restarting this - // process. - if (hasVisibleActivities) { - mMainStack.ensureActivitiesVisibleLocked(null, 0); - } - } - } } private final int getLRURecordIndexForAppLocked(IApplicationThread thread) { @@ -3724,12 +3760,10 @@ public final class ActivityManagerService extends ActivityManagerNative } mWindowManager.closeSystemDialogs(reason); - for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) { - r.stack.finishActivityLocked(r, i, - Activity.RESULT_CANCELED, null, "close-sys", true); - } + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.closeSystemDialogsLocked(); } broadcastIntentLocked(null, null, intent, null, @@ -3937,36 +3971,15 @@ public final class ActivityManagerService extends ActivityManagerNative boolean didSomething = killPackageProcessesLocked(name, appId, userId, -100, callerWillRestart, true, doit, evenPersistent, name == null ? ("force stop user " + userId) : ("force stop " + name)); - - TaskRecord lastTask = null; - for (i=0; i<mMainStack.mHistory.size(); i++) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - final boolean samePackage = r.packageName.equals(name) - || (name == null && r.userId == userId); - if ((userId == UserHandle.USER_ALL || r.userId == userId) - && (samePackage || r.task == lastTask) - && (r.app == null || evenPersistent || !r.app.persistent)) { + + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + if (stack.forceStopPackageLocked(name, doit, evenPersistent, userId)) { if (!doit) { - if (r.finishing) { - // If this activity is just finishing, then it is not - // interesting as far as something to stop. - continue; - } return true; } didSomething = true; - Slog.i(TAG, " Force finishing activity " + r); - if (samePackage) { - if (r.app != null) { - r.app.removed = true; - } - r.app = null; - } - lastTask = r.task; - if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, - null, "force-stop", true)) { - i--; - } } } @@ -4055,8 +4068,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (mBooted) { - mMainStack.resumeTopActivityLocked(null); - mMainStack.scheduleIdleLocked(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.resumeTopActivityLocked(null); + stack.scheduleIdleLocked(); + } } } @@ -4309,14 +4325,14 @@ public final class ActivityManagerService extends ActivityManagerNative boolean didSomething = false; // See if the top visible activity is waiting to run in this process... - ActivityRecord hr = mMainStack.topRunningActivityLocked(null); + ActivityRecord hr = mFocusedStack.topRunningActivityLocked(null); if (hr != null && normalMode) { if (hr.app == null && app.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { try { if (mHeadless) { Slog.e(TAG, "Starting activities not supported on headless device: " + hr); - } else if (mMainStack.realStartActivityLocked(hr, app, true, true)) { + } else if (mFocusedStack.realStartActivityLocked(hr, app, true, true)) { didSomething = true; } } catch (Exception e) { @@ -4325,7 +4341,7 @@ public final class ActivityManagerService extends ActivityManagerNative badApp = true; } } else { - mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0); + mFocusedStack.ensureActivitiesVisibleLocked(hr, null, processName, 0); } } @@ -4385,13 +4401,15 @@ public final class ActivityManagerService extends ActivityManagerNative } } + @Override public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) { final long origId = Binder.clearCallingIdentity(); - ActivityRecord r = mMainStack.activityIdleInternal(token, false, config); if (stopProfiling) { synchronized (this) { - if (mProfileProc == r.app) { - if (mProfileFd != null) { + ActivityStack stack = ActivityRecord.getStackLocked(token); + if (stack != null) { + ActivityRecord r = stack.activityIdleInternal(token, false, config); + if ((mProfileProc == r.app) && (mProfileFd != null)) { try { mProfileFd.close(); } catch (IOException e) { @@ -4428,7 +4446,7 @@ public final class ActivityManagerService extends ActivityManagerNative mLockScreenShown = false; comeOutOfSleepIfNeededLocked(); } - mMainStack.dismissKeyguardOnNextActivityLocked(); + mFocusedStack.dismissKeyguardOnNextActivityLocked(); } } finally { Binder.restoreCallingIdentity(token); @@ -4514,18 +4532,31 @@ public final class ActivityManagerService extends ActivityManagerNative } } + @Override public final void activityResumed(IBinder token) { final long origId = Binder.clearCallingIdentity(); - mMainStack.activityResumed(token); + synchronized(this) { + ActivityStack stack = ActivityRecord.getStackLocked(token); + if (stack != null) { + stack.activityResumedLocked(token); + } + } Binder.restoreCallingIdentity(origId); } + @Override public final void activityPaused(IBinder token) { final long origId = Binder.clearCallingIdentity(); - mMainStack.activityPaused(token, false); + synchronized(this) { + ActivityStack stack = ActivityRecord.getStackLocked(token); + if (stack != null) { + stack.activityPausedLocked(token, false); + } + } Binder.restoreCallingIdentity(origId); } + @Override public final void activityStopped(IBinder token, Bundle icicle, Bitmap thumbnail, CharSequence description) { if (localLOGV) Slog.v( @@ -4541,9 +4572,9 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); synchronized (this) { - r = mMainStack.isInStackLocked(token); + r = ActivityRecord.isInStackLocked(token); if (r != null) { - r.stack.activityStoppedLocked(r, icicle, thumbnail, description); + r.task.stack.activityStoppedLocked(r, icicle, thumbnail, description); } } @@ -4556,9 +4587,15 @@ public final class ActivityManagerService extends ActivityManagerNative Binder.restoreCallingIdentity(origId); } + @Override public final void activityDestroyed(IBinder token) { if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token); - mMainStack.activityDestroyed(token); + synchronized (this) { + ActivityStack stack = ActivityRecord.getStackLocked(token); + if (stack != null) { + stack.activityDestroyedLocked(token); + } + } } public String getCallingPackage(IBinder token) { @@ -4576,16 +4613,17 @@ public final class ActivityManagerService extends ActivityManagerNative } private ActivityRecord getCallingRecordLocked(IBinder token) { - ActivityRecord r = mMainStack.isInStackLocked(token); + ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { return null; } return r.resultTo; } + @Override public ComponentName getActivityClassForToken(IBinder token) { synchronized(this) { - ActivityRecord r = mMainStack.isInStackLocked(token); + ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { return null; } @@ -4595,7 +4633,7 @@ public final class ActivityManagerService extends ActivityManagerNative public String getPackageForToken(IBinder token) { synchronized(this) { - ActivityRecord r = mMainStack.isInStackLocked(token); + ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { return null; } @@ -4673,7 +4711,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - + IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, int userId, IBinder token, String resultWho, int requestCode, Intent[] intents, String[] resolvedTypes, int flags, @@ -4682,7 +4720,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid); ActivityRecord activity = null; if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) { - activity = mMainStack.isInStackLocked(token); + activity = ActivityRecord.isInStackLocked(token); if (activity == null) { return null; } @@ -5677,12 +5715,12 @@ public final class ActivityManagerService extends ActivityManagerNative // TASK MANAGEMENT // ========================================================= - public List getTasks(int maxNum, int flags, + @Override + public List<RunningTaskInfo> getTasks(int maxNum, int flags, IThumbnailReceiver receiver) { - ArrayList list = new ArrayList(); + ArrayList<RunningTaskInfo> list = new ArrayList<RunningTaskInfo>(); - PendingThumbnailsRecord pending = null; - IApplicationThread topThumbnail = null; + PendingThumbnailsRecord pending = new PendingThumbnailsRecord(receiver); ActivityRecord topRecord = null; synchronized(this) { @@ -5708,88 +5746,20 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } - int pos = mMainStack.mHistory.size()-1; - ActivityRecord next = - pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null; - ActivityRecord top = null; - TaskRecord curTask = null; - int numActivities = 0; - int numRunning = 0; - while (pos >= 0 && maxNum > 0) { - final ActivityRecord r = next; - pos--; - next = pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null; - - // Initialize state for next task if needed. - if (top == null || - (top.state == ActivityState.INITIALIZING - && top.task == r.task)) { - top = r; - curTask = r.task; - numActivities = numRunning = 0; - } - - // Add 'r' into the current task. - numActivities++; - if (r.app != null && r.app.thread != null) { - numRunning++; - } - - if (localLOGV) Slog.v( - TAG, r.intent.getComponent().flattenToShortString() - + ": task=" + r.task); - - // If the next one is a different task, generate a new - // TaskInfo entry for what we have. - if (next == null || next.task != curTask) { - ActivityManager.RunningTaskInfo ci - = new ActivityManager.RunningTaskInfo(); - ci.id = curTask.taskId; - ci.baseActivity = r.intent.getComponent(); - ci.topActivity = top.intent.getComponent(); - if (top.thumbHolder != null) { - ci.description = top.thumbHolder.lastDescription; - } - ci.numActivities = numActivities; - ci.numRunning = numRunning; - //System.out.println( - // "#" + maxNum + ": " + " descr=" + ci.description); - if (ci.thumbnail == null && receiver != null) { - if (localLOGV) Slog.v( - TAG, "State=" + top.state + "Idle=" + top.idle - + " app=" + top.app - + " thr=" + (top.app != null ? top.app.thread : null)); - if (top.state == ActivityState.RESUMED - || top.state == ActivityState.PAUSING) { - if (top.idle && top.app != null - && top.app.thread != null) { - topRecord = top; - topThumbnail = top.app.thread; - } else { - top.thumbnailNeeded = true; - } - } - if (pending == null) { - pending = new PendingThumbnailsRecord(receiver); - } - pending.pendingRecords.add(top); - } - list.add(ci); - maxNum--; - top = null; - } - } + // TODO: Improve with MRU list from all ActivityStacks. + topRecord = mFocusedStack.getTasksLocked(maxNum, receiver, pending, list); - if (pending != null) { + if (!pending.pendingRecords.isEmpty()) { mPendingThumbnails.add(pending); } } if (localLOGV) Slog.v(TAG, "We have pending thumbnails: " + pending); - if (topThumbnail != null) { + if (topRecord != null) { if (localLOGV) Slog.v(TAG, "Requesting top thumbnail"); try { + IApplicationThread topThumbnail = topRecord.app.thread; topThumbnail.requestThumbnail(topRecord.appToken); } catch (Exception e) { Slog.w(TAG, "Exception thrown when requesting thumbnail", e); @@ -5883,49 +5853,77 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private TaskRecord taskForIdLocked(int id) { + private TaskRecord recentTaskForIdLocked(int id) { final int N = mRecentTasks.size(); - for (int i=0; i<N; i++) { - TaskRecord tr = mRecentTasks.get(i); - if (tr.taskId == id) { - return tr; + for (int i=0; i<N; i++) { + TaskRecord tr = mRecentTasks.get(i); + if (tr.taskId == id) { + return tr; + } + } + return null; + } + + TaskRecord anyTaskForIdLocked(int id) { + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { + ActivityStack stack = mStacks.get(stackNdx); + TaskRecord task = stack.taskForIdLocked(id); + if (task != null) { + return task; } } return null; } + int getNextTaskId() { + do { + mCurTask++; + if (mCurTask <= 0) { + mCurTask = 1; + } + } while (anyTaskForIdLocked(mCurTask) != null); + return mCurTask; + } + + @Override public ActivityManager.TaskThumbnails getTaskThumbnails(int id) { synchronized (this) { enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, "getTaskThumbnails()"); - TaskRecord tr = taskForIdLocked(id); + TaskRecord tr = recentTaskForIdLocked(id); if (tr != null) { - return mMainStack.getTaskThumbnailsLocked(tr); + return tr.stack.getTaskThumbnailsLocked(tr); } } return null; } + @Override public Bitmap getTaskTopThumbnail(int id) { synchronized (this) { enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, "getTaskTopThumbnail()"); - TaskRecord tr = taskForIdLocked(id); + TaskRecord tr = recentTaskForIdLocked(id); if (tr != null) { - return mMainStack.getTaskTopThumbnailLocked(tr); + return tr.stack.getTaskTopThumbnailLocked(tr); } } return null; } + @Override public boolean removeSubTask(int taskId, int subTaskIndex) { synchronized (this) { enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, "removeSubTask()"); long ident = Binder.clearCallingIdentity(); try { - return mMainStack.removeTaskActivitiesLocked(taskId, subTaskIndex, - true) != null; + TaskRecord tr = recentTaskForIdLocked(taskId); + if (tr != null) { + return tr.stack.removeTaskActivitiesLocked(taskId, subTaskIndex, + true) != null; + } + return false; } finally { Binder.restoreCallingIdentity(ident); } @@ -5979,43 +5977,32 @@ public final class ActivityManagerService extends ActivityManagerNative } } + @Override public boolean removeTask(int taskId, int flags) { synchronized (this) { enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, "removeTask()"); long ident = Binder.clearCallingIdentity(); try { - ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1, - false); - if (r != null) { - mRecentTasks.remove(r.task); - cleanUpRemovedTaskLocked(r.task, flags); - return true; - } else { - TaskRecord tr = null; - int i=0; - while (i < mRecentTasks.size()) { - TaskRecord t = mRecentTasks.get(i); - if (t.taskId == taskId) { - tr = t; - break; - } - i++; + TaskRecord tr = recentTaskForIdLocked(taskId); + if (tr != null) { + ActivityRecord r = tr.stack.removeTaskActivitiesLocked(taskId, -1, false); + if (r != null) { + mRecentTasks.remove(tr); + cleanUpRemovedTaskLocked(tr, flags); + return true; } - if (tr != null) { - if (tr.numActivities <= 0) { - // Caller is just removing a recent task that is - // not actively running. That is easy! - mRecentTasks.remove(i); - cleanUpRemovedTaskLocked(tr, flags); - return true; - } else { - Slog.w(TAG, "removeTask: task " + taskId - + " does not have activities to remove, " - + " but numActivities=" + tr.numActivities - + ": " + tr); - } + if (tr.mActivities.size() == 0) { + // Caller is just removing a recent task that is + // not actively running. That is easy! + mRecentTasks.remove(tr); + cleanUpRemovedTaskLocked(tr, flags); + return true; } + Slog.w(TAG, "removeTask: task " + taskId + + " does not have activities to remove, " + + " but numActivities=" + tr.numActivities + + ": " + tr); } } finally { Binder.restoreCallingIdentity(ident); @@ -6024,46 +6011,10 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } - private final int findAffinityTaskTopLocked(int startIndex, String affinity) { - int j; - TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task; - TaskRecord jt = startTask; - - // First look backwards - for (j=startIndex-1; j>=0; j--) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j); - if (r.task != jt) { - jt = r.task; - if (affinity.equals(jt.affinity)) { - return j; - } - } - } - - // Now look forwards - final int N = mMainStack.mHistory.size(); - jt = startTask; - for (j=startIndex+1; j<N; j++) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j); - if (r.task != jt) { - if (affinity.equals(jt.affinity)) { - return j; - } - jt = r.task; - } - } - - // Might it be at the top? - if (affinity.equals(((ActivityRecord)mMainStack.mHistory.get(N-1)).task.affinity)) { - return N-1; - } - - return -1; - } - /** * TODO: Add mController hook */ + @Override public void moveTaskToFront(int task, int flags, Bundle options) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()"); @@ -6076,31 +6027,23 @@ public final class ActivityManagerService extends ActivityManagerNative } final long origId = Binder.clearCallingIdentity(); try { - TaskRecord tr = taskForIdLocked(task); + TaskRecord tr = recentTaskForIdLocked(task); if (tr != null) { + ActivityStack stack = tr.stack; if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { - mMainStack.mUserLeaving = true; + stack.mUserLeaving = true; } if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) { // Caller wants the home activity moved with it. To accomplish this, // we'll just move the home task to the top first. - mMainStack.moveHomeToFrontLocked(); + stack.moveHomeToFrontLocked(); } - mMainStack.moveTaskToFrontLocked(tr, null, options); + stack.moveTaskToFrontLocked(tr, null, options); return; } - for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { - ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i); - if (hr.task.taskId == task) { - if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { - mMainStack.mUserLeaving = true; - } - if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) { - // Caller wants the home activity moved with it. To accomplish this, - // we'll just move the home task to the top first. - mMainStack.moveHomeToFrontLocked(); - } - mMainStack.moveTaskToFrontLocked(hr.task, null, options); + // Failed to find the task in the recents list. Look in all stacks. + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { + if (mStacks.get(stackNdx).findTaskToMoveToFrontLocked(task, flags, options)) { return; } } @@ -6111,21 +6054,25 @@ public final class ActivityManagerService extends ActivityManagerNative } } + @Override public void moveTaskToBack(int task) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToBack()"); synchronized(this) { - if (mMainStack.mResumedActivity != null - && mMainStack.mResumedActivity.task.taskId == task) { - if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), - Binder.getCallingUid(), "Task to back")) { - return; + TaskRecord tr = recentTaskForIdLocked(task); + if (tr != null) { + ActivityStack stack = tr.stack; + if (stack.mResumedActivity != null && stack.mResumedActivity.task == tr) { + if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), + Binder.getCallingUid(), "Task to back")) { + return; + } } + final long origId = Binder.clearCallingIdentity(); + stack.moveTaskToBackLocked(task, null); + Binder.restoreCallingIdentity(origId); } - final long origId = Binder.clearCallingIdentity(); - mMainStack.moveTaskToBackLocked(task, null); - Binder.restoreCallingIdentity(origId); } } @@ -6138,19 +6085,21 @@ public final class ActivityManagerService extends ActivityManagerNative * of a task; if true it will work for any activity in a task. * @return Returns true if the move completed, false if not. */ + @Override public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) { enforceNotIsolatedCaller("moveActivityTaskToBack"); synchronized(this) { final long origId = Binder.clearCallingIdentity(); - int taskId = getTaskForActivityLocked(token, !nonRoot); + int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot); if (taskId >= 0) { - return mMainStack.moveTaskToBackLocked(taskId, null); + return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId, null); } Binder.restoreCallingIdentity(origId); } return false; } + @Override public void moveTaskBackwards(int task) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskBackwards()"); @@ -6170,27 +6119,54 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.e(TAG, "moveTaskBackwards not yet implemented!"); } - public int getTaskForActivity(IBinder token, boolean onlyRoot) { - synchronized(this) { - return getTaskForActivityLocked(token, onlyRoot); + private ActivityStack getStack(int stackId) { + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + if (stack.getStackId() == stackId) { + return stack; + } } + return null; } - int getTaskForActivityLocked(IBinder token, boolean onlyRoot) { - final int N = mMainStack.mHistory.size(); - TaskRecord lastTask = null; - for (int i=0; i<N; i++) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - if (r.appToken == token) { - if (!onlyRoot || lastTask != r.task) { - return r.task.taskId; + @Override + public int createStack(int relativeStackId, int position, float weight) { + synchronized (this) { + while (true) { + if (++mLastStackId <= HOME_ACTIVITY_STACK) { + mLastStackId = HOME_ACTIVITY_STACK + 1; + } + if (getStack(mLastStackId) == null) { + break; } - return -1; } - lastTask = r.task; + mStacks.add(new ActivityStack(this, mContext, false, mLooper, mLastStackId)); + mWindowManager.createStack(mLastStackId, position, relativeStackId, weight); + return mLastStackId; } + } - return -1; + @Override + public void moveTaskToStack(int taskId, int stackId, boolean toTop) { + final ActivityStack stack = getStack(stackId); + if (stack == null) { + Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId); + return; + } + stack.moveTask(taskId, toTop); + mWindowManager.moveTaskToStack(taskId, stackId, toTop); + } + + @Override + public void resizeStack(int stackId, float weight) { + mWindowManager.resizeStack(stackId, weight); + } + + @Override + public int getTaskForActivity(IBinder token, boolean onlyRoot) { + synchronized(this) { + return ActivityRecord.getTaskForActivityLocked(token, onlyRoot); + } } // ========================================================= @@ -6214,7 +6190,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { if (r == null) { - r = mMainStack.isInStackLocked(token); + r = ActivityRecord.isInStackLocked(token); if (r == null) { return; } @@ -7098,13 +7074,10 @@ public final class ActivityManagerService extends ActivityManagerNative "unhandledBack()"); synchronized(this) { - int count = mMainStack.mHistory.size(); - if (DEBUG_SWITCH) Slog.d( - TAG, "Performing unhandledBack(): stack size = " + count); - if (count > 1) { - final long origId = Binder.clearCallingIdentity(); - mMainStack.finishActivityLocked((ActivityRecord)mMainStack.mHistory.get(count-1), - count-1, Activity.RESULT_CANCELED, null, "unhandled-back", true); + final long origId = Binder.clearCallingIdentity(); + try { + mFocusedStack.unhandledBackLocked(); + } finally { Binder.restoreCallingIdentity(origId); } } @@ -7162,7 +7135,11 @@ public final class ActivityManagerService extends ActivityManagerNative if (!mSleeping) { mSleeping = true; - mMainStack.stopIfSleepingLocked(); + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.stopIfSleepingLocked(); + } // Initialize the wake times of all processes. checkExcessivePowerUsageLocked(false); @@ -7173,33 +7150,38 @@ public final class ActivityManagerService extends ActivityManagerNative } } + @Override public boolean shutdown(int timeout) { if (checkCallingPermission(android.Manifest.permission.SHUTDOWN) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires permission " + android.Manifest.permission.SHUTDOWN); } - + boolean timedout = false; - + synchronized(this) { mShuttingDown = true; updateEventDispatchingLocked(); - if (mMainStack.mResumedActivity != null) { - mMainStack.stopIfSleepingLocked(); - final long endTime = System.currentTimeMillis() + timeout; - while (mMainStack.mResumedActivity != null - || mMainStack.mPausingActivity != null) { - long delay = endTime - System.currentTimeMillis(); - if (delay <= 0) { - Slog.w(TAG, "Activity manager shutdown timed out"); - timedout = true; - break; - } - try { - this.wait(); - } catch (InterruptedException e) { + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + if (stack.mResumedActivity != null) { + stack.stopIfSleepingLocked(); + final long endTime = System.currentTimeMillis() + timeout; + while (stack.mResumedActivity != null + || stack.mPausingActivity != null) { + long delay = endTime - System.currentTimeMillis(); + if (delay <= 0) { + Slog.w(TAG, "Activity manager shutdown timed out"); + timedout = true; + break; + } + try { + this.wait(); + } catch (InterruptedException e) { + } } } } @@ -7208,7 +7190,7 @@ public final class ActivityManagerService extends ActivityManagerNative mAppOpsService.shutdown(); mUsageStatsService.shutdown(); mBatteryStatsService.shutdown(); - + return timedout; } @@ -7221,9 +7203,9 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); synchronized (this) { - r = mMainStack.isInStackLocked(token); + r = ActivityRecord.isInStackLocked(token); if (r != null) { - mMainStack.activitySleptLocked(r); + r.task.stack.activitySleptLocked(r); } } @@ -7234,8 +7216,12 @@ public final class ActivityManagerService extends ActivityManagerNative if (!mWentToSleep && !mLockScreenShown) { if (mSleeping) { mSleeping = false; - mMainStack.awakeFromSleepingLocked(); - mMainStack.resumeTopActivityLocked(null); + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.awakeFromSleepingLocked(); + stack.resumeTopActivityLocked(null); + } } } } @@ -7510,7 +7496,7 @@ public final class ActivityManagerService extends ActivityManagerNative PendingActivityExtras pae; Bundle extras = new Bundle(); synchronized (this) { - ActivityRecord activity = mMainStack.mResumedActivity; + ActivityRecord activity = mFocusedStack.mResumedActivity; if (activity == null) { Slog.w(TAG, "getTopActivityExtras failed: no resumed activity"); return null; @@ -7577,7 +7563,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void setImmersive(IBinder token, boolean immersive) { synchronized(this) { - final ActivityRecord r = mMainStack.isInStackLocked(token); + final ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { throw new IllegalArgumentException(); } @@ -7595,7 +7581,7 @@ public final class ActivityManagerService extends ActivityManagerNative public boolean isImmersive(IBinder token) { synchronized (this) { - ActivityRecord r = mMainStack.isInStackLocked(token); + ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { throw new IllegalArgumentException(); } @@ -7606,7 +7592,7 @@ public final class ActivityManagerService extends ActivityManagerNative public boolean isTopActivityImmersive() { enforceNotIsolatedCaller("startActivity"); synchronized (this) { - ActivityRecord r = mMainStack.topRunningActivityLocked(null); + ActivityRecord r = mFocusedStack.topRunningActivityLocked(null); return (r != null) ? r.immersive : false; } } @@ -8104,7 +8090,7 @@ public final class ActivityManagerService extends ActivityManagerNative } finally { Binder.restoreCallingIdentity(ident); } - mMainStack.resumeTopActivityLocked(null); + mFocusedStack.resumeTopActivityLocked(null); sendUserSwitchBroadcastsLocked(-1, mCurrentUserId); } } @@ -8193,20 +8179,16 @@ public final class ActivityManagerService extends ActivityManagerNative } else { crashTime = null; } + final int numStacks = mStacks.size(); if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) { // This process loses! Slog.w(TAG, "Process " + app.info.processName + " has crashed too many times: killing!"); EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, app.userId, app.info.processName, app.uid); - for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); - if (r.app == app) { - Slog.w(TAG, " Force finishing activity " - + r.intent.getComponent().flattenToShortString()); - r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, - null, "crashed", false); - } + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.handleAppCrashLocked(app); } if (!app.persistent) { // We don't want to start this process again until the user @@ -8227,37 +8209,20 @@ public final class ActivityManagerService extends ActivityManagerNative // annoy the user repeatedly. Unless it is persistent, since those // processes run critical code. removeProcessLocked(app, false, false, "crash"); - mMainStack.resumeTopActivityLocked(null); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.resumeTopActivityLocked(null); + } return false; } - mMainStack.resumeTopActivityLocked(null); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.resumeTopActivityLocked(null); + } } else { - ActivityRecord r = mMainStack.topRunningActivityLocked(null); - if (r != null && r.app == app) { - // If the top running activity is from this crashing - // process, then terminate it to avoid getting in a loop. - Slog.w(TAG, " Force finishing activity " - + r.intent.getComponent().flattenToShortString()); - int index = mMainStack.indexOfActivityLocked(r); - r.stack.finishActivityLocked(r, index, - Activity.RESULT_CANCELED, null, "crashed", false); - // Also terminate any activities below it that aren't yet - // stopped, to avoid a situation where one will get - // re-start our crashing activity once it gets resumed again. - index--; - if (index >= 0) { - r = (ActivityRecord)mMainStack.mHistory.get(index); - if (r.state == ActivityState.RESUMED - || r.state == ActivityState.PAUSING - || r.state == ActivityState.PAUSED) { - if (!r.isHomeActivity || mHomeProcess != r.app) { - Slog.w(TAG, " Force finishing activity " - + r.intent.getComponent().flattenToShortString()); - r.stack.finishActivityLocked(r, index, - Activity.RESULT_CANCELED, null, "crashed", false); - } - } - } + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.finishTopRunningActivityLocked(app); } } @@ -8278,9 +8243,9 @@ public final class ActivityManagerService extends ActivityManagerNative // from blocking the user to manually clear the list. if (app == mHomeProcess && mHomeProcess.activities.size() > 0 && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - Iterator it = mHomeProcess.activities.iterator(); + Iterator<ActivityRecord> it = mHomeProcess.activities.iterator(); while (it.hasNext()) { - ActivityRecord r = (ActivityRecord)it.next(); + ActivityRecord r = it.next(); if (r.isHomeActivity) { Log.i(TAG, "Clearing package preferred activities from " + r.packageName); try { @@ -9276,49 +9241,57 @@ public final class ActivityManagerService extends ActivityManagerNative boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)"); - pw.println(" Main stack:"); - dumpHistoryList(fd, pw, mMainStack.mHistory, " ", "Hist", true, !dumpAll, dumpClient, - dumpPackage); - pw.println(" "); - pw.println(" Running activities (most recent first):"); - dumpHistoryList(fd, pw, mMainStack.mLRUActivities, " ", "Run", false, !dumpAll, false, - dumpPackage); - if (mMainStack.mWaitingVisibleActivities.size() > 0) { - pw.println(" "); - pw.println(" Activities waiting for another to become visible:"); - dumpHistoryList(fd, pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false, - !dumpAll, false, dumpPackage); - } - if (mMainStack.mStoppingActivities.size() > 0) { - pw.println(" "); - pw.println(" Activities waiting to stop:"); - dumpHistoryList(fd, pw, mMainStack.mStoppingActivities, " ", "Stop", false, - !dumpAll, false, dumpPackage); - } - if (mMainStack.mGoingToSleepActivities.size() > 0) { - pw.println(" "); - pw.println(" Activities waiting to sleep:"); - dumpHistoryList(fd, pw, mMainStack.mGoingToSleepActivities, " ", "Sleep", false, - !dumpAll, false, dumpPackage); - } - if (mMainStack.mFinishingActivities.size() > 0) { + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + pw.print(" Stack #"); pw.print(mStacks.indexOf(stack)); pw.println(":"); + stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage); pw.println(" "); - pw.println(" Activities waiting to finish:"); - dumpHistoryList(fd, pw, mMainStack.mFinishingActivities, " ", "Fin", false, - !dumpAll, false, dumpPackage); + pw.println(" Running activities (most recent first):"); + dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, !dumpAll, false, + dumpPackage); + if (stack.mWaitingVisibleActivities.size() > 0) { + pw.println(" "); + pw.println(" Activities waiting for another to become visible:"); + dumpHistoryList(fd, pw, stack.mWaitingVisibleActivities, " ", "Wait", false, + !dumpAll, false, dumpPackage); + } + if (stack.mStoppingActivities.size() > 0) { + pw.println(" "); + pw.println(" Activities waiting to stop:"); + dumpHistoryList(fd, pw, stack.mStoppingActivities, " ", "Stop", false, + !dumpAll, false, dumpPackage); + } + if (stack.mGoingToSleepActivities.size() > 0) { + pw.println(" "); + pw.println(" Activities waiting to sleep:"); + dumpHistoryList(fd, pw, stack.mGoingToSleepActivities, " ", "Sleep", false, + !dumpAll, false, dumpPackage); + } + if (stack.mFinishingActivities.size() > 0) { + pw.println(" "); + pw.println(" Activities waiting to finish:"); + dumpHistoryList(fd, pw, stack.mFinishingActivities, " ", "Fin", false, + !dumpAll, false, dumpPackage); + } } pw.println(" "); - if (mMainStack.mPausingActivity != null) { - pw.println(" mPausingActivity: " + mMainStack.mPausingActivity); - } - pw.println(" mResumedActivity: " + mMainStack.mResumedActivity); pw.println(" mFocusedActivity: " + mFocusedActivity); - if (dumpAll) { - pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity); - pw.println(" mSleepTimeout: " + mMainStack.mSleepTimeout); - pw.println(" mDismissKeyguardOnNextActivity: " - + mMainStack.mDismissKeyguardOnNextActivity); + pw.println(" "); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + pw.print(" Stack #"); pw.println(mStacks.indexOf(stack)); + if (stack.mPausingActivity != null) { + pw.println(" mPausingActivity: " + stack.mPausingActivity); + } + pw.println(" mResumedActivity: " + stack.mResumedActivity); + if (dumpAll) { + pw.println(" mLastPausedActivity: " + stack.mLastPausedActivity); + pw.println(" mSleepTimeout: " + stack.mSleepTimeout); + pw.println(" mDismissKeyguardOnNextActivity: " + + stack.mDismissKeyguardOnNextActivity); + } } if (mRecentTasks.size() > 0) { @@ -9563,7 +9536,7 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(" mConfiguration: " + mConfiguration); if (dumpAll) { - pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange); + pw.println(" mConfigWillChange: " + mFocusedStack.mConfigWillChange); if (mCompatModePackages.getPackages().size() > 0) { boolean printed = false; for (Map.Entry<String, Integer> entry @@ -9623,8 +9596,8 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" mLastPowerCheckUptime="); TimeUtils.formatDuration(mLastPowerCheckUptime, pw); pw.println(""); - pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep); - pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity); + pw.println(" mGoingToSleep=" + mFocusedStack.mGoingToSleep); + pw.println(" mLaunchingActivity=" + mFocusedStack.mLaunchingActivity); pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq); pw.println(" mNumNonHiddenProcs=" + mNumNonHiddenProcs + " mNumHiddenProcs=" + mNumHiddenProcs @@ -9808,32 +9781,10 @@ public final class ActivityManagerService extends ActivityManagerNative */ protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll) { - ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(); - - if ("all".equals(name)) { - synchronized (this) { - for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) { - activities.add(r1); - } - } - } else if ("top".equals(name)) { - synchronized (this) { - final int N = mMainStack.mHistory.size(); - if (N > 0) { - activities.add((ActivityRecord)mMainStack.mHistory.get(N-1)); - } - } - } else { - ItemMatcher matcher = new ItemMatcher(); - matcher.build(name); - - synchronized (this) { - for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) { - if (matcher.match(r1, r1.intent.getComponent())) { - activities.add(r1); - } - } - } + ArrayList<ActivityRecord> activities; + + synchronized (this) { + activities = mFocusedStack.getDumpActivitiesLocked(name); } if (activities.size() <= 0) { @@ -10085,7 +10036,7 @@ public final class ActivityManagerService extends ActivityManagerNative return needSep; } - private static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List list, + static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List list, String prefix, String label, boolean complete, boolean brief, boolean client, String dumpPackage) { TaskRecord lastTask = null; @@ -12563,21 +12514,25 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - - if (changes != 0 && starting == null) { - // If the configuration changed, and the caller is not already - // in the process of starting an activity, then find the top - // activity to check if its configuration needs to change. - starting = mMainStack.topRunningActivityLocked(null); - } - - if (starting != null) { - kept = mMainStack.ensureActivityConfigurationLocked(starting, changes); - // And we need to make sure at this point that all other activities - // are made visible with the correct configuration. - mMainStack.ensureActivitiesVisibleLocked(starting, changes); + + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + if (changes != 0 && starting == null) { + // If the configuration changed, and the caller is not already + // in the process of starting an activity, then find the top + // activity to check if its configuration needs to change. + starting = stack.topRunningActivityLocked(null); + } + + if (starting != null) { + kept = stack.ensureActivityConfigurationLocked(starting, changes); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + stack.ensureActivitiesVisibleLocked(starting, changes); + } } - + if (values != null && mWindowManager != null) { mWindowManager.setNewConfiguration(mConfiguration); } @@ -12623,95 +12578,13 @@ public final class ActivityManagerService extends ActivityManagerNative public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode, Intent resultData) { - ComponentName dest = destIntent.getComponent(); synchronized (this) { - ActivityRecord srec = ActivityRecord.forToken(token); - if (srec == null) { - return false; - } - ArrayList<ActivityRecord> history = srec.stack.mHistory; - final int start = history.indexOf(srec); - if (start < 0) { - // Current activity is not in history stack; do nothing. - return false; - } - int finishTo = start - 1; - ActivityRecord parent = null; - boolean foundParentInTask = false; - if (dest != null) { - TaskRecord tr = srec.task; - for (int i = start - 1; i >= 0; i--) { - ActivityRecord r = history.get(i); - if (tr != r.task) { - // Couldn't find parent in the same task; stop at the one above this. - // (Root of current task; in-app "home" behavior) - // Always at least finish the current activity. - finishTo = Math.min(start - 1, i + 1); - parent = history.get(finishTo); - break; - } else if (r.info.packageName.equals(dest.getPackageName()) && - r.info.name.equals(dest.getClassName())) { - finishTo = i; - parent = r; - foundParentInTask = true; - break; - } - } - } - - if (mController != null) { - ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0); - if (next != null) { - // ask watcher if this is allowed - boolean resumeOK = true; - try { - resumeOK = mController.activityResuming(next.packageName); - } catch (RemoteException e) { - mController = null; - } - - if (!resumeOK) { - return false; - } - } + final ActivityStack stack = ActivityRecord.getStackLocked(token); + if (stack != null) { + return stack.navigateUpToLocked(token, destIntent, resultCode, resultData); } - final long origId = Binder.clearCallingIdentity(); - for (int i = start; i > finishTo; i--) { - ActivityRecord r = history.get(i); - mMainStack.requestFinishActivityLocked(r.appToken, resultCode, resultData, - "navigate-up", true); - // Only return the supplied result for the first activity finished - resultCode = Activity.RESULT_CANCELED; - resultData = null; - } - - if (parent != null && foundParentInTask) { - final int parentLaunchMode = parent.info.launchMode; - final int destIntentFlags = destIntent.getFlags(); - if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE || - parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK || - parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP || - (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { - parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent); - } else { - try { - ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo( - destIntent.getComponent(), 0, srec.userId); - int res = mMainStack.startActivityLocked(srec.app.thread, destIntent, - null, aInfo, parent.appToken, null, - 0, -1, parent.launchedFromUid, parent.launchedFromPackage, - 0, null, true, null); - foundParentInTask = res == ActivityManager.START_SUCCESS; - } catch (RemoteException e) { - foundParentInTask = false; - } - mMainStack.requestFinishActivityLocked(parent.appToken, resultCode, - resultData, "navigate-up", true); - } - } - Binder.restoreCallingIdentity(origId); - return foundParentInTask; + return false; } } @@ -13452,8 +13325,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } return !processingBroadcasts - && (mSleeping || (mMainStack.mResumedActivity != null && - mMainStack.mResumedActivity.idle)); + && (mSleeping || (mFocusedStack.mResumedActivity != null && + mFocusedStack.mResumedActivity.idle)); } /** @@ -13737,11 +13610,11 @@ public final class ActivityManagerService extends ActivityManagerNative } private final ActivityRecord resumedAppLocked() { - ActivityRecord resumedActivity = mMainStack.mResumedActivity; + ActivityRecord resumedActivity = mFocusedStack.mResumedActivity; if (resumedActivity == null || resumedActivity.app == null) { - resumedActivity = mMainStack.mPausingActivity; + resumedActivity = mFocusedStack.mPausingActivity; if (resumedActivity == null || resumedActivity.app == null) { - resumedActivity = mMainStack.topRunningActivityLocked(null); + resumedActivity = mFocusedStack.topRunningActivityLocked(null); } } return resumedActivity; @@ -13987,7 +13860,11 @@ public final class ActivityManagerService extends ActivityManagerNative // be in a consistent state at this point. // For these apps we will also finish their activities // to help them free memory. - mMainStack.scheduleDestroyActivities(app, false, "trim"); + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.scheduleDestroyActivities(app, false, "trim"); + } } } } @@ -14061,7 +13938,11 @@ public final class ActivityManagerService extends ActivityManagerNative if (mAlwaysFinishActivities) { // Need to do this on its own message because the stack may not // be in a consistent state at this point. - mMainStack.scheduleDestroyActivities(null, false, "always-finish"); + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.scheduleDestroyActivities(null, false, "always-finish"); + } } } @@ -14361,7 +14242,6 @@ public final class ActivityManagerService extends ActivityManagerNative } mCurrentUserId = userId; - mCurrentUserArray = new int[] { userId }; final Integer userIdInt = Integer.valueOf(userId); mUserLru.remove(userIdInt); mUserLru.add(userIdInt); @@ -14427,7 +14307,12 @@ public final class ActivityManagerService extends ActivityManagerNative } } - boolean haveActivities = mMainStack.switchUserLocked(userId, uss); + boolean haveActivities = false; + final int numStacks = mStacks.size(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + haveActivities |= stack.switchUserLocked(userId, uss); + } if (!haveActivities) { startHomeActivityLocked(userId); } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 054d213..2075af7 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -20,8 +20,8 @@ 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.app.ResultInfo; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -55,7 +55,6 @@ import java.util.HashSet; */ 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. @@ -95,9 +94,9 @@ final class ActivityRecord { 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 + ArrayList<ResultInfo> 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 + ArrayList<Intent> 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. @@ -182,7 +181,7 @@ final class ActivityRecord { 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); + Intent intent = newIntents.get(i); pw.print(prefix); pw.print(" - "); if (intent == null) { pw.println("null"); @@ -326,13 +325,12 @@ final class ActivityRecord { } } - ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller, + 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) { service = _service; - stack = _stack; appToken = new Token(this); info = aInfo; launchedFromUid = _launchedFromUid; @@ -504,6 +502,7 @@ final class ActivityRecord { inHistory = false; if (task != null && !finishing) { task.numActivities--; + task = null; } clearOptionsLocked(); } @@ -538,7 +537,7 @@ final class ActivityRecord { ActivityResult r = new ActivityResult(from, resultWho, requestCode, resultCode, resultData); if (results == null) { - results = new ArrayList(); + results = new ArrayList<ResultInfo>(); } results.add(r); } @@ -563,7 +562,7 @@ final class ActivityRecord { void addNewIntentLocked(Intent intent) { if (newIntents == null) { - newIntents = new ArrayList(); + newIntents = new ArrayList<Intent>(); } newIntents.add(intent); } @@ -583,7 +582,7 @@ final class ActivityRecord { // 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)) + && task.stack.topRunningActivityLocked(null) == this)) && app != null && app.thread != null) { try { ArrayList<Intent> ar = new ArrayList<Intent>(); @@ -727,6 +726,7 @@ final class ActivityRecord { boolean continueLaunchTickingLocked() { if (launchTickTime != 0) { + final ActivityStack stack = task.stack; Message msg = stack.mHandler.obtainMessage(ActivityStack.LAUNCH_TICK_MSG); msg.obj = this; stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG); @@ -738,7 +738,7 @@ final class ActivityRecord { void finishLaunchTickingLocked() { launchTickTime = 0; - stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG); + task.stack.mHandler.removeMessages(ActivityStack.LAUNCH_TICK_MSG); } // IApplicationToken @@ -767,6 +767,7 @@ final class ActivityRecord { public void windowsDrawn() { synchronized(service) { if (launchTime != 0) { + final ActivityStack stack = task.stack; final long curTime = SystemClock.uptimeMillis(); final long thisTime = curTime - launchTime; final long totalTime = stack.mInitialStartTime != 0 @@ -802,6 +803,7 @@ final class ActivityRecord { public void windowsVisible() { synchronized(service) { + final ActivityStack stack = task.stack; stack.reportActivityVisibleLocked(this); if (ActivityManagerService.DEBUG_SWITCH) Log.v( ActivityManagerService.TAG, "windowsVisible(): " + this); @@ -822,8 +824,7 @@ final class ActivityRecord { final int N = stack.mWaitingVisibleActivities.size(); if (N > 0) { for (int i=0; i<N; i++) { - ActivityRecord r = (ActivityRecord) - stack.mWaitingVisibleActivities.get(i); + ActivityRecord r = stack.mWaitingVisibleActivities.get(i); r.waitingVisible = false; if (ActivityManagerService.DEBUG_SWITCH) Log.v( ActivityManagerService.TAG, @@ -851,6 +852,7 @@ final class ActivityRecord { // for another app to start, then we have paused dispatching // for this activity. ActivityRecord r = this; + final ActivityStack stack = task.stack; if (r.waitingVisible) { // Hmmm, who might we be waiting for? r = stack.mResumedActivity; @@ -900,6 +902,7 @@ final class ActivityRecord { if (app != null && app.thread != null) { try { app.thread.scheduleSleeping(appToken, _sleeping); + final ActivityStack stack = task.stack; if (sleeping && !stack.mGoingToSleepActivities.contains(this)) { stack.mGoingToSleepActivities.add(this); } @@ -910,10 +913,40 @@ final class ActivityRecord { } } } - + + static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) { + final ActivityRecord r = ActivityRecord.forToken(token); + if (r == null) { + return -1; + } + final TaskRecord task = r.task; + switch (task.mActivities.indexOf(r)) { + case -1: return -1; + case 0: return task.taskId; + default: return onlyRoot ? -1 : task.taskId; + } + } + + static ActivityRecord isInStackLocked(IBinder token) { + final ActivityRecord r = ActivityRecord.forToken(token); + if (r != null) { + return r.task.stack.isInStackLocked(token); + } + return null; + } + + static final ActivityStack getStackLocked(IBinder token) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + return r.task.stack; + } + return null; + } + + @Override public String toString() { if (stringName != null) { - return stringName; + return stringName + " t" + task.taskId + (finishing ? " f}" : "}"); } StringBuilder sb = new StringBuilder(128); sb.append("ActivityRecord{"); @@ -922,7 +955,7 @@ final class ActivityRecord { sb.append(userId); sb.append(' '); sb.append(intent.getComponent().flattenToShortString()); - sb.append('}'); - return stringName = sb.toString(); + stringName = sb.toString(); + return toString(); } } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 526b24f..18d9e59 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -21,18 +21,24 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.util.Objects; +import com.android.server.am.ActivityManagerService.ItemMatcher; import com.android.server.am.ActivityManagerService.PendingActivityLaunch; import com.android.server.wm.AppTransition; +import com.android.server.wm.TaskGroup; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.AppGlobals; +import android.app.IActivityController; import android.app.IActivityManager; +import android.app.IThumbnailReceiver; import android.app.IThumbnailRetriever; import android.app.IApplicationThread; import android.app.PendingIntent; import android.app.ResultInfo; +import android.app.ActivityManager.RunningTaskInfo; import android.app.IActivityManager.WaitResult; import android.content.ComponentName; import android.content.Context; @@ -63,7 +69,9 @@ import android.util.Log; import android.util.Slog; import android.view.Display; +import java.io.FileDescriptor; import java.io.IOException; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; @@ -84,14 +92,14 @@ final class ActivityStack { static final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION; static final boolean DEBUG_TASKS = ActivityManagerService.DEBUG_TASKS; static final boolean DEBUG_CLEANUP = ActivityManagerService.DEBUG_CLEANUP; - + static final boolean DEBUG_STATES = false; static final boolean DEBUG_ADD_REMOVE = false; static final boolean DEBUG_SAVED_STATE = false; static final boolean DEBUG_APP = false; static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS; - + // How long we wait until giving up on the last activity telling us it // is idle. static final int IDLE_TIMEOUT = 10*1000; @@ -118,19 +126,19 @@ final class ActivityStack { // How long we wait until giving up on an activity telling us it has // finished destroying itself. static final int DESTROY_TIMEOUT = 10*1000; - + // How long until we reset a task when the user returns to it. Currently // disabled. static final long ACTIVITY_INACTIVE_RESET_TIME = 0; - + // 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; - + enum ActivityState { INITIALIZING, RESUMED, @@ -145,19 +153,19 @@ final class ActivityStack { final ActivityManagerService mService; final boolean mMainStack; - + final Context mContext; - + /** * The back history of all previous (and possibly still - * running) activities. It contains HistoryRecord objects. + * running) activities. It contains #TaskRecord objects. */ - final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>(); + private ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>(); /** * Used for validating app tokens with window manager. */ - final ArrayList<IBinder> mValidateAppTokens = new ArrayList<IBinder>(); + final ArrayList<TaskGroup> mValidateAppTokens = new ArrayList<TaskGroup>(); /** * List of running activities, sorted by recent usage. @@ -202,13 +210,13 @@ final class ActivityStack { */ final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<ActivityRecord>(); - + /** * List of people waiting to find out about the next launched activity. */ final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched = new ArrayList<IActivityManager.WaitResult>(); - + /** * List of people waiting to find out about the next visible activity. */ @@ -250,14 +258,14 @@ final class ActivityStack { * Current activity that is resumed, or null if there is none. */ 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. @@ -269,9 +277,9 @@ final class ActivityStack { * newly launched activity is being brought in front of us. */ boolean mUserLeaving = false; - + long mInitialStartTime = 0; - + /** * Set when we have taken too long waiting to go to sleep. */ @@ -289,11 +297,19 @@ final class ActivityStack { private ActivityRecord mLastScreenshotActivity = null; private Bitmap mLastScreenshotBitmap = null; + /** + * List of ActivityRecord objects that have been finished and must + * still report back to a pending thumbnail receiver. + */ + private final ArrayList<ActivityRecord> mCancelledThumbnails = new ArrayList<ActivityRecord>(); + int mThumbnailWidth = -1; int mThumbnailHeight = -1; private int mCurrentUser; + final int mStackId; + static final int SLEEP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG; static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1; static final int IDLE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2; @@ -348,9 +364,9 @@ final class ActivityStack { mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r); } - } - activityPaused(r != null ? r.appToken : null, true); + activityPausedLocked(r != null ? r.appToken : null, true); + } } break; case IDLE_TIMEOUT_MSG: { if (mService.mDidDexOpt) { @@ -380,7 +396,9 @@ final class ActivityStack { // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. Slog.w(TAG, "Activity destroy timeout for " + r); - activityDestroyed(r != null ? r.appToken : null); + synchronized (mService) { + activityDestroyedLocked(r != null ? r.appToken : null); + } } break; case IDLE_NOW_MSG: { ActivityRecord r = (ActivityRecord)msg.obj; @@ -426,7 +444,16 @@ final class ActivityStack { } } - ActivityStack(ActivityManagerService service, Context context, boolean mainStack, Looper looper) { + private int numActivities() { + int count = 0; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + count += mTaskHistory.get(taskNdx).mActivities.size(); + } + return count; + } + + ActivityStack(ActivityManagerService service, Context context, boolean mainStack, Looper looper, + int stackId) { mHandler = new ActivityStackHandler(looper); mService = service; mContext = context; @@ -436,6 +463,7 @@ final class ActivityStack { mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep"); mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch"); mLaunchingActivity.setReferenceCounted(false); + mStackId = stackId; } private boolean okToShow(ActivityRecord r) { @@ -444,25 +472,29 @@ final class ActivityStack { } final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { - int i = mHistory.size()-1; - while (i >= 0) { - ActivityRecord r = mHistory.get(i); - if (!r.finishing && r != notTop && okToShow(r)) { - return r; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final TaskRecord task = mTaskHistory.get(taskNdx); + final ArrayList<ActivityRecord> activities = task.mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord r = activities.get(activityNdx); + if (!r.finishing && r != notTop && okToShow(r)) { + return r; + } } - i--; } return null; } final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) { - int i = mHistory.size()-1; - while (i >= 0) { - ActivityRecord r = mHistory.get(i); - if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) { - return r; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final TaskRecord task = mTaskHistory.get(taskNdx); + final ArrayList<ActivityRecord> activities = task.mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord r = activities.get(activityNdx); + if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) { + return r; + } } - i--; } return null; } @@ -470,38 +502,49 @@ final class ActivityStack { /** * This is a simplified version of topRunningActivityLocked that provides a number of * optional skip-over modes. It is intended for use with the ActivityController hook only. - * + * * @param token If non-null, any history records matching this token will be skipped. * @param taskId If non-zero, we'll attempt to skip over records with the same task ID. - * + * * @return Returns the HistoryRecord of the next activity on the stack. */ final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) { - int i = mHistory.size()-1; - while (i >= 0) { - ActivityRecord r = mHistory.get(i); - // Note: the taskId check depends on real taskId fields being non-zero - if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId) - && okToShow(r)) { - return r; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + TaskRecord task = mTaskHistory.get(taskNdx); + if (task.taskId == taskId) { + continue; + } + ArrayList<ActivityRecord> activities = task.mActivities; + for (int i = activities.size() - 1; i >= 0; --i) { + final ActivityRecord r = activities.get(i); + // Note: the taskId check depends on real taskId fields being non-zero + if (!r.finishing && (token != r.appToken) && okToShow(r)) { + return r; + } } - i--; } return null; } - final int indexOfTokenLocked(IBinder token) { - return mHistory.indexOf(ActivityRecord.forToken(token)); - } - - final int indexOfActivityLocked(ActivityRecord r) { - return mHistory.indexOf(r); + TaskRecord taskForIdLocked(int id) { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final TaskRecord task = mTaskHistory.get(taskNdx); + if (task.taskId == id) { + return task; + } + } + return null; } - final ActivityRecord isInStackLocked(IBinder token) { - ActivityRecord r = ActivityRecord.forToken(token); - if (mHistory.contains(r)) { - return r; + ActivityRecord isInStackLocked(IBinder token) { + final ActivityRecord r = ActivityRecord.forToken(token); + if (r != null) { + final TaskRecord task = r.task; + if (task.mActivities.contains(r) && mTaskHistory.contains(task)) { + if (task.stack != this) Slog.w(TAG, + "Illegal state! task does not point to stack it is in."); + return r; + } } return null; } @@ -521,37 +564,35 @@ final class ActivityStack { if (info.targetActivity != null) { cls = new ComponentName(info.packageName, info.targetActivity); } + final int userId = UserHandle.getUserId(info.applicationInfo.uid); - TaskRecord cp = null; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final TaskRecord task = mTaskHistory.get(taskNdx); + final ActivityRecord r = task.getTopActivity(); + if (r == null || r.finishing || r.userId != userId || + r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { + continue; + } - final int userId = UserHandle.getUserId(info.applicationInfo.uid); - final int N = mHistory.size(); - for (int i=(N-1); i>=0; i--) { - ActivityRecord r = mHistory.get(i); - if (!r.finishing && r.task != cp && r.userId == userId - && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) { - cp = r.task; - //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString() - // + "/aff=" + r.task.affinity + " to new cls=" - // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity); - if (r.task.affinity != null) { - if (r.task.affinity.equals(info.taskAffinity)) { - //Slog.i(TAG, "Found matching affinity!"); - return r; - } - } else if (r.task.intent != null - && r.task.intent.getComponent().equals(cls)) { - //Slog.i(TAG, "Found matching class!"); - //dump(); - //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); - return r; - } else if (r.task.affinityIntent != null - && r.task.affinityIntent.getComponent().equals(cls)) { - //Slog.i(TAG, "Found matching class!"); - //dump(); - //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString() + // + "/aff=" + r.task.affinity + " to new cls=" + // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity); + if (task.affinity != null) { + if (task.affinity.equals(info.taskAffinity)) { + //Slog.i(TAG, "Found matching affinity!"); return r; } + } else if (task.intent != null && task.intent.getComponent().equals(cls)) { + //Slog.i(TAG, "Found matching class!"); + //dump(); + //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + return r; + } else if (task.affinityIntent != null + && task.affinityIntent.getComponent().equals(cls)) { + //Slog.i(TAG, "Found matching class!"); + //dump(); + //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + return r; } } @@ -570,11 +611,11 @@ final class ActivityStack { } final int userId = UserHandle.getUserId(info.applicationInfo.uid); - final int N = mHistory.size(); - for (int i=(N-1); i>=0; i--) { - ActivityRecord r = mHistory.get(i); - if (!r.finishing) { - if (r.intent.getComponent().equals(cls) && r.userId == userId) { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord r = activities.get(activityNdx); + if (!r.finishing && r.intent.getComponent().equals(cls) && r.userId == userId) { //Slog.i(TAG, "Found matching class!"); //dump(); //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); @@ -598,33 +639,31 @@ final class ActivityStack { * @return whether there are any activities for the specified user. */ final boolean switchUserLocked(int userId, UserStartedState uss) { - mCurrentUser = userId; + if (VALIDATE_TOKENS) { + validateAppTokensLocked(); + } mStartingUsers.add(uss); + if (mCurrentUser == userId) { + return true; + } + mCurrentUser = userId; - // Only one activity? Nothing to do... - if (mHistory.size() < 2) - return false; - + // Move userId's tasks to the top. boolean haveActivities = false; - // Check if the top activity is from the new user. - ActivityRecord top = mHistory.get(mHistory.size() - 1); - if (top.userId == userId) return true; - // Otherwise, move the user's activities to the top. - int N = mHistory.size(); - int i = 0; - while (i < N) { - ActivityRecord r = mHistory.get(i); - if (r.userId == userId) { - ActivityRecord moveToTop = mHistory.remove(i); - mHistory.add(moveToTop); - // No need to check the top one now - N--; + TaskRecord task = null; + int index = mTaskHistory.size(); + for (int i = 0; i < index; ++i) { + task = mTaskHistory.get(i); + if (task.userId == userId) { haveActivities = true; - } else { - i++; + mTaskHistory.remove(i); + mTaskHistory.add(task); + --index; } } - // Transition from the old top to the new top + + // task is now the original topmost TaskRecord. Transition from the old top to the new top. + ActivityRecord top = task != null ? task.getTopActivity() : null; resumeTopActivityLocked(top); return haveActivities; } @@ -709,7 +748,13 @@ final class ActivityStack { try { profileFd = profileFd.dup(); } catch (IOException e) { - profileFd = null; + if (profileFd != null) { + try { + profileFd.close(); + } catch (IOException o) { + } + profileFd = null; + } } } app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, @@ -718,7 +763,7 @@ final class ActivityStack { r.compat, r.icicle, results, newIntents, !andResume, mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop); - + if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { // This may be a heavy-weight process! Note that the package // manager will ensure that only activity can run in the main @@ -738,7 +783,7 @@ final class ActivityStack { mService.mHandler.sendMessage(msg); } } - + } catch (RemoteException e) { if (r.launchFailed) { // This is the second time we failed -- finish activity @@ -773,9 +818,7 @@ final class ActivityStack { r.stopped = false; mResumedActivity = r; r.task.touchActiveTime(); - if (mMainStack) { - mService.addRecentTaskLocked(r.task); - } + mService.addRecentTaskLocked(r.task); completeResumeLocked(r); checkReadyForSleepLocked(); if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle); @@ -797,7 +840,7 @@ final class ActivityStack { if (mMainStack) { mService.startSetupActivityLocked(); } - + return true; } @@ -815,7 +858,7 @@ final class ActivityStack { } else if (mInitialStartTime == 0) { mInitialStartTime = SystemClock.uptimeMillis(); } - + if (app != null && app.thread != null) { try { app.addPackage(r.info.packageName); @@ -833,7 +876,7 @@ final class ActivityStack { mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false); } - + void stopIfSleepingLocked() { if (mService.isSleeping()) { if (!mGoingToSleep.isHeld()) { @@ -857,9 +900,11 @@ final class ActivityStack { mGoingToSleep.release(); } // Ensure activities are no longer sleeping. - for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = mHistory.get(i); - r.setSleeping(false); + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + activities.get(activityNdx).setSleeping(false); + } } mGoingToSleepActivities.clear(); } @@ -901,10 +946,13 @@ final class ActivityStack { // Make sure any stopped but visible activities are now sleeping. // This ensures that the activity's onStop() is called. - for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = mHistory.get(i); - if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) { - r.setSleeping(true); + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) { + r.setSleeping(true); + } } } @@ -930,7 +978,7 @@ final class ActivityStack { if (who.noDisplay) { return null; } - + Resources res = mService.mContext.getResources(); int w = mThumbnailWidth; int h = mThumbnailHeight; @@ -979,7 +1027,7 @@ final class ActivityStack { prev.updateThumbnail(screenshotActivities(prev), null); mService.updateCpuStats(); - + if (prev.app != null && prev.app.thread != null) { if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev); try { @@ -1041,42 +1089,30 @@ final class ActivityStack { } } - final void activityResumed(IBinder token) { - ActivityRecord r = null; - - synchronized (mService) { - int index = indexOfTokenLocked(token); - if (index >= 0) { - r = mHistory.get(index); - if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r); - r.icicle = null; - r.haveState = false; - } - } + final void activityResumedLocked(IBinder token) { + final ActivityRecord r = ActivityRecord.forToken(token); + if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r); + r.icicle = null; + r.haveState = false; } - final void activityPaused(IBinder token, boolean timeout) { + final void activityPausedLocked(IBinder token, boolean timeout) { if (DEBUG_PAUSE) Slog.v( TAG, "Activity paused: token=" + token + ", timeout=" + timeout); - ActivityRecord r = null; - - synchronized (mService) { - int index = indexOfTokenLocked(token); - if (index >= 0) { - r = mHistory.get(index); - mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); - if (mPausingActivity == r) { - if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r - + (timeout ? " (due to timeout)" : " (pause complete)")); - r.state = ActivityState.PAUSED; - completePauseLocked(); - } else { - EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, - r.userId, System.identityHashCode(r), r.shortComponentName, - mPausingActivity != null - ? mPausingActivity.shortComponentName : "(none)"); - } + final ActivityRecord r = isInStackLocked(token); + if (r != null) { + mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); + if (mPausingActivity == r) { + if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r + + (timeout ? " (due to timeout)" : " (pause complete)")); + r.state = ActivityState.PAUSED; + completePauseLocked(); + } else { + EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, + r.userId, System.identityHashCode(r), r.shortComponentName, + mPausingActivity != null + ? mPausingActivity.shortComponentName : "(none)"); } } } @@ -1132,7 +1168,7 @@ final class ActivityStack { private final void completePauseLocked() { ActivityRecord prev = mPausingActivity; if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev); - + if (prev != null) { if (prev.finishing) { if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev); @@ -1186,7 +1222,7 @@ final class ActivityStack { resumeTopActivityLocked(null); } } - + if (prev != null) { prev.resumeKeyDispatchingLocked(); } @@ -1274,106 +1310,95 @@ final class ActivityStack { // If the top activity is not fullscreen, then we need to // make sure any activities under it are now visible. - final int count = mHistory.size(); - int i = count-1; - while (mHistory.get(i) != top) { - i--; - } - ActivityRecord r; + boolean aboveTop = true; boolean behindFullscreen = false; - for (; i>=0; i--) { - r = mHistory.get(i); - if (DEBUG_VISBILITY) Slog.v( - TAG, "Make visible? " + r + " finishing=" + r.finishing - + " state=" + r.state); - if (r.finishing) { - continue; - } - - final boolean doThisProcess = onlyThisProcess == null - || onlyThisProcess.equals(r.processName); - - // First: if this is not the current activity being started, make - // sure it matches the current configuration. - if (r != starting && doThisProcess) { - ensureActivityConfigurationLocked(r, 0); - } - - if (r.app == null || r.app.thread == null) { - if (onlyThisProcess == null - || onlyThisProcess.equals(r.processName)) { - // This activity needs to be visible, but isn't even - // running... get it started, but don't resume it - // at this point. + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.finishing) { + continue; + } + if (aboveTop && r != top) { + continue; + } + aboveTop = false; + if (!behindFullscreen) { if (DEBUG_VISBILITY) Slog.v( - TAG, "Start and freeze screen for " + r); - if (r != starting) { - r.startFreezingScreenLocked(r.app, configChanges); - } - if (!r.visible) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Starting and making visible: " + r); - mService.mWindowManager.setAppVisibility(r.appToken, true); - } - if (r != starting) { - startSpecificActivityLocked(r, false, false); + TAG, "Make visible? " + r + " finishing=" + r.finishing + + " state=" + r.state); + + final boolean doThisProcess = onlyThisProcess == null + || onlyThisProcess.equals(r.processName); + + // First: if this is not the current activity being started, make + // sure it matches the current configuration. + if (r != starting && doThisProcess) { + ensureActivityConfigurationLocked(r, 0); } - } - } else if (r.visible) { - // If this activity is already visible, then there is nothing - // else to do here. - if (DEBUG_VISBILITY) Slog.v( - TAG, "Skipping: already visible at " + r); - r.stopFreezingScreenLocked(false); - - } else if (onlyThisProcess == null) { - // This activity is not currently visible, but is running. - // Tell it to become visible. - r.visible = true; - if (r.state != ActivityState.RESUMED && r != starting) { - // If this activity is paused, tell it - // to now show its window. - if (DEBUG_VISBILITY) Slog.v( - TAG, "Making visible and scheduling visibility: " + r); - try { - mService.mWindowManager.setAppVisibility(r.appToken, true); - r.sleeping = false; - r.app.pendingUiClean = true; - r.app.thread.scheduleWindowVisibility(r.appToken, true); + if (r.app == null || r.app.thread == null) { + if (onlyThisProcess == null + || onlyThisProcess.equals(r.processName)) { + // This activity needs to be visible, but isn't even + // running... get it started, but don't resume it + // at this point. + if (DEBUG_VISBILITY) Slog.v( + TAG, "Start and freeze screen for " + r); + if (r != starting) { + r.startFreezingScreenLocked(r.app, configChanges); + } + if (!r.visible) { + if (DEBUG_VISBILITY) Slog.v( + TAG, "Starting and making visible: " + r); + mService.mWindowManager.setAppVisibility(r.appToken, true); + } + if (r != starting) { + startSpecificActivityLocked(r, false, false); + } + } + + } else if (r.visible) { + // If this activity is already visible, then there is nothing + // else to do here. + if (DEBUG_VISBILITY) Slog.v( + TAG, "Skipping: already visible at " + r); r.stopFreezingScreenLocked(false); - } catch (Exception e) { - // Just skip on any failure; we'll make it - // visible when it next restarts. - Slog.w(TAG, "Exception thrown making visibile: " - + r.intent.getComponent(), e); - } - } - } - // Aggregate current change flags. - configChanges |= r.configChangeFlags; + } else if (onlyThisProcess == null) { + // This activity is not currently visible, but is running. + // Tell it to become visible. + r.visible = true; + if (r.state != ActivityState.RESUMED && r != starting) { + // If this activity is paused, tell it + // to now show its window. + if (DEBUG_VISBILITY) Slog.v( + TAG, "Making visible and scheduling visibility: " + r); + try { + mService.mWindowManager.setAppVisibility(r.appToken, true); + r.sleeping = false; + r.app.pendingUiClean = true; + r.app.thread.scheduleWindowVisibility(r.appToken, true); + r.stopFreezingScreenLocked(false); + } catch (Exception e) { + // Just skip on any failure; we'll make it + // visible when it next restarts. + Slog.w(TAG, "Exception thrown making visibile: " + + r.intent.getComponent(), e); + } + } + } - if (r.fullscreen) { - // At this point, nothing else needs to be shown - if (DEBUG_VISBILITY) Slog.v( - TAG, "Stopping: fullscreen at " + r); - behindFullscreen = true; - i--; - break; - } - } + // Aggregate current change flags. + configChanges |= r.configChangeFlags; - // Now for any activities that aren't visible to the user, make - // sure they no longer are keeping the screen frozen. - while (i >= 0) { - r = mHistory.get(i); - if (DEBUG_VISBILITY) Slog.v( - TAG, "Make invisible? " + r + " finishing=" + r.finishing - + " state=" + r.state - + " behindFullscreen=" + behindFullscreen); - if (!r.finishing) { - if (behindFullscreen) { + if (r.fullscreen) { + // At this point, nothing else needs to be shown + if (DEBUG_VISBILITY) Slog.v( + TAG, "Stopping: fullscreen at " + r); + behindFullscreen = true; + } + } else { if (r.visible) { if (DEBUG_VISBILITY) Slog.v( TAG, "Making invisible: " + r); @@ -1397,13 +1422,8 @@ final class ActivityStack { if (DEBUG_VISBILITY) Slog.v( TAG, "Already invisible: " + r); } - } else if (r.fullscreen) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Now behindFullscreen: " + r); - behindFullscreen = true; } } - i--; } } @@ -1417,7 +1437,7 @@ final class ActivityStack { ensureActivitiesVisibleLocked(r, starting, null, configChanges); } } - + /** * Ensure that the top activity in the stack is resumed. * @@ -1450,7 +1470,7 @@ final class ActivityStack { } next.delayedResume = false; - + // If the top activity is the resumed one, nothing to do. if (mResumedActivity == next && next.state == ActivityState.RESUMED) { // Make sure we have executed any pending transitions, since there @@ -1532,7 +1552,7 @@ final class ActivityStack { mLastStartedActivity = next; } } - + // We need to start pausing the current activity so the top one // can be resumed... if (mResumedActivity != null) { @@ -1641,7 +1661,7 @@ final class ActivityStack { mService.mWindowManager.setAppWillBeHidden(prev.appToken); mService.mWindowManager.setAppVisibility(prev.appToken, false); } - } else if (mHistory.size() > 1) { + } else if (numActivities() > 1) { if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: no previous"); if (mNoAnimActivities.contains(next)) { @@ -1672,14 +1692,12 @@ final class ActivityStack { ActivityState lastState = next.state; mService.updateCpuStats(); - + if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + next + " (in existing)"); next.state = ActivityState.RESUMED; mResumedActivity = next; next.task.touchActiveTime(); - if (mMainStack) { - mService.addRecentTaskLocked(next.task); - } + mService.addRecentTaskLocked(next.task); mService.updateLruProcessLocked(next.app, true); updateLRUListLocked(next); @@ -1719,10 +1737,10 @@ final class ActivityStack { mNoAnimActivities.clear(); return true; } - + try { // Deliver all pending results. - ArrayList a = next.results; + ArrayList<ResultInfo> a = next.results; if (a != null) { final int N = a.size(); if (!next.finishing && N > 0) { @@ -1740,13 +1758,13 @@ final class ActivityStack { EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, System.identityHashCode(next), next.task.taskId, next.shortComponentName); - + next.sleeping = false; showAskCompatModeDialogLocked(next); next.app.pendingUiClean = true; next.app.thread.scheduleResumeActivity(next.appToken, mService.isNextTransitionForward()); - + checkReadyForSleepLocked(); } catch (Exception e) { @@ -1810,36 +1828,38 @@ final class ActivityStack { return true; } + private final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition, Bundle options) { - final int NH = mHistory.size(); - - int addPos = -1; - + TaskRecord task = null; + TaskRecord rTask = r.task; + final int taskId = rTask.taskId; + if (taskForIdLocked(taskId) == null || newTask) { + // Last activity in task had been removed or ActivityManagerService is reusing task. + // Insert or replace. + // Might not even be in. + mTaskHistory.remove(rTask); + // Now put task at top. + mTaskHistory.add(rTask); + mService.mWindowManager.moveTaskToTop(taskId); + } if (!newTask) { // If starting in an existing task, find where that is... boolean startIt = true; - for (int i = NH-1; i >= 0; i--) { - ActivityRecord p = mHistory.get(i); - if (p.finishing) { - continue; - } - if (p.task == r.task) { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + task = mTaskHistory.get(taskNdx); + if (task == r.task) { // Here it is! Now, if this is not yet visible to the // user, then just add it without starting; it will // get started when the user navigates back to it. - addPos = i+1; if (!startIt) { - if (DEBUG_ADD_REMOVE) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos, - here); - } - mHistory.add(addPos, r); + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + + task, new RuntimeException("here").fillInStackTrace()); + task.addActivityToTop(r); r.putInHistory(); - mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId, - r.info.screenOrientation, r.fullscreen, + mService.mWindowManager.addAppToken(task.mActivities.indexOf(r), + r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, + r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0); if (VALIDATE_TOKENS) { validateAppTokensLocked(); @@ -1848,8 +1868,7 @@ final class ActivityStack { return; } break; - } - if (p.fullscreen) { + } else if (task.numFullscreen > 0) { startIt = false; } } @@ -1857,28 +1876,26 @@ final class ActivityStack { // Place a new activity at top of stack, so it is next to interact // with the user. - if (addPos < 0) { - addPos = NH; - } - + // If we are not placing the new activity frontmost, we do not want // to deliver the onUserLeaving callback to the actual frontmost // activity - if (addPos < NH) { + if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { mUserLeaving = false; - if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false"); + if (DEBUG_USER_LEAVING) Slog.v(TAG, + "startActivity() behind front, mUserLeaving=false"); } - + + task = r.task; + // Slot the activity into the history stack and proceed - if (DEBUG_ADD_REMOVE) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos, here); - } - mHistory.add(addPos, r); + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task, + new RuntimeException("here").fillInStackTrace()); + task.addActivityToTop(r); + r.putInHistory(); r.frontOfTask = newTask; - if (NH > 0) { + if (numActivities() > 1) { // We want to show the starting preview window if we are // switching to a new task, or the next activity's process is // not currently running. @@ -1903,8 +1920,8 @@ final class ActivityStack { mNoAnimActivities.remove(r); } r.updateOptionsLocked(options); - mService.mWindowManager.addAppToken( - addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen, + mService.mWindowManager.addAppToken(task.mActivities.indexOf(r), + r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0); boolean doShow = true; if (newTask) { @@ -1942,8 +1959,8 @@ final class ActivityStack { } else { // If this is the first activity, don't do any fancy animations, // because there is nothing for it to animate on top of. - mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId, - r.info.screenOrientation, r.fullscreen, + mService.mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, + r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0); ActivityOptions.abort(options); } @@ -1958,9 +1975,22 @@ final class ActivityStack { final void validateAppTokensLocked() { mValidateAppTokens.clear(); - mValidateAppTokens.ensureCapacity(mHistory.size()); - for (int i=0; i<mHistory.size(); i++) { - mValidateAppTokens.add(mHistory.get(i).appToken); + mValidateAppTokens.ensureCapacity(numActivities()); + final int numTasks = mTaskHistory.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + TaskRecord task = mTaskHistory.get(taskNdx); + final ArrayList<ActivityRecord> activities = task.mActivities; + if (activities.size() == 0) { + continue; + } + TaskGroup group = new TaskGroup(); + group.taskId = task.taskId; + mValidateAppTokens.add(group); + final int numActivities = activities.size(); + for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + group.tokens.add(r.appToken); + } } mService.mWindowManager.validateAppTokens(mValidateAppTokens); } @@ -1969,216 +1999,190 @@ final class ActivityStack { * Perform a reset of the given task, if needed as part of launching it. * Returns the new HistoryRecord at the top of the task. */ - private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop, - ActivityRecord newActivity) { - boolean forceReset = (newActivity.info.flags - &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; - if (ACTIVITY_INACTIVE_RESET_TIME > 0 - && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { - if ((newActivity.info.flags - &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) { - forceReset = true; - } - } - - final TaskRecord task = taskTop.task; - - // We are going to move through the history list so that we can look - // at each activity 'target' with 'below' either the interesting - // activity immediately below it in the stack or null. - ActivityRecord target = null; - int targetI = 0; - int taskTopI = -1; - int replyChainEnd = -1; - int lastReparentPos = -1; + /** + * Helper method for #resetTaskIfNeededLocked. + * We are inside of the task being reset... we'll either finish this activity, push it out + * for another task, or leave it as-is. + * @param task The task containing the Activity (taskTop) that might be reset. + * @param forceReset + * @return An ActivityOptions that needs to be processed. + */ + private final ActivityOptions resetTargetTaskIfNeededLocked(TaskRecord task, + boolean forceReset) { ActivityOptions topOptions = null; + + int replyChainEnd = -1; boolean canMoveOptions = true; - for (int i=mHistory.size()-1; i>=-1; i--) { - ActivityRecord below = i >= 0 ? mHistory.get(i) : null; - - if (below != null && below.finishing) { - continue; - } - // Don't check any lower in the stack if we're crossing a user boundary. - if (below != null && below.userId != taskTop.userId) { - break; - } - if (target == null) { - target = below; - targetI = i; - // If we were in the middle of a reply chain before this - // task, it doesn't appear like the root of the chain wants - // anything interesting, so drop it. - replyChainEnd = -1; - continue; - } - + + // We only do this for activities that are not the root of the task (since if we finish + // the root, we may no longer have the task!). + final ArrayList<ActivityRecord> activities = task.mActivities; + final int numActivities = activities.size(); + for (int i = numActivities - 1; i > 0; --i ) { + ActivityRecord target = activities.get(i); + final int flags = target.info.flags; - final boolean finishOnTaskLaunch = - (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; + (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; final boolean allowTaskReparenting = - (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0; - - if (target.task == task) { - // We are inside of the task being reset... we'll either - // finish this activity, push it out for another task, - // or leave it as-is. We only do this - // for activities that are not the root of the task (since - // if we finish the root, we may no longer have the task!). - if (taskTopI < 0) { - taskTopI = targetI; + (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0; + final boolean clearWhenTaskReset = + (target.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0; + + if (!finishOnTaskLaunch + && !clearWhenTaskReset + && target.resultTo != null) { + // If this activity is sending a reply to a previous + // activity, we can't do anything with it now until + // we reach the start of the reply chain. + // XXX note that we are assuming the result is always + // to the previous activity, which is almost always + // the case but we really shouldn't count on. + if (replyChainEnd < 0) { + replyChainEnd = i; } - if (below != null && below.task == task) { - final boolean clearWhenTaskReset = - (target.intent.getFlags() - &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0; - if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) { - // If this activity is sending a reply to a previous - // activity, we can't do anything with it now until - // we reach the start of the reply chain. - // XXX note that we are assuming the result is always - // to the previous activity, which is almost always - // the case but we really shouldn't count on. - if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting - && target.taskAffinity != null - && !target.taskAffinity.equals(task.affinity)) { - // If this activity has an affinity for another - // task, then we need to move it out of here. We will - // move it as far out of the way as possible, to the - // bottom of the activity stack. This also keeps it - // correctly ordered with any activities we previously - // moved. - ActivityRecord p = mHistory.get(0); - if (target.taskAffinity != null - && target.taskAffinity.equals(p.task.affinity)) { - // If the activity currently at the bottom has the - // same task affinity as the one we are moving, - // then merge it into the same task. - target.setTask(p.task, p.thumbHolder, false); - if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target - + " out to bottom task " + p.task); - } else { - mService.mCurTask++; - if (mService.mCurTask <= 0) { - mService.mCurTask = 1; - } - target.setTask(new TaskRecord(mService.mCurTask, target.info, null), - null, false); - target.task.affinityIntent = target.intent; - if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target - + " out to new task " + target.task); - } - mService.mWindowManager.setAppGroupId(target.appToken, task.taskId); - if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - int dstPos = 0; - ThumbnailHolder curThumbHolder = target.thumbHolder; - boolean gotOptions = !canMoveOptions; - for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = mHistory.get(srcPos); - if (p.finishing) { - continue; - } - if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p - + " out to target's task " + target.task); - p.setTask(target.task, curThumbHolder, false); - curThumbHolder = p.thumbHolder; - canMoveOptions = false; - if (!gotOptions && topOptions == null) { - topOptions = p.takeOptionsLocked(); - if (topOptions != null) { - gotOptions = true; - } - } - if (DEBUG_ADD_REMOVE) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Removing and adding activity " + p + " to stack at " - + dstPos, here); - } - mHistory.remove(srcPos); - mHistory.add(dstPos, p); - mService.mWindowManager.moveAppToken(dstPos, p.appToken); - mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId); - dstPos++; - if (VALIDATE_TOKENS) { - validateAppTokensLocked(); - } - i++; - } - if (taskTop == p) { - taskTop = below; - } - if (taskTopI == replyChainEnd) { - taskTopI = -1; - } - replyChainEnd = -1; - } else if (forceReset || finishOnTaskLaunch - || clearWhenTaskReset) { - // If the activity should just be removed -- either - // because it asks for it, or the task should be - // cleared -- then finish it and anything that is - // part of its reply chain. - if (clearWhenTaskReset) { - // In this case, we want to finish this activity - // and everything above it, so be sneaky and pretend - // like these are all in the reply chain. - replyChainEnd = targetI+1; - while (replyChainEnd < mHistory.size() && - (mHistory.get( - replyChainEnd)).task == task) { - replyChainEnd++; - } - replyChainEnd--; - } else if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - ActivityRecord p = null; - boolean gotOptions = !canMoveOptions; - for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = mHistory.get(srcPos); - if (p.finishing) { - continue; - } - canMoveOptions = false; - if (!gotOptions && topOptions == null) { - topOptions = p.takeOptionsLocked(); - if (topOptions != null) { - gotOptions = true; - } - } - if (finishActivityLocked(p, srcPos, - Activity.RESULT_CANCELED, null, "reset", false)) { - replyChainEnd--; - srcPos--; - } - } - if (taskTop == p) { - taskTop = below; - } - if (taskTopI == replyChainEnd) { - taskTopI = -1; + } else if (!finishOnTaskLaunch + && !clearWhenTaskReset + && allowTaskReparenting + && target.taskAffinity != null + && !target.taskAffinity.equals(task.affinity)) { + // If this activity has an affinity for another + // task, then we need to move it out of here. We will + // move it as far out of the way as possible, to the + // bottom of the activity stack. This also keeps it + // correctly ordered with any activities we previously + // moved. + TaskRecord bottomTask = mTaskHistory.get(0); + ActivityRecord p = bottomTask.mActivities.get(0); + if (target.taskAffinity != null + && target.taskAffinity.equals(p.task.affinity)) { + // If the activity currently at the bottom has the + // same task affinity as the one we are moving, + // then merge it into the same task. + setTask(target, p.task, p.thumbHolder, false); + if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + + " out to bottom task " + p.task); + } else { + setTask(target, createTaskRecord(mService.getNextTaskId(), target.info, null, + false), null, false); + target.task.affinityIntent = target.intent; + if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + + " out to new task " + target.task); + } + + final TaskRecord targetTask = target.task; + final int targetTaskId = targetTask.taskId; + mService.mWindowManager.setAppGroupId(target.appToken, targetTaskId); + + ThumbnailHolder curThumbHolder = target.thumbHolder; + boolean gotOptions = !canMoveOptions; + + final int start = replyChainEnd < 0 ? i : replyChainEnd; + for (int srcPos = start; srcPos >= i; --srcPos) { + p = activities.get(srcPos); + if (p.finishing) { + continue; + } + + curThumbHolder = p.thumbHolder; + canMoveOptions = false; + if (!gotOptions && topOptions == null) { + topOptions = p.takeOptionsLocked(); + if (topOptions != null) { + gotOptions = true; } - replyChainEnd = -1; - } else { - // If we were in the middle of a chain, well the - // activity that started it all doesn't want anything - // special, so leave it all as-is. - replyChainEnd = -1; } + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing activity " + p + " from task=" + + task + " adding to task=" + targetTask, + new RuntimeException("here").fillInStackTrace()); + if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p + + " out to target's task " + target.task); + setTask(p, targetTask, curThumbHolder, false); + targetTask.addActivityAtBottom(p); + + mService.mWindowManager.setAppGroupId(p.appToken, targetTaskId); + } + + mService.mWindowManager.moveTaskToBottom(targetTaskId); + if (VALIDATE_TOKENS) { + validateAppTokensLocked(); + } + + replyChainEnd = -1; + } else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) { + // If the activity should just be removed -- either + // because it asks for it, or the task should be + // cleared -- then finish it and anything that is + // part of its reply chain. + int end; + if (clearWhenTaskReset) { + // In this case, we want to finish this activity + // and everything above it, so be sneaky and pretend + // like these are all in the reply chain. + end = numActivities - 1; + } else if (replyChainEnd < 0) { + end = i; } else { - // Reached the bottom of the task -- any reply chain - // should be left as-is. - replyChainEnd = -1; + end = replyChainEnd; } + ActivityRecord p = null; + boolean gotOptions = !canMoveOptions; + for (int srcPos = i; srcPos <= end; srcPos++) { + p = activities.get(srcPos); + if (p.finishing) { + continue; + } + canMoveOptions = false; + if (!gotOptions && topOptions == null) { + topOptions = p.takeOptionsLocked(); + if (topOptions != null) { + gotOptions = true; + } + } + if (DEBUG_TASKS) Slog.w(TAG, + "resetTaskIntendedTask: calling finishActivity on " + p); + if (finishActivityLocked(p, Activity.RESULT_CANCELED, null, "reset", false)) { + end--; + srcPos--; + } + } + replyChainEnd = -1; + } else { + // If we were in the middle of a chain, well the + // activity that started it all doesn't want anything + // special, so leave it all as-is. + replyChainEnd = -1; + } + } + + return topOptions; + } - } else if (target.resultTo != null && (below == null - || below.task == target.task)) { + /** + * Helper method for #resetTaskIfNeededLocked. Processes all of the activities in a given + * TaskRecord looking for an affinity with the task of resetTaskIfNeededLocked.taskTop. + * @param affinityTask The task we are looking for an affinity to. + * @param task Task that resetTaskIfNeededLocked.taskTop belongs to. + * @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked. + * @param forceReset Flag passed in to resetTaskIfNeededLocked. + */ + private final int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task, + boolean topTaskIsHigher, boolean forceReset, int taskInsertionPoint) { + int replyChainEnd = -1; + final int taskId = task.taskId; + final String taskAffinity = task.affinity; + + final ArrayList<ActivityRecord> activities = affinityTask.mActivities; + final int numActivities = activities.size(); + // Do not operate on the root Activity. + for (int i = numActivities - 1; i > 0; --i) { + ActivityRecord target = activities.get(i); + + final int flags = target.info.flags; + boolean finishOnTaskLaunch = (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; + boolean allowTaskReparenting = (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0; + + if (target.resultTo != null) { // If this activity is sending a reply to a previous // activity, we can't do anything with it now until // we reach the start of the reply chain. @@ -2186,14 +2190,13 @@ final class ActivityStack { // to the previous activity, which is almost always // the case but we really shouldn't count on. if (replyChainEnd < 0) { - replyChainEnd = targetI; + replyChainEnd = i; } - - } else if (taskTopI >= 0 && allowTaskReparenting - && task.affinity != null - && task.affinity.equals(target.taskAffinity)) { - // We are inside of another task... if this activity has - // an affinity for our task, then either remove it if we are + } else if (topTaskIsHigher + && allowTaskReparenting + && taskAffinity != null + && taskAffinity.equals(target.taskAffinity)) { + // This activity has an affinity for our task. Either remove it if we are // clearing or move it over to our task. Note that // we currently punt on the case where we are resetting a // task that is not at the top but who has activities above @@ -2204,93 +2207,104 @@ final class ActivityStack { // someone starts an activity in a new task from an activity // in a task that is not currently on top.) if (forceReset || finishOnTaskLaunch) { - if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - ActivityRecord p = null; - if (DEBUG_TASKS) Slog.v(TAG, "Finishing task at index " - + targetI + " to " + replyChainEnd); - for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = mHistory.get(srcPos); + final int start = replyChainEnd >= 0 ? replyChainEnd : i; + if (DEBUG_TASKS) Slog.v(TAG, "Finishing task at index " + start + " to " + i); + for (int srcPos = start; srcPos >= i; --srcPos) { + final ActivityRecord p = activities.get(srcPos); if (p.finishing) { continue; } - if (finishActivityLocked(p, srcPos, - Activity.RESULT_CANCELED, null, "reset", false)) { - taskTopI--; - lastReparentPos--; - replyChainEnd--; - srcPos--; - } + finishActivityLocked(p, Activity.RESULT_CANCELED, null, "reset", false); } - replyChainEnd = -1; } else { - if (replyChainEnd < 0) { - replyChainEnd = targetI; + if (taskInsertionPoint < 0) { + taskInsertionPoint = task.mActivities.size(); + } - if (DEBUG_TASKS) Slog.v(TAG, "Reparenting task at index " - + targetI + " to " + replyChainEnd); - for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) { - ActivityRecord p = mHistory.get(srcPos); - if (p.finishing) { - continue; - } - if (lastReparentPos < 0) { - lastReparentPos = taskTopI; - taskTop = p; - } else { - lastReparentPos--; - } - if (DEBUG_ADD_REMOVE) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Removing and adding activity " + p + " to stack at " - + lastReparentPos, here); - } - mHistory.remove(srcPos); - p.setTask(task, null, false); - mHistory.add(lastReparentPos, p); - if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p - + " from " + srcPos + " to " + lastReparentPos + + final int start = replyChainEnd >= 0 ? replyChainEnd : i; + if (DEBUG_TASKS) Slog.v(TAG, "Reparenting from task=" + affinityTask + ":" + + start + "-" + i + " to task=" + task + ":" + taskInsertionPoint); + for (int srcPos = start; srcPos >= i; --srcPos) { + final ActivityRecord p = activities.get(srcPos); + setTask(p, task, null, false); + task.addActivityAtIndex(taskInsertionPoint, p); + + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + p + + " to stack at " + task, + new RuntimeException("here").fillInStackTrace()); + if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " from " + srcPos + " in to resetting task " + task); - mService.mWindowManager.moveAppToken(lastReparentPos, p.appToken); - mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId); - if (VALIDATE_TOKENS) { - validateAppTokensLocked(); - } + mService.mWindowManager.setAppGroupId(p.appToken, taskId); + } + mService.mWindowManager.moveTaskToTop(taskId); + if (VALIDATE_TOKENS) { + validateAppTokensLocked(); } - replyChainEnd = -1; - + // Now we've moved it in to place... but what if this is // a singleTop activity and we have put it on top of another // instance of the same activity? Then we drop the instance // below so it remains singleTop. if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) { - for (int j=lastReparentPos-1; j>=0; j--) { - ActivityRecord p = mHistory.get(j); - if (p.finishing) { - continue; - } + ArrayList<ActivityRecord> taskActivities = task.mActivities; + boolean found = false; + int targetNdx = taskActivities.indexOf(target); + if (targetNdx > 0) { + ActivityRecord p = taskActivities.get(targetNdx - 1); if (p.intent.getComponent().equals(target.intent.getComponent())) { - if (finishActivityLocked(p, j, - Activity.RESULT_CANCELED, null, "replace", false)) { - taskTopI--; - lastReparentPos--; - } + finishActivityLocked(p, Activity.RESULT_CANCELED, null, "replace", + false); } } } } - } else if (below != null && below.task != target.task) { - // We hit the botton of a task; the reply chain can't - // pass through it. replyChainEnd = -1; } - - target = below; - targetI = i; } + return taskInsertionPoint; + } + + private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop, + ActivityRecord newActivity) { + boolean forceReset = + (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; + if (ACTIVITY_INACTIVE_RESET_TIME > 0 + && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { + if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) { + forceReset = true; + } + } + + final TaskRecord task = taskTop.task; + + /** False until we evaluate the TaskRecord associated with taskTop. Switches to true + * for remaining tasks. Used for later tasks to reparent to task. */ + boolean taskFound = false; + + /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */ + ActivityOptions topOptions = null; + + // Preserve the location for reparenting in the new task. + int reparentInsertionPoint = -1; + + for (int i = mTaskHistory.size() - 1; i >= 0; --i) { + final TaskRecord targetTask = mTaskHistory.get(i); + + if (targetTask == task) { + topOptions = resetTargetTaskIfNeededLocked(task, forceReset); + taskFound = true; + } else { + reparentInsertionPoint = resetAffinityTaskIfNeededLocked(targetTask, task, + taskFound, forceReset, reparentInsertionPoint); + } + } + + int taskNdx = mTaskHistory.indexOf(task); + do { + taskTop = mTaskHistory.get(taskNdx--).getTopActivity(); + } while (taskTop == null && taskNdx >= 0); if (topOptions != null) { // If we got some ActivityOptions from an activity on top that @@ -2304,7 +2318,7 @@ final class ActivityStack { return taskTop; } - + /** * Perform clear operation as requested by * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the @@ -2316,39 +2330,22 @@ final class ActivityStack { * @return Returns the old activity that should be continued to be used, * or null if none was found. */ - private final ActivityRecord performClearTaskLocked(int taskId, + private final ActivityRecord performClearTaskLocked(TaskRecord task, ActivityRecord newR, int launchFlags) { - int i = mHistory.size(); - - // First find the requested task. - while (i > 0) { - i--; - ActivityRecord r = mHistory.get(i); - if (r.task.taskId == taskId) { - i++; - break; - } - } - - // Now clear it. - while (i > 0) { - i--; - ActivityRecord r = mHistory.get(i); + + final ArrayList<ActivityRecord> activities = task.mActivities; + int numActivities = activities.size(); + for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord r = activities.get(activityNdx); if (r.finishing) { continue; } - if (r.task.taskId != taskId) { - return null; - } if (r.realActivity.equals(newR.realActivity)) { // Here it is! Now finish everything in front... ActivityRecord ret = r; - while (i < (mHistory.size()-1)) { - i++; - r = mHistory.get(i); - if (r.task.taskId != taskId) { - break; - } + + for (++activityNdx; activityNdx < numActivities; ++activityNdx) { + r = activities.get(activityNdx); if (r.finishing) { continue; } @@ -2356,27 +2353,26 @@ final class ActivityStack { if (opts != null) { ret.updateOptionsLocked(opts); } - if (finishActivityLocked(r, i, Activity.RESULT_CANCELED, - null, "clear", false)) { - i--; + if (finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) { + --activityNdx; + --numActivities; } } - + // Finally, if this is a normal launch mode (that is, not // expecting onNewIntent()), then we will finish the current // instance of the activity so a new fresh one can be started. if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE - && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { + && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { if (!ret.finishing) { - int index = indexOfTokenLocked(ret.appToken); - if (index >= 0) { - finishActivityLocked(ret, index, Activity.RESULT_CANCELED, - null, "clear", false); + if (activities.contains(ret)) { + finishActivityLocked(ret, Activity.RESULT_CANCELED, null, + "clear", false); } return null; } } - + return ret; } } @@ -2388,20 +2384,17 @@ final class ActivityStack { * Completely remove all activities associated with an existing * task starting at a specified index. */ - private final void performClearTaskAtIndexLocked(int taskId, int i) { - while (i < mHistory.size()) { - ActivityRecord r = mHistory.get(i); - if (r.task.taskId != taskId) { - // Whoops hit the end. - return; - } + private final void performClearTaskAtIndexLocked(TaskRecord task, int activityNdx) { + final ArrayList<ActivityRecord> activities = task.mActivities; + int numActivities = activities.size(); + for ( ; activityNdx < numActivities; ++activityNdx) { + final ActivityRecord r = activities.get(activityNdx); if (r.finishing) { - i++; continue; } - if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED, - null, "clear", false)) { - i++; + if (finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) { + --activityNdx; + --numActivities; } } } @@ -2409,75 +2402,42 @@ final class ActivityStack { /** * Completely remove all activities associated with an existing task. */ - private final void performClearTaskLocked(int taskId) { - int i = mHistory.size(); - - // First find the requested task. - while (i > 0) { - i--; - ActivityRecord r = mHistory.get(i); - if (r.task.taskId == taskId) { - i++; - break; - } - } - - // Now find the start and clear it. - while (i > 0) { - i--; - ActivityRecord r = mHistory.get(i); - if (r.finishing) { - continue; - } - if (r.task.taskId != taskId) { - // We hit the bottom. Now finish it all... - performClearTaskAtIndexLocked(taskId, i+1); - return; - } - } + private final void performClearTaskLocked(TaskRecord task) { + performClearTaskAtIndexLocked(task, 0); } /** * Find the activity in the history stack within the given task. Returns * the index within the history at which it's found, or < 0 if not found. */ - private final int findActivityInHistoryLocked(ActivityRecord r, int task) { - int i = mHistory.size(); - while (i > 0) { - i--; - ActivityRecord candidate = mHistory.get(i); + private final ActivityRecord findActivityInHistoryLocked(ActivityRecord r, TaskRecord task) { + final ComponentName realActivity = r.realActivity; + ArrayList<ActivityRecord> activities = task.mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord candidate = activities.get(activityNdx); if (candidate.finishing) { continue; } - if (candidate.task.taskId != task) { - break; - } - if (candidate.realActivity.equals(r.realActivity)) { - return i; + if (candidate.realActivity.equals(realActivity)) { + return candidate; } } - - return -1; + return null; } /** * Reorder the history stack so that the activity at the given index is * brought to the front. */ - private final ActivityRecord moveActivityToFrontLocked(int where) { - ActivityRecord newTop = mHistory.remove(where); - int top = mHistory.size(); - ActivityRecord oldTop = mHistory.get(top-1); - if (DEBUG_ADD_REMOVE) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Removing and adding activity " + newTop + " to stack at " - + top, here); - } - mHistory.add(top, newTop); - oldTop.frontOfTask = false; + private final void moveActivityToFrontLocked(ActivityRecord newTop) { + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop + + " to stack at top", new RuntimeException("here").fillInStackTrace()); + + final TaskRecord task = newTop.task; + task.getTopActivity().frontOfTask = false; + task.mActivities.remove(newTop); + task.mActivities.add(newTop); newTop.frontOfTask = true; - return newTop; } final int startActivityLocked(IApplicationThread caller, @@ -2485,7 +2445,6 @@ final class ActivityStack { String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options, boolean componentSpecified, ActivityRecord[] outActivity) { - int err = ActivityManager.START_SUCCESS; ProcessRecord callerApp = null; @@ -2511,11 +2470,10 @@ final class ActivityStack { ActivityRecord sourceRecord = null; ActivityRecord resultRecord = null; if (resultTo != null) { - int index = indexOfTokenLocked(resultTo); + sourceRecord = isInStackLocked(resultTo); if (DEBUG_RESULTS) Slog.v( - TAG, "Will send result to " + resultTo + " (index " + index + ")"); - if (index >= 0) { - sourceRecord = mHistory.get(index); + TAG, "Will send result to " + resultTo + " " + sourceRecord); + if (sourceRecord != null) { if (requestCode >= 0 && !sourceRecord.finishing) { resultRecord = sourceRecord; } @@ -2604,7 +2562,7 @@ final class ActivityStack { } catch (RemoteException e) { mService.mController = null; } - + if (abort) { if (resultRecord != null) { sendActivityResultLocked(-1, @@ -2620,7 +2578,7 @@ final class ActivityStack { } } - ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid, callingPackage, + ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified); if (outActivity != null) { @@ -2631,17 +2589,15 @@ final class ActivityStack { if (mResumedActivity == null || mResumedActivity.info.applicationInfo.uid != callingUid) { if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) { - PendingActivityLaunch pal = new PendingActivityLaunch(); - pal.r = r; - pal.sourceRecord = sourceRecord; - pal.startFlags = startFlags; + PendingActivityLaunch pal = + new PendingActivityLaunch(r, sourceRecord, startFlags, this); mService.mPendingActivityLaunches.add(pal); mDismissKeyguardOnNextActivity = false; 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 @@ -2652,10 +2608,10 @@ final class ActivityStack { } else { mService.mDidAppSwitch = true; } - + mService.doPendingActivityLaunchesLocked(false); } - + err = startActivityUncheckedLocked(r, sourceRecord, startFlags, true, options); if (mDismissKeyguardOnNextActivity && mPausingActivity == null) { @@ -2668,7 +2624,7 @@ final class ActivityStack { } return err; } - + final void moveHomeToFrontFromLaunchLocked(int launchFlags) { if ((launchFlags & (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) @@ -2686,20 +2642,20 @@ final class ActivityStack { final int callingUid = r.launchedFromUid; int launchFlags = intent.getFlags(); - + // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving); - + // If the caller has asked not to resume at this point, we make note // of this in the record so that we can skip it when trying to find // the top running activity. if (!doResume) { r.delayedResume = true; } - + ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; @@ -2821,7 +2777,7 @@ final class ActivityStack { // existing task with its new activity. Well that should // not be too hard... reuseTask = taskTop.task; - performClearTaskLocked(taskTop.task.taskId); + performClearTaskLocked(taskTop.task); reuseTask.setIntent(r.intent, r.info); } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0 || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK @@ -2830,8 +2786,7 @@ final class ActivityStack { // from the task up to the one being started. In most // cases this means we are resetting the task to its // initial state. - ActivityRecord top = performClearTaskLocked( - taskTop.task.taskId, r, launchFlags); + ActivityRecord top = performClearTaskLocked(taskTop.task, r, launchFlags); if (top != null) { if (top.frontOfTask) { // Activity aliases may mean we use different @@ -2962,30 +2917,25 @@ final class ActivityStack { if (r.resultTo == null && !addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { if (reuseTask == null) { - // todo: should do better management of integers. - mService.mCurTask++; - if (mService.mCurTask <= 0) { - mService.mCurTask = 1; - } - r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true); + setTask(r, createTaskRecord(mService.getNextTaskId(), r.info, intent, true), null, + true); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " + r.task); } else { - r.setTask(reuseTask, reuseTask, true); + setTask(r, reuseTask, reuseTask, true); } newTask = true; if (!movedHome) { moveHomeToFrontFromLaunchLocked(launchFlags); } - + } else if (sourceRecord != null) { if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { // In this case, we are adding the activity to an existing // task, but the caller has asked to clear that task if the // activity is already running. - ActivityRecord top = performClearTaskLocked( - sourceRecord.task.taskId, r, launchFlags); + ActivityRecord top = performClearTaskLocked(sourceRecord.task, r, launchFlags); keepCurTransition = true; if (top != null) { logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); @@ -3003,9 +2953,9 @@ final class ActivityStack { // In this case, we are launching an activity in our own task // that may already be running somewhere in the history, and // we want to shuffle it to the front of the stack if so. - int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId); - if (where >= 0) { - ActivityRecord top = moveActivityToFrontLocked(where); + final ActivityRecord top = findActivityInHistoryLocked(r, sourceRecord.task); + if (top != null) { + moveActivityToFrontLocked(top); logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); top.updateOptionsLocked(options); top.deliverNewIntentLocked(callingUid, r.intent); @@ -3018,7 +2968,7 @@ final class ActivityStack { // An existing activity is starting this new activity, so we want // to keep the new one in the same task as the one that is starting // it. - r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false); + setTask(r, sourceRecord.task, sourceRecord.thumbHolder, false); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in existing task " + r.task); @@ -3026,12 +2976,20 @@ final class ActivityStack { // This not being started from an existing activity, and not part // of a new task... just put it in the top task, though these days // this case should never happen. - final int N = mHistory.size(); - ActivityRecord prev = - N > 0 ? mHistory.get(N-1) : null; - r.setTask(prev != null + ActivityRecord prev = null; + // Iterate to find the first non-empty task stack. Note that this code can + // go away once we stop storing tasks with empty mActivities lists. + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + prev = activities.get(activityNdx); + break; + } + } + setTask(r, prev != null ? prev.task - : new TaskRecord(mService.mCurTask, r.info, intent), null, true); + : createTaskRecord(mService.getNextTaskId(), r.info, intent, true), null, + true); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new guessed " + r.task); } @@ -3523,8 +3481,7 @@ final class ActivityStack { } // Get the activity record. - int index = indexOfActivityLocked(r); - if (index >= 0) { + if (isInStackLocked(token) != null) { res = r; if (fromTimeout) { @@ -3579,9 +3536,9 @@ final class ActivityStack { finishes = new ArrayList<ActivityRecord>(mFinishingActivities); mFinishingActivities.clear(); } - if ((NT=mService.mCancelledThumbnails.size()) > 0) { - thumbnails = new ArrayList<ActivityRecord>(mService.mCancelledThumbnails); - mService.mCancelledThumbnails.clear(); + if ((NT=mCancelledThumbnails.size()) > 0) { + thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails); + mCancelledThumbnails.clear(); } if (mMainStack) { @@ -3609,7 +3566,7 @@ final class ActivityStack { // Stop any activities that are scheduled to do so but have been // waiting for the next one to start. for (i=0; i<NS; i++) { - ActivityRecord r = (ActivityRecord)stops.get(i); + ActivityRecord r = stops.get(i); synchronized (mService) { if (r.finishing) { finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false); @@ -3622,7 +3579,7 @@ final class ActivityStack { // Finish any activities that are scheduled to do so but have been // waiting for the next one to start. for (i=0; i<NF; i++) { - ActivityRecord r = (ActivityRecord)finishes.get(i); + ActivityRecord r = finishes.get(i); synchronized (mService) { activityRemoved = destroyActivityLocked(r, true, false, "finish-idle"); } @@ -3630,7 +3587,7 @@ final class ActivityStack { // Report back to any thumbnail receivers. for (i=0; i<NT; i++) { - ActivityRecord r = (ActivityRecord)thumbnails.get(i); + ActivityRecord r = thumbnails.get(i); mService.sendPendingThumbnail(r, null, null, null, true); } @@ -3663,63 +3620,83 @@ final class ActivityStack { */ final boolean requestFinishActivityLocked(IBinder token, int resultCode, Intent resultData, String reason, boolean oomAdj) { - int index = indexOfTokenLocked(token); + ActivityRecord r = isInStackLocked(token); if (DEBUG_RESULTS || DEBUG_STATES) Slog.v( - TAG, "Finishing activity @" + index + ": token=" + token + TAG, "Finishing activity token=" + token + " r=" + ", result=" + resultCode + ", data=" + resultData + ", reason=" + reason); - if (index < 0) { + if (r == null) { return false; } - ActivityRecord r = mHistory.get(index); - finishActivityLocked(r, index, resultCode, resultData, reason, oomAdj); + finishActivityLocked(r, resultCode, resultData, reason, oomAdj); return true; } - final void finishSubActivityLocked(IBinder token, String resultWho, int requestCode) { - ActivityRecord self = isInStackLocked(token); - if (self == null) { - return; - } - - int i; - for (i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); - if (r.resultTo == self && r.requestCode == requestCode) { - if ((r.resultWho == null && resultWho == null) || - (r.resultWho != null && r.resultWho.equals(resultWho))) { - finishActivityLocked(r, i, - Activity.RESULT_CANCELED, null, "request-sub", false); + final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord r = activities.get(activityNdx); + if (r.resultTo == self && r.requestCode == requestCode) { + if ((r.resultWho == null && resultWho == null) || + (r.resultWho != null && r.resultWho.equals(resultWho))) { + finishActivityLocked(r, Activity.RESULT_CANCELED, null, "request-sub", + false); + } } } } mService.updateOomAdjLocked(); } - final boolean finishActivityAffinityLocked(IBinder token) { - int index = indexOfTokenLocked(token); - if (DEBUG_RESULTS) Slog.v( - TAG, "Finishing activity affinity @" + index + ": token=" + token); - if (index < 0) { - return false; + final void finishTopRunningActivityLocked(ProcessRecord app) { + ActivityRecord r = topRunningActivityLocked(null); + if (r != null && r.app == app) { + // If the top running activity is from this crashing + // process, then terminate it to avoid getting in a loop. + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + int taskNdx = mTaskHistory.indexOf(r.task); + int activityNdx = r.task.mActivities.indexOf(r); + finishActivityLocked(r, Activity.RESULT_CANCELED, null, "crashed", false); + // Also terminate any activities below it that aren't yet + // stopped, to avoid a situation where one will get + // re-start our crashing activity once it gets resumed again. + --activityNdx; + if (activityNdx < 0) { + do { + --taskNdx; + if (taskNdx < 0) { + break; + } + activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1; + } while (activityNdx < 0); + } + if (activityNdx >= 0) { + r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx); + if (r.state == ActivityState.RESUMED + || r.state == ActivityState.PAUSING + || r.state == ActivityState.PAUSED) { + if (!r.isHomeActivity || mService.mHomeProcess != r.app) { + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + finishActivityLocked(r, Activity.RESULT_CANCELED, null, "crashed", false); + } + } + } } - ActivityRecord r = mHistory.get(index); + } - while (index >= 0) { - ActivityRecord cur = mHistory.get(index); - if (cur.task != r.task) { - break; - } - if (cur.taskAffinity == null && r.taskAffinity != null) { + final boolean finishActivityAffinityLocked(ActivityRecord r) { + ArrayList<ActivityRecord> activities = r.task.mActivities; + for (int index = activities.indexOf(r); index >= 0; --index) { + ActivityRecord cur = activities.get(index); + if (!Objects.equal(cur.taskAffinity, r.taskAffinity)) { break; } - if (cur.taskAffinity != null && !cur.taskAffinity.equals(r.taskAffinity)) { - break; - } - finishActivityLocked(cur, index, Activity.RESULT_CANCELED, null, - "request-affinity", true); - index--; + finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", + true); } return true; } @@ -3755,17 +3732,17 @@ final class ActivityStack { * @return Returns true if this activity has been removed from the history * list, or false if it is still in the list and will be removed later. */ - final boolean finishActivityLocked(ActivityRecord r, int index, - int resultCode, Intent resultData, String reason, boolean oomAdj) { - return finishActivityLocked(r, index, resultCode, resultData, reason, false, oomAdj); + final boolean finishActivityLocked(ActivityRecord r, int resultCode, + Intent resultData, String reason, boolean oomAdj) { + return finishActivityLocked(r, resultCode, resultData, reason, false, oomAdj); } /** * @return Returns true if this activity has been removed from the history * list, or false if it is still in the list and will be removed later. */ - final boolean finishActivityLocked(ActivityRecord r, int index, int resultCode, - Intent resultData, String reason, boolean immediate, boolean oomAdj) { + final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, + String reason, boolean immediate, boolean oomAdj) { if (r.finishing) { Slog.w(TAG, "Duplicate finish request for " + r); return false; @@ -3775,19 +3752,19 @@ final class ActivityStack { EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), r.task.taskId, r.shortComponentName, reason); - if (index < (mHistory.size()-1)) { - ActivityRecord next = mHistory.get(index+1); - if (next.task == r.task) { - if (r.frontOfTask) { - // The next activity is now the front of the task. - next.frontOfTask = true; - } - if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { - // If the caller asked that this activity (and all above it) - // be cleared when the task is reset, don't lose that information, - // but propagate it up to the next activity. - next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - } + final ArrayList<ActivityRecord> activities = r.task.mActivities; + final int index = activities.indexOf(r); + if (index < (activities.size() - 1)) { + ActivityRecord next = activities.get(index+1); + if (r.frontOfTask) { + // The next activity is now the front of the task. + next.frontOfTask = true; + } + if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { + // If the caller asked that this activity (and all above it) + // be cleared when the task is reset, don't lose that information, + // but propagate it up to the next activity. + next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); } } @@ -3804,21 +3781,19 @@ final class ActivityStack { // There are clients waiting to receive thumbnails so, in case // this is an activity that someone is waiting for, add it // to the pending list so we can correctly update the clients. - mService.mCancelledThumbnails.add(r); + mCancelledThumbnails.add(r); } if (immediate) { - return finishCurrentActivityLocked(r, index, - FINISH_IMMEDIATELY, oomAdj) == null; + return finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, oomAdj) == null; } else if (mResumedActivity == r) { - boolean endTask = index <= 0 - || (mHistory.get(index-1)).task != r.task; + boolean endTask = index <= 0; if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare close transition: finishing " + r); mService.mWindowManager.prepareAppTransition(endTask ? AppTransition.TRANSIT_TASK_CLOSE : AppTransition.TRANSIT_ACTIVITY_CLOSE, false); - + // Tell window manager to prepare for this one to be removed. mService.mWindowManager.setAppVisibility(r.appToken, false); @@ -3832,8 +3807,7 @@ final class ActivityStack { // If the activity is PAUSING, we will complete the finish once // it is done pausing; else we can just directly finish it here. if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r); - return finishCurrentActivityLocked(r, index, - FINISH_AFTER_PAUSE, oomAdj) == null; + return finishCurrentActivityLocked(r, FINISH_AFTER_PAUSE, oomAdj) == null; } else { if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r); } @@ -3845,18 +3819,9 @@ final class ActivityStack { private static final int FINISH_AFTER_PAUSE = 1; private static final int FINISH_AFTER_VISIBLE = 2; - private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, - int mode, boolean oomAdj) { - final int index = indexOfActivityLocked(r); - if (index < 0) { - return null; - } - - return finishCurrentActivityLocked(r, index, mode, oomAdj); - } private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, - int index, int mode, boolean oomAdj) { + int mode, boolean oomAdj) { // First things first: if this activity is currently visible, // and the resumed activity is not yet visible, then hold off on // finishing until the resumed one becomes visible. @@ -3903,16 +3868,95 @@ final class ActivityStack { resumeTopActivityLocked(null); } return activityRemoved ? null : r; - } else { - // Need to go through the full pause cycle to get this - // activity into the stopped state and then finish it. - if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r); - mFinishingActivities.add(r); - resumeTopActivityLocked(null); } + + // Need to go through the full pause cycle to get this + // activity into the stopped state and then finish it. + if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r); + mFinishingActivities.add(r); + resumeTopActivityLocked(null); return r; } + final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode, + Intent resultData) { + final ActivityRecord srec = ActivityRecord.forToken(token); + final TaskRecord task = srec.task; + final ArrayList<ActivityRecord> activities = task.mActivities; + final int start = activities.indexOf(srec); + if (!mTaskHistory.contains(task) || (start < 0)) { + return false; + } + int finishTo = start - 1; + ActivityRecord parent = finishTo < 0 ? null : activities.get(finishTo); + boolean foundParentInTask = false; + final ComponentName dest = destIntent.getComponent(); + if (start > 0 && dest != null) { + for (int i = finishTo; i >= 0; i--) { + ActivityRecord r = activities.get(i); + if (r.info.packageName.equals(dest.getPackageName()) && + r.info.name.equals(dest.getClassName())) { + finishTo = i; + parent = r; + foundParentInTask = true; + break; + } + } + } + + IActivityController controller = mService.mController; + if (controller != null) { + ActivityRecord next = topRunningActivityLocked(srec.appToken, 0); + if (next != null) { + // ask watcher if this is allowed + boolean resumeOK = true; + try { + resumeOK = controller.activityResuming(next.packageName); + } catch (RemoteException e) { + mService.mController = null; + } + + if (!resumeOK) { + return false; + } + } + } + final long origId = Binder.clearCallingIdentity(); + for (int i = start; i > finishTo; i--) { + ActivityRecord r = activities.get(i); + requestFinishActivityLocked(r.appToken, resultCode, resultData, "navigate-up", true); + // Only return the supplied result for the first activity finished + resultCode = Activity.RESULT_CANCELED; + resultData = null; + } + + if (parent != null && foundParentInTask) { + final int parentLaunchMode = parent.info.launchMode; + final int destIntentFlags = destIntent.getFlags(); + if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE || + parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK || + parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP || + (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { + parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent); + } else { + try { + ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo( + destIntent.getComponent(), 0, srec.userId); + int res = startActivityLocked(srec.app.thread, destIntent, + null, aInfo, parent.appToken, null, + 0, -1, parent.launchedFromUid, parent.launchedFromPackage, + 0, null, true, null); + foundParentInTask = res == ActivityManager.START_SUCCESS; + } catch (RemoteException e) { + foundParentInTask = false; + } + requestFinishActivityLocked(parent.appToken, resultCode, + resultData, "navigate-up", true); + } + } + Binder.restoreCallingIdentity(origId); + return foundParentInTask; + } /** * Perform the common clean-up of an activity record. This is called both * as part of destroyActivityLocked() (when destroying the client-side @@ -3964,7 +4008,7 @@ final class ActivityStack { // There are clients waiting to receive thumbnails so, in case // this is an activity that someone is waiting for, add it // to the pending list so we can correctly update the clients. - mService.mCancelledThumbnails.add(r); + mCancelledThumbnails.add(r); } // Get rid of any pending idle timeouts. @@ -3987,11 +4031,12 @@ final class ActivityStack { here.fillInStackTrace(); Slog.i(TAG, "Removing activity " + r + " from stack"); } - mHistory.remove(r); + if (r.task != null) { + removeActivity(r); + } r.takeFromHistory(); removeTimeoutsForActivityLocked(r); - if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r - + " (removed from history)"); + if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (removed from history)"); r.state = ActivityState.DESTROYED; if (DEBUG_APP) Slog.v(TAG, "Clearing app during remove for activity " + r); r.app = null; @@ -4027,31 +4072,34 @@ final class ActivityStack { final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj, String reason) { boolean lastIsOpaque = false; boolean activityRemoved = false; - for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = mHistory.get(i); - if (r.finishing) { - continue; - } - if (r.fullscreen) { - lastIsOpaque = true; - } - if (owner != null && r.app != owner) { - continue; - } - if (!lastIsOpaque) { - continue; - } - // We can destroy this one if we have its icicle saved and - // it is not in the process of pausing/stopping/finishing. - if (r.app != null && r != mResumedActivity && r != mPausingActivity - && r.haveState && !r.visible && r.stopped - && r.state != ActivityState.DESTROYING - && r.state != ActivityState.DESTROYED) { - if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state - + " resumed=" + mResumedActivity - + " pausing=" + mPausingActivity); - if (destroyActivityLocked(r, true, oomAdj, reason)) { - activityRemoved = true; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.finishing) { + continue; + } + if (r.fullscreen) { + lastIsOpaque = true; + } + if (owner != null && r.app != owner) { + continue; + } + if (!lastIsOpaque) { + continue; + } + // We can destroy this one if we have its icicle saved and + // it is not in the process of pausing/stopping/finishing. + if (r.app != null && r != mResumedActivity && r != mPausingActivity + && r.haveState && !r.visible && r.stopped + && r.state != ActivityState.DESTROYING + && r.state != ActivityState.DESTROYED) { + if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state + + " resumed=" + mResumedActivity + + " pausing=" + mPausingActivity); + if (destroyActivityLocked(r, true, oomAdj, reason)) { + activityRemoved = true; + } } } } @@ -4083,10 +4131,7 @@ final class ActivityStack { if (hadApp) { if (removeFromApp) { - int idx = r.app.activities.indexOf(r); - if (idx >= 0) { - r.app.activities.remove(idx); - } + r.app.activities.remove(r); if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) { mService.mHeavyWeightProcess = null; mService.mHandler.sendEmptyMessage( @@ -4162,38 +4207,35 @@ final class ActivityStack { return removedFromHistory; } - final void activityDestroyed(IBinder token) { - synchronized (mService) { - final long origId = Binder.clearCallingIdentity(); - try { - ActivityRecord r = ActivityRecord.forToken(token); - if (r != null) { - mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r); - } + final void activityDestroyedLocked(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + ActivityRecord r = ActivityRecord.forToken(token); + if (r != null) { + mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r); + } - int index = indexOfActivityLocked(r); - if (index >= 0) { - if (r.state == ActivityState.DESTROYING) { - cleanUpActivityLocked(r, true, false); - removeActivityFromHistoryLocked(r); - } + if (isInStackLocked(token) != null) { + if (r.state == ActivityState.DESTROYING) { + cleanUpActivityLocked(r, true, false); + removeActivityFromHistoryLocked(r); } - resumeTopActivityLocked(null); - } finally { - Binder.restoreCallingIdentity(origId); } + resumeTopActivityLocked(null); + } finally { + Binder.restoreCallingIdentity(origId); } } - private void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app, - String listName) { + private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list, + ProcessRecord app, String listName) { int i = list.size(); if (DEBUG_CLEANUP) Slog.v( TAG, "Removing app " + app + " from list " + listName + " with " + i + " entries"); while (i > 0) { i--; - ActivityRecord r = (ActivityRecord)list.get(i); + ActivityRecord r = list.get(i); if (DEBUG_CLEANUP) Slog.v(TAG, "Record #" + i + " " + r); if (r.app == app) { if (DEBUG_CLEANUP) Slog.v(TAG, "---> REMOVING this entry!"); @@ -4214,90 +4256,93 @@ final class ActivityStack { boolean hasVisibleActivities = false; // Clean out the history list. - int i = mHistory.size(); + int i = numActivities(); if (DEBUG_CLEANUP) Slog.v( TAG, "Removing app " + app + " from history with " + i + " entries"); - while (i > 0) { - i--; - ActivityRecord r = (ActivityRecord)mHistory.get(i); - if (DEBUG_CLEANUP) Slog.v( - TAG, "Record #" + i + " " + r + ": app=" + r.app); - if (r.app == app) { - boolean remove; - if ((!r.haveState && !r.stateNotNeeded) || r.finishing) { - // Don't currently have state for the activity, or - // it is finishing -- always remove it. - remove = true; - } else if (r.launchCount > 2 && - r.lastLaunchTime > (SystemClock.uptimeMillis()-60000)) { - // We have launched this activity too many times since it was - // able to run, so give up and remove it. - remove = true; - } else { - // The process may be gone, but the activity lives on! - remove = false; - } - if (remove) { - if (ActivityStack.DEBUG_ADD_REMOVE || DEBUG_CLEANUP) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Removing activity " + r + " from stack at " + i - + ": haveState=" + r.haveState - + " stateNotNeeded=" + r.stateNotNeeded - + " finishing=" + r.finishing - + " state=" + r.state, here); - } - if (!r.finishing) { - Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); - EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, - r.userId, System.identityHashCode(r), - r.task.taskId, r.shortComponentName, - "proc died without state saved"); + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + --i; + if (DEBUG_CLEANUP) Slog.v( + TAG, "Record #" + i + " " + r + ": app=" + r.app); + if (r.app == app) { + boolean remove; + if ((!r.haveState && !r.stateNotNeeded) || r.finishing) { + // Don't currently have state for the activity, or + // it is finishing -- always remove it. + remove = true; + } else if (r.launchCount > 2 && + r.lastLaunchTime > (SystemClock.uptimeMillis()-60000)) { + // We have launched this activity too many times since it was + // able to run, so give up and remove it. + remove = true; + } else { + // The process may be gone, but the activity lives on! + remove = false; } - removeActivityFromHistoryLocked(r); + if (remove) { + if (ActivityStack.DEBUG_ADD_REMOVE || DEBUG_CLEANUP) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.i(TAG, "Removing activity " + r + " from stack at " + i + + ": haveState=" + r.haveState + + " stateNotNeeded=" + r.stateNotNeeded + + " finishing=" + r.finishing + + " state=" + r.state, here); + } + if (!r.finishing) { + Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); + EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, + r.userId, System.identityHashCode(r), + r.task.taskId, r.shortComponentName, + "proc died without state saved"); + } + removeActivityFromHistoryLocked(r); - } else { - // We have the current state for this activity, so - // it can be restarted later when needed. - if (localLOGV) Slog.v( - TAG, "Keeping entry, setting app to null"); - if (r.visible) { - hasVisibleActivities = true; - } - if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity " - + r); - r.app = null; - r.nowVisible = false; - if (!r.haveState) { - if (ActivityStack.DEBUG_SAVED_STATE) Slog.i(TAG, - "App died, clearing saved state of " + r); - r.icicle = null; + } else { + // We have the current state for this activity, so + // it can be restarted later when needed. + if (localLOGV) Slog.v( + TAG, "Keeping entry, setting app to null"); + if (r.visible) { + hasVisibleActivities = true; + } + if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity " + + r); + r.app = null; + r.nowVisible = false; + if (!r.haveState) { + if (ActivityStack.DEBUG_SAVED_STATE) Slog.i(TAG, + "App died, clearing saved state of " + r); + r.icicle = null; + } } - } - r.stack.cleanUpActivityLocked(r, true, true); + cleanUpActivityLocked(r, true, true); + } } } return hasVisibleActivities; } - + /** * Move the current home activity's task (if one exists) to the front * of the stack. */ final void moveHomeToFrontLocked() { - TaskRecord homeTask = null; - for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord hr = mHistory.get(i); - if (hr.isHomeActivity) { - homeTask = hr.task; - break; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final TaskRecord task = mTaskHistory.get(taskNdx); + final ArrayList<ActivityRecord> activities = task.mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.isHomeActivity) { + moveTaskToFrontLocked(task, null, null); + return; + } } } - if (homeTask != null) { - moveTaskToFrontLocked(homeTask, null, null); - } } final void updateTransitLocked(int transit, Bundle options) { @@ -4312,13 +4357,29 @@ final class ActivityStack { mService.mWindowManager.prepareAppTransition(transit, false); } + final boolean findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) { + final TaskRecord task = taskForIdLocked(taskId); + if (task != null) { + if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { + mUserLeaving = true; + } + if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) { + // Caller wants the home activity moved with it. To accomplish this, + // we'll just move the home task to the top first. + moveHomeToFrontLocked(); + } + moveTaskToFrontLocked(task, null, options); + return true; + } + return false; + } + final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) { if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr); - final int task = tr.taskId; - int top = mHistory.size()-1; - - if (top < 0 || (mHistory.get(top)).task.taskId == task) { + final int numTasks = mTaskHistory.size(); + final int index = mTaskHistory.indexOf(tr); + if (numTasks == 0 || index < 0 || index == numTasks - 1) { // nothing to do! if (reason != null && (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { @@ -4329,40 +4390,15 @@ final class ActivityStack { return; } - ArrayList<IBinder> moved = new ArrayList<IBinder>(); - - // Applying the affinities may have removed entries from the history, - // so get the size again. - top = mHistory.size()-1; - int pos = top; - // Shift all activities with this task up to the top // of the stack, keeping them in the same internal order. - while (pos >= 0) { - ActivityRecord r = mHistory.get(pos); - if (localLOGV) Slog.v( - TAG, "At " + pos + " ckp " + r.task + ": " + r); - if (r.task.taskId == task) { - if (localLOGV) Slog.v(TAG, "Removing and adding at " + top); - if (DEBUG_ADD_REMOVE) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Removing and adding activity " + r + " to stack at " + top, here); - } - mHistory.remove(pos); - mHistory.add(top, r); - moved.add(0, r.appToken); - top--; - } - pos--; - } + mTaskHistory.remove(tr); + mTaskHistory.add(tr); - if (DEBUG_TRANSITION) Slog.v(TAG, - "Prepare to front transition: task=" + tr); + if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr); if (reason != null && (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - mService.mWindowManager.prepareAppTransition( - AppTransition.TRANSIT_NONE, false); + mService.mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); ActivityRecord r = topRunningActivityLocked(null); if (r != null) { mNoAnimActivities.add(r); @@ -4371,18 +4407,15 @@ final class ActivityStack { } else { updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options); } - - mService.mWindowManager.moveAppTokensToTop(moved); - if (VALIDATE_TOKENS) { - validateAppTokensLocked(); - } - finishTaskMoveLocked(task); - EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, task); - } + mService.mWindowManager.moveTaskToTop(tr.taskId); - private final void finishTaskMoveLocked(int task) { resumeTopActivityLocked(null); + EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId); + + if (VALIDATE_TOKENS) { + validateAppTokensLocked(); + } } /** @@ -4398,7 +4431,7 @@ final class ActivityStack { */ final boolean moveTaskToBackLocked(int task, ActivityRecord reason) { Slog.i(TAG, "moveTaskToBack: " + task); - + // If we have a watcher, preflight the move before committing to it. First check // for *other* available tasks, but if none are available, then try again allowing the // current task to be selected. @@ -4421,41 +4454,19 @@ final class ActivityStack { } } - ArrayList<IBinder> moved = new ArrayList<IBinder>(); - if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to back transition: task=" + task); - - final int N = mHistory.size(); - int bottom = 0; - int pos = 0; - // Shift all activities with this task down to the bottom - // of the stack, keeping them in the same internal order. - while (pos < N) { - ActivityRecord r = mHistory.get(pos); - if (localLOGV) Slog.v( - TAG, "At " + pos + " ckp " + r.task + ": " + r); - if (r.task.taskId == task) { - if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1)); - if (DEBUG_ADD_REMOVE) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.i(TAG, "Removing and adding activity " + r + " to stack at " - + bottom, here); - } - mHistory.remove(pos); - mHistory.add(bottom, r); - moved.add(r.appToken); - bottom++; - } - pos++; + final TaskRecord tr = taskForIdLocked(task); + if (tr == null) { + return false; } + mTaskHistory.remove(tr); + mTaskHistory.add(0, tr); if (reason != null && (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - mService.mWindowManager.prepareAppTransition( - AppTransition.TRANSIT_NONE, false); + mService.mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false); ActivityRecord r = topRunningActivityLocked(null); if (r != null) { mNoAnimActivities.add(r); @@ -4464,20 +4475,21 @@ final class ActivityStack { mService.mWindowManager.prepareAppTransition( AppTransition.TRANSIT_TASK_TO_BACK, false); } - mService.mWindowManager.moveAppTokensToBottom(moved); + mService.mWindowManager.moveTaskToBottom(task); + if (VALIDATE_TOKENS) { validateAppTokensLocked(); } - finishTaskMoveLocked(task); + resumeTopActivityLocked(null); return true; } public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) { - TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true); + TaskAccessInfo info = getTaskAccessInfoLocked(tr, true); ActivityRecord resumed = mResumedActivity; if (resumed != null && resumed.thumbHolder == tr) { - info.mainThumbnail = resumed.stack.screenshotActivities(resumed); + info.mainThumbnail = screenshotActivities(resumed); } if (info.mainThumbnail == null) { info.mainThumbnail = tr.lastThumbnail; @@ -4490,21 +4502,24 @@ final class ActivityStack { if (resumed != null && resumed.task == tr) { // This task is the current resumed task, we just need to take // a screenshot of it and return that. - return resumed.stack.screenshotActivities(resumed); + return screenshotActivities(resumed); } // Return the information about the task, to figure out the top // thumbnail to return. - TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true); + TaskAccessInfo info = getTaskAccessInfoLocked(tr, true); if (info.numSubThumbbails <= 0) { return info.mainThumbnail != null ? info.mainThumbnail : tr.lastThumbnail; - } else { - return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail; } + return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail; } public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex, boolean taskRequired) { - TaskAccessInfo info = getTaskAccessInfoLocked(taskId, false); + final TaskRecord task = taskForIdLocked(taskId); + if (task == null) { + return null; + } + TaskAccessInfo info = getTaskAccessInfoLocked(task, false); if (info.root == null) { if (taskRequired) { Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId); @@ -4514,7 +4529,7 @@ final class ActivityStack { if (subTaskIndex < 0) { // Just remove the entire task. - performClearTaskAtIndexLocked(taskId, info.rootIndex); + performClearTaskAtIndexLocked(task, info.rootIndex); return info.root; } @@ -4527,19 +4542,20 @@ final class ActivityStack { // Remove all of this task's activities starting at the sub task. TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex); - performClearTaskAtIndexLocked(taskId, subtask.index); + performClearTaskAtIndexLocked(task, subtask.index); return subtask.activity; } - public TaskAccessInfo getTaskAccessInfoLocked(int taskId, boolean inclThumbs) { + public TaskAccessInfo getTaskAccessInfoLocked(TaskRecord task, boolean inclThumbs) { final TaskAccessInfo thumbs = new TaskAccessInfo(); // How many different sub-thumbnails? - final int NA = mHistory.size(); + final ArrayList<ActivityRecord> activities = task.mActivities; + final int NA = activities.size(); int j = 0; ThumbnailHolder holder = null; while (j < NA) { - ActivityRecord ar = mHistory.get(j); - if (!ar.finishing && ar.task.taskId == taskId) { + ActivityRecord ar = activities.get(j); + if (!ar.finishing) { thumbs.root = ar; thumbs.rootIndex = j; holder = ar.thumbHolder; @@ -4559,14 +4575,11 @@ final class ActivityStack { ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>(); thumbs.subtasks = subtasks; while (j < NA) { - ActivityRecord ar = mHistory.get(j); + ActivityRecord ar = activities.get(j); j++; if (ar.finishing) { continue; } - if (ar.task.taskId != taskId) { - break; - } if (ar.thumbHolder != holder && holder != null) { thumbs.numSubThumbbails++; holder = ar.thumbHolder; @@ -4579,6 +4592,7 @@ final class ActivityStack { } if (thumbs.numSubThumbbails > 0) { thumbs.retriever = new IThumbnailRetriever.Stub() { + @Override public Bitmap getThumbnail(int index) { if (index < 0 || index >= thumbs.subtasks.size()) { return null; @@ -4586,7 +4600,7 @@ final class ActivityStack { TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index); ActivityRecord resumed = mResumedActivity; if (resumed != null && resumed.thumbHolder == sub.holder) { - return resumed.stack.screenshotActivities(resumed); + return screenshotActivities(resumed); } return sub.holder.lastThumbnail; } @@ -4777,4 +4791,273 @@ final class ActivityStack { public void dismissKeyguardOnNextActivityLocked() { mDismissKeyguardOnNextActivity = true; } + + boolean willActivityBeVisibleLocked(IBinder token) { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.appToken == token) { + return true; + } + if (r.fullscreen && !r.finishing) { + return false; + } + } + } + return true; + } + + void closeSystemDialogsLocked() { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) { + finishActivityLocked(r, Activity.RESULT_CANCELED, null, "close-sys", true); + } + } + } + } + + boolean forceStopPackageLocked(String name, boolean doit, boolean evenPersistent, int userId) { + boolean didSomething = false; + TaskRecord lastTask = null; + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + int numActivities = activities.size(); + for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { + ActivityRecord r = activities.get(activityNdx); + final boolean samePackage = r.packageName.equals(name) + || (name == null && r.userId == userId); + if ((userId == UserHandle.USER_ALL || r.userId == userId) + && (samePackage || r.task == lastTask) + && (r.app == null || evenPersistent || !r.app.persistent)) { + if (!doit) { + if (r.finishing) { + // If this activity is just finishing, then it is not + // interesting as far as something to stop. + continue; + } + return true; + } + didSomething = true; + Slog.i(TAG, " Force finishing activity " + r); + if (samePackage) { + if (r.app != null) { + r.app.removed = true; + } + r.app = null; + } + lastTask = r.task; + finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop", true); + } + } + } + return didSomething; + } + + ActivityRecord getTasksLocked(int maxNum, IThumbnailReceiver receiver, + PendingThumbnailsRecord pending, List<RunningTaskInfo> list) { + ActivityRecord topRecord = null; + for (int taskNdx = mTaskHistory.size() - 1; maxNum > 0 && taskNdx >= 0; + --maxNum, --taskNdx) { + final TaskRecord task = mTaskHistory.get(taskNdx); + ActivityRecord r = null; + ActivityRecord top = null; + int numActivities = 0; + int numRunning = 0; + final ArrayList<ActivityRecord> activities = task.mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + r = activities.get(activityNdx); + + // Initialize state for next task if needed. + if (top == null || (top.state == ActivityState.INITIALIZING)) { + top = r; + numActivities = numRunning = 0; + } + + // Add 'r' into the current task. + numActivities++; + if (r.app != null && r.app.thread != null) { + numRunning++; + } + + if (localLOGV) Slog.v( + TAG, r.intent.getComponent().flattenToShortString() + + ": task=" + r.task); + } + + RunningTaskInfo ci = new RunningTaskInfo(); + ci.id = task.taskId; + ci.baseActivity = r.intent.getComponent(); + ci.topActivity = top.intent.getComponent(); + if (top.thumbHolder != null) { + ci.description = top.thumbHolder.lastDescription; + } + ci.numActivities = numActivities; + ci.numRunning = numRunning; + //System.out.println( + // "#" + maxNum + ": " + " descr=" + ci.description); + if (receiver != null) { + if (localLOGV) Slog.v( + TAG, "State=" + top.state + "Idle=" + top.idle + + " app=" + top.app + + " thr=" + (top.app != null ? top.app.thread : null)); + if (top.state == ActivityState.RESUMED || top.state == ActivityState.PAUSING) { + if (top.idle && top.app != null && top.app.thread != null) { + topRecord = top; + } else { + top.thumbnailNeeded = true; + } + } + pending.pendingRecords.add(top); + } + list.add(ci); + } + return topRecord; + } + + public void unhandledBackLocked() { + final int top = mTaskHistory.size() - 1; + if (DEBUG_SWITCH) Slog.d( + TAG, "Performing unhandledBack(): top activity at " + top); + if (top >= 0) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities; + int activityTop = activities.size() - 1; + if (activityTop > 0) { + finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null, + "unhandled-back", true); + } + } + } + + void handleAppCrashLocked(ProcessRecord app) { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.app == app) { + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + finishActivityLocked(r, Activity.RESULT_CANCELED, null, "crashed", false); + } + } + } + } + + void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll, + boolean dumpClient, String dumpPackage) { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final TaskRecord task = mTaskHistory.get(taskNdx); + pw.print(" Task "); pw.print(taskNdx); pw.print(": id #"); pw.println(task.taskId); + ActivityManagerService.dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities, + " ", "Hist", true, !dumpAll, dumpClient, dumpPackage); + } + } + + ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) { + ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>(); + + if ("all".equals(name)) { + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + activities.addAll(mTaskHistory.get(taskNdx).mActivities); + } + } else if ("top".equals(name)) { + final int top = mTaskHistory.size() - 1; + if (top >= 0) { + final ArrayList<ActivityRecord> list = mTaskHistory.get(top).mActivities; + int listTop = list.size() - 1; + if (listTop >= 0) { + activities.add(list.get(listTop)); + } + } + } else { + ItemMatcher matcher = new ItemMatcher(); + matcher.build(name); + + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + for (ActivityRecord r1 : mTaskHistory.get(taskNdx).mActivities) { + if (matcher.match(r1, r1.intent.getComponent())) { + activities.add(r1); + } + } + } + } + + return activities; + } + + ActivityRecord restartPackage(String packageName) { + ActivityRecord starting = topRunningActivityLocked(null); + + // All activities that came from the package must be + // restarted as if there was a config change. + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord a = activities.get(activityNdx); + if (a.info.packageName.equals(packageName)) { + a.forceNewConfig = true; + if (starting != null && a == starting && a.visible) { + a.startFreezingScreenLocked(starting.app, + ActivityInfo.CONFIG_SCREEN_LAYOUT); + } + } + } + } + + return starting; + } + + private void removeActivity(ActivityRecord r) { + final TaskRecord task = r.task; + // TODO: use ActivityManagerService.removeTask to do this. + if (task.removeActivity(r)) { + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "removeActivity: Removing from history, task=" + + task); + mTaskHistory.remove(task); + } + } + + private void setTask(ActivityRecord r, TaskRecord newTask, ThumbnailHolder newThumbHolder, + boolean isRoot) { + if (r.task != null) { + removeActivity(r); + } + r.setTask(newTask, newThumbHolder, isRoot); + } + + private TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, + boolean toTop) { + TaskRecord task = new TaskRecord(taskId, info, intent, this); + if (toTop) { + mTaskHistory.add(task); + } else { + mTaskHistory.add(0, task); + } + return task; + } + + ArrayList<TaskRecord> getAllTasks() { + return new ArrayList<TaskRecord>(mTaskHistory); + } + + void moveTask(int taskId, boolean toTop) { + final TaskRecord task = mService.anyTaskForIdLocked(taskId); + if (task == null) { + return; + } + task.stack.mTaskHistory.remove(task); + task.stack = this; + if (toTop) { + mTaskHistory.add(task); + } else { + mTaskHistory.add(0, task); + } + } + + public int getStackId() { + return mStackId; + } } diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java index 3a6492e..c56fdff 100644 --- a/services/java/com/android/server/am/CompatModePackages.java +++ b/services/java/com/android/server/am/CompatModePackages.java @@ -166,7 +166,7 @@ public class CompatModePackages { } public boolean getFrontActivityAskCompatModeLocked() { - ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); + ActivityRecord r = mService.mFocusedStack.topRunningActivityLocked(null); if (r == null) { return false; } @@ -178,7 +178,7 @@ public class CompatModePackages { } public void setFrontActivityAskCompatModeLocked(boolean ask) { - ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); + ActivityRecord r = mService.mFocusedStack.topRunningActivityLocked(null); if (r != null) { setPackageAskCompatModeLocked(r.packageName, ask); } @@ -200,7 +200,7 @@ public class CompatModePackages { } public int getFrontActivityScreenCompatModeLocked() { - ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); + ActivityRecord r = mService.mFocusedStack.topRunningActivityLocked(null); if (r == null) { return ActivityManager.COMPAT_MODE_UNKNOWN; } @@ -208,7 +208,7 @@ public class CompatModePackages { } public void setFrontActivityScreenCompatModeLocked(int mode) { - ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); + ActivityRecord r = mService.mFocusedStack.topRunningActivityLocked(null); if (r == null) { Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity"); return; @@ -295,20 +295,8 @@ public class CompatModePackages { Message msg = mHandler.obtainMessage(MSG_WRITE); mHandler.sendMessageDelayed(msg, 10000); - ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null); - - // All activities that came from the package must be - // restarted as if there was a config change. - for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) { - ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i); - if (a.info.packageName.equals(packageName)) { - a.forceNewConfig = true; - if (starting != null && a == starting && a.visible) { - a.startFreezingScreenLocked(starting.app, - ActivityInfo.CONFIG_SCREEN_LAYOUT); - } - } - } + + ActivityRecord starting = mService.mFocusedStack.restartPackage(packageName); // Tell all processes that loaded this package about the change. for (int i=mService.mLruProcesses.size()-1; i>=0; i--) { @@ -327,10 +315,10 @@ public class CompatModePackages { } if (starting != null) { - mService.mMainStack.ensureActivityConfigurationLocked(starting, 0); + mService.mFocusedStack.ensureActivityConfigurationLocked(starting, 0); // And we need to make sure at this point that all other activities // are made visible with the correct configuration. - mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0); + mService.mFocusedStack.ensureActivitiesVisibleLocked(starting, 0); } } } diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index 8ab71dd..28593fe 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -259,7 +259,7 @@ class PendingIntentRecord extends IIntentSender.Stub { } break; case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT: - key.activity.stack.sendActivityResultLocked(-1, key.activity, + key.activity.task.stack.sendActivityResultLocked(-1, key.activity, key.who, key.requestCode, code, finalIntent); break; case ActivityManager.INTENT_SENDER_BROADCAST: diff --git a/services/java/com/android/server/am/PendingThumbnailsRecord.java b/services/java/com/android/server/am/PendingThumbnailsRecord.java index ed478c9..c460791 100644 --- a/services/java/com/android/server/am/PendingThumbnailsRecord.java +++ b/services/java/com/android/server/am/PendingThumbnailsRecord.java @@ -27,13 +27,13 @@ import java.util.HashSet; class PendingThumbnailsRecord { final IThumbnailReceiver receiver; // who is waiting. - HashSet pendingRecords; // HistoryRecord objects we still wait for. + final HashSet<ActivityRecord> pendingRecords; // HistoryRecord objects we still wait for. boolean finished; // Is pendingRecords empty? PendingThumbnailsRecord(IThumbnailReceiver _receiver) { receiver = _receiver; - pendingRecords = new HashSet(); + pendingRecords = new HashSet<ActivityRecord>(); finished = false; } } diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index 1bae9ca..9833d31 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -23,6 +23,7 @@ import android.os.UserHandle; import android.util.Slog; import java.io.PrintWriter; +import java.util.ArrayList; class TaskRecord extends ThumbnailHolder { final int taskId; // Unique identifier for this task. @@ -39,11 +40,20 @@ class TaskRecord extends ThumbnailHolder { String stringName; // caching of toString() result. int userId; // user for which this task was created - - TaskRecord(int _taskId, ActivityInfo info, Intent _intent) { + + int numFullscreen; // Number of fullscreen activities. + + /** List of all activities in the task arranged in history order */ + final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>(); + + /** Current stack */ + ActivityStack stack; + + TaskRecord(int _taskId, ActivityInfo info, Intent _intent, ActivityStack _stack) { taskId = _taskId; affinity = info.taskAffinity; setIntent(_intent, info); + stack = _stack; } void touchActiveTime() { @@ -104,12 +114,53 @@ class TaskRecord extends ThumbnailHolder { userId = UserHandle.getUserId(info.applicationInfo.uid); } } - + + ActivityRecord getTopActivity() { + for (int i = mActivities.size() - 1; i >= 0; --i) { + final ActivityRecord r = mActivities.get(i); + if (r.finishing) { + continue; + } + return r; + } + return null; + } + + void addActivityAtBottom(ActivityRecord r) { + addActivityAtIndex(0, r); + } + + void addActivityToTop(ActivityRecord r) { + if (!mActivities.remove(r) && r.fullscreen) { + // Was not previously in list. + numFullscreen++; + } + mActivities.add(r); + } + + void addActivityAtIndex(int index, ActivityRecord r) { + if (!mActivities.remove(r) && r.fullscreen) { + // Was not previously in list. + numFullscreen++; + } + mActivities.add(index, r); + } + + /** @return true if this was the last activity in the task */ + boolean removeActivity(ActivityRecord r) { + if (mActivities.remove(r) && r.fullscreen) { + // Was previously in list. + numFullscreen--; + } + return mActivities.size() == 0; + } + void dump(PrintWriter pw, String prefix) { if (numActivities != 0 || rootWasReset || userId != 0) { pw.print(prefix); pw.print("numActivities="); pw.print(numActivities); pw.print(" rootWasReset="); pw.print(rootWasReset); - pw.print(" userId="); pw.println(userId); + pw.print(" userId="); pw.print(userId); + pw.print(" numFullscreen="); pw.println(numFullscreen); } if (affinity != null) { pw.print(prefix); pw.print("affinity="); pw.println(affinity); @@ -136,6 +187,7 @@ class TaskRecord extends ThumbnailHolder { pw.print(prefix); pw.print("realActivity="); pw.println(realActivity.flattenToShortString()); } + pw.print(prefix); pw.print("Activities="); pw.println(mActivities); if (!askedCompatMode) { pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); } @@ -146,6 +198,7 @@ class TaskRecord extends ThumbnailHolder { pw.print((getInactiveDuration()/1000)); pw.println("s)"); } + @Override public String toString() { if (stringName != null) { return stringName; @@ -156,19 +209,21 @@ class TaskRecord extends ThumbnailHolder { sb.append(" #"); sb.append(taskId); if (affinity != null) { - sb.append(" A "); + sb.append(" A="); sb.append(affinity); } else if (intent != null) { - sb.append(" I "); + sb.append(" I="); sb.append(intent.getComponent().flattenToShortString()); } else if (affinityIntent != null) { - sb.append(" aI "); + sb.append(" aI="); sb.append(affinityIntent.getComponent().flattenToShortString()); } else { sb.append(" ??"); } - sb.append(" U "); + sb.append(" U="); sb.append(userId); + sb.append(" sz="); + sb.append(mActivities.size()); sb.append('}'); return stringName = sb.toString(); } diff --git a/services/java/com/android/server/pm/KeySetManager.java b/services/java/com/android/server/pm/KeySetManager.java new file mode 100644 index 0000000..afb7d4b --- /dev/null +++ b/services/java/com/android/server/pm/KeySetManager.java @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2013 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.pm; + +import android.content.pm.KeySet; +import android.content.pm.PackageParser; +import android.os.Binder; +import android.util.Base64; +import android.util.Log; +import android.util.LongSparseArray; + +import java.io.IOException; +import java.io.PrintWriter; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +/* + * Manages system-wide KeySet state. + */ +public class KeySetManager { + + static final String TAG = "KeySetManager"; + + private static final long KEYSET_NOT_FOUND = -1; + private static final long PUBLIC_KEY_NOT_FOUND = -1; + + private final Object mLockObject = new Object(); + + private final LongSparseArray<KeySet> mKeySets; + + private final LongSparseArray<PublicKey> mPublicKeys; + + private final LongSparseArray<Set<Long>> mKeySetMapping; + + private final Map<String, PackageSetting> mPackages; + + private static long lastIssuedKeySetId = 0; + + private static long lastIssuedKeyId = 0; + + public KeySetManager(Map<String, PackageSetting> packages) { + mKeySets = new LongSparseArray<KeySet>(); + mPublicKeys = new LongSparseArray<PublicKey>(); + mKeySetMapping = new LongSparseArray<Set<Long>>(); + mPackages = packages; + } + + /* + * Determine if a package is signed by the given KeySet. + * + * Returns false if the package was not signed by all the + * keys in the KeySet. + * + * Returns true if the package was signed by at least the + * keys in the given KeySet. + * + * Note that this can return true for multiple KeySets. + */ + public boolean packageIsSignedBy(String packageName, KeySet ks) { + synchronized (mLockObject) { + PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new NullPointerException("Invalid package name"); + } + if (pkg.keySetData == null) { + throw new NullPointerException("Package has no KeySet data"); + } + long id = getIdByKeySetLocked(ks); + return pkg.keySetData.packageIsSignedBy(id); + } + } + + /* + * This informs the system that the given package has defined a KeySet + * in its manifest that a) contains the given keys and b) is named + * alias by that package. + */ + public void addDefinedKeySetToPackage(String packageName, + Set<PublicKey> keys, String alias) { + if ((packageName == null) || (keys == null) || (alias == null)) { + Log.e(TAG, "Got null argument for a defined keyset, ignoring!"); + return; + } + synchronized (mLockObject) { + KeySet ks = addKeySetLocked(keys); + PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new NullPointerException("Unknown package"); + } + long id = getIdByKeySetLocked(ks); + pkg.keySetData.addDefinedKeySet(id, alias); + } + } + + /* + * Similar to the above, this informs the system that the given package + * was signed by the provided KeySet. + */ + public void addSigningKeySetToPackage(String packageName, + Set<PublicKey> signingKeys) { + if ((packageName == null) || (signingKeys == null)) { + Log.e(TAG, "Got null argument for a signing keyset, ignoring!"); + return; + } + synchronized (mLockObject) { + // add the signing KeySet + KeySet ks = addKeySetLocked(signingKeys); + long id = getIdByKeySetLocked(ks); + Set<Long> publicKeyIds = mKeySetMapping.get(id); + if (publicKeyIds == null) { + throw new NullPointerException("Got invalid KeySet id"); + } + + // attach it to the package + PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new NullPointerException("No such package!"); + } + pkg.keySetData.addSigningKeySet(id); + + // for each KeySet the package defines which is a subset of + // the one above, add the KeySet id to the package's signing KeySets + for (Long keySetID : pkg.keySetData.getDefinedKeySets()) { + Set<Long> definedKeys = mKeySetMapping.get(keySetID); + if (publicKeyIds.contains(definedKeys)) { + pkg.keySetData.addSigningKeySet(keySetID); + } + } + } + } + + /* + * Fetches the stable identifier associated with the given KeySet. + * + * Returns KEYSET_NOT_FOUND if the KeySet... wasn't found. + */ + public long getIdByKeySet(KeySet ks) { + synchronized (mLockObject) { + return getIdByKeySetLocked(ks); + } + } + + private long getIdByKeySetLocked(KeySet ks) { + for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) { + KeySet value = mKeySets.valueAt(keySetIndex); + if (ks.equals(value)) { + return mKeySets.keyAt(keySetIndex); + } + } + return KEYSET_NOT_FOUND; + } + + /* + * Fetches the KeySet corresponding to the given stable identifier. + * + * Returns KEYSET_NOT_FOUND if the identifier doesn't identify a KeySet. + */ + public KeySet getKeySetById(long id) { + synchronized (mLockObject) { + return mKeySets.get(id); + } + } + + /* + * Fetches the KeySet that a given package refers to by the provided alias. + * + * If the package isn't known to us, throws an IllegalArgumentException. + * Returns null if the alias isn't known to us. + */ + public KeySet getKeySetByAliasAndPackageName(String packageName, String alias) { + synchronized (mLockObject) { + PackageSetting p = mPackages.get(packageName); + if (p == null) { + throw new NullPointerException("Unknown package"); + } + if (p.keySetData == null) { + throw new IllegalArgumentException("Package has no keySet data"); + } + long keySetId = p.keySetData.getAliases().get(alias); + return mKeySets.get(keySetId); + } + } + + /* + * Fetches all the known KeySets that signed the given package. + * + * If the package is unknown to us, throws an IllegalArgumentException. + */ + public Set<KeySet> getSigningKeySetsByPackageName(String packageName) { + synchronized (mLockObject) { + Set<KeySet> signingKeySets = new HashSet<KeySet>(); + PackageSetting p = mPackages.get(packageName); + if (p == null) { + throw new NullPointerException("Unknown package"); + } + if (p.keySetData == null) { + throw new IllegalArgumentException("Package has no keySet data"); + } + for (long l : p.keySetData.getSigningKeySets()) { + signingKeySets.add(mKeySets.get(l)); + } + return signingKeySets; + } + } + + /* + * Creates a new KeySet corresponding to the given keys. + * + * If the PublicKeys aren't known to the system, this adds them. Otherwise, + * they're deduped. + * + * If the KeySet isn't known to the system, this adds that and creates the + * mapping to the PublicKeys. If it is known, then it's deduped. + * + * Throws if the provided set is null. + */ + private KeySet addKeySetLocked(Set<PublicKey> keys) { + if (keys == null) { + throw new NullPointerException("Provided keys cannot be null"); + } + // add each of the keys in the provided set + Set<Long> addedKeyIds = new HashSet<Long>(keys.size()); + for (PublicKey k : keys) { + long id = addPublicKeyLocked(k); + addedKeyIds.add(id); + } + + // check to see if the resulting keyset is new + long existingKeySetId = getIdFromKeyIdsLocked(addedKeyIds); + if (existingKeySetId != KEYSET_NOT_FOUND) { + return mKeySets.get(existingKeySetId); + } + + // create the KeySet object + KeySet ks = new KeySet(new Binder()); + // get the first unoccupied slot in mKeySets + long id = getFreeKeySetIDLocked(); + // add the KeySet object to it + mKeySets.put(id, ks); + // add the stable key ids to the mapping + mKeySetMapping.put(id, addedKeyIds); + // go home + return ks; + } + + /* + * Adds the given PublicKey to the system, deduping as it goes. + */ + private long addPublicKeyLocked(PublicKey key) { + // check if the public key is new + long existingKeyId = getIdForPublicKeyLocked(key); + if (existingKeyId != PUBLIC_KEY_NOT_FOUND) { + return existingKeyId; + } + // if it's new find the first unoccupied slot in the public keys + long id = getFreePublicKeyIdLocked(); + // add the public key to it + mPublicKeys.put(id, key); + // return the stable identifier + return id; + } + + /* + * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs. + * + * Returns KEYSET_NOT_FOUND if there isn't one. + */ + private long getIdFromKeyIdsLocked(Set<Long> publicKeyIds) { + for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) { + Set<Long> value = mKeySetMapping.valueAt(keyMapIndex); + if (value.equals(publicKeyIds)) { + return mKeySetMapping.keyAt(keyMapIndex); + } + } + return KEYSET_NOT_FOUND; + } + + /* + * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND. + */ + private long getIdForPublicKeyLocked(PublicKey k) { + String encodedPublicKey = new String(k.getEncoded()); + for (int publicKeyIndex = 0; publicKeyIndex < mPublicKeys.size(); publicKeyIndex++) { + PublicKey value = mPublicKeys.valueAt(publicKeyIndex); + String encodedExistingKey = new String(value.getEncoded()); + if (encodedPublicKey.equals(encodedExistingKey)) { + return mPublicKeys.keyAt(publicKeyIndex); + } + } + return PUBLIC_KEY_NOT_FOUND; + } + + /* + * Gets an unused stable identifier for a KeySet. + */ + private long getFreeKeySetIDLocked() { + lastIssuedKeySetId += 1; + return lastIssuedKeySetId; + } + + /* + * Same as above, but for public keys. + */ + private long getFreePublicKeyIdLocked() { + lastIssuedKeyId += 1; + return lastIssuedKeyId; + } + + public void removeAppKeySetData(String packageName) { + Log.e(TAG, "Removing application " + packageName); + synchronized (mLockObject) { + // Get the package's known keys and KeySets + Set<Long> deletableKeySets = getKnownKeySetsByPackageName(packageName); + Set<Long> deletableKeys = new HashSet<Long>(); + for (Long ks : deletableKeySets) { + deletableKeys.addAll(mKeySetMapping.get(ks)); + } + + // Now remove the keys and KeySets known to any other package + for (String pkgName : mPackages.keySet()) { + if (pkgName.equals(packageName)) { + continue; + } + Set<Long> knownKeySets = getKnownKeySetsByPackageName(pkgName); + deletableKeySets.removeAll(knownKeySets); + Set<Long> knownKeys = new HashSet<Long>(); + for (Long ks : knownKeySets) { + deletableKeys.removeAll(mKeySetMapping.get(ks)); + } + } + + // The remaining keys and KeySets are not known to any other + // application and so can be safely deleted. + for (Long ks : deletableKeySets) { + mKeySets.delete(ks); + mKeySetMapping.delete(ks); + } + for (Long keyId : deletableKeys) { + mPublicKeys.delete(keyId); + } + } + } + + private Set<Long> getKnownKeySetsByPackageName(String packageName) { + PackageSetting p = mPackages.get(packageName); + if (p == null) { + throw new NullPointerException("Unknown package"); + } + if (p.keySetData == null) { + throw new IllegalArgumentException("Package has no keySet data"); + } + Set<Long> knownKeySets = new HashSet<Long>(); + for (Long ks : p.keySetData.getSigningKeySets()) { + knownKeySets.add(ks); + } + for (Long ks : p.keySetData.getDefinedKeySets()) { + knownKeySets.add(ks); + } + return knownKeySets; + } + + public String encodePublicKey(PublicKey k) throws IOException { + return new String(Base64.encode(k.getEncoded(), 0)); + } + + public void dump(PrintWriter pw) { + synchronized (mLockObject) { + pw.println(" Dumping KeySetManager"); + for (Map.Entry<String, PackageSetting> e : mPackages.entrySet()) { + String packageName = e.getKey(); + PackageSetting pkg = e.getValue(); + pw.print(" ["); pw.print(packageName); pw.println("]"); + if (pkg.keySetData != null) { + pw.print(" Defined KeySets:"); + for (long keySetId : pkg.keySetData.getDefinedKeySets()) { + pw.print(" "); pw.print(Long.toString(keySetId)); + } + pw.println(""); + pw.print(" Signing KeySets:"); + for (long keySetId : pkg.keySetData.getSigningKeySets()) { + pw.print(" "); pw.print(Long.toString(keySetId)); + } + pw.println(""); + } + } + } + } + + void writeKeySetManagerLPr(XmlSerializer serializer) throws IOException { + serializer.startTag(null, "keyset-settings"); + writePublicKeysLPr(serializer); + writeKeySetsLPr(serializer); + serializer.startTag(null, "lastIssuedKeyId"); + serializer.attribute(null, "value", Long.toString(lastIssuedKeyId)); + serializer.endTag(null, "lastIssuedKeyId"); + serializer.startTag(null, "lastIssuedKeySetId"); + serializer.attribute(null, "value", Long.toString(lastIssuedKeySetId)); + serializer.endTag(null, "lastIssuedKeySetId"); + serializer.endTag(null, "keyset-settings"); + } + + void writePublicKeysLPr(XmlSerializer serializer) throws IOException { + serializer.startTag(null, "keys"); + for (int pKeyIndex = 0; pKeyIndex < mPublicKeys.size(); pKeyIndex++) { + long id = mPublicKeys.keyAt(pKeyIndex); + PublicKey key = mPublicKeys.valueAt(pKeyIndex); + String encodedKey = encodePublicKey(key); + serializer.startTag(null, "public-key"); + serializer.attribute(null, "identifier", Long.toString(id)); + serializer.attribute(null, "value", encodedKey); + serializer.endTag(null, "public-key"); + } + serializer.endTag(null, "keys"); + } + + void writeKeySetsLPr(XmlSerializer serializer) throws IOException { + serializer.startTag(null, "keysets"); + for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) { + long id = mKeySetMapping.keyAt(keySetIndex); + Set<Long> keys = mKeySetMapping.valueAt(keySetIndex); + serializer.startTag(null, "keyset"); + serializer.attribute(null, "identifier", Long.toString(id)); + for (long keyId : keys) { + serializer.startTag(null, "key-id"); + serializer.attribute(null, "identifier", Long.toString(keyId)); + serializer.endTag(null, "key-id"); + } + serializer.endTag(null, "keyset"); + } + serializer.endTag(null, "keysets"); + } + + void readKeySetsLPw(XmlPullParser parser) + throws XmlPullParserException, IOException { + int type; + long currentKeySetId = 0; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + final String tagName = parser.getName(); + if (tagName.equals("keys")) { + readKeysLPw(parser); + } else if (tagName.equals("keysets")) { + readKeySetListLPw(parser); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Could not read KeySets for KeySetManager!"); + } + } + } + + void readKeysLPw(XmlPullParser parser) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + final String tagName = parser.getName(); + if (tagName.equals("public-key")) { + readPublicKeyLPw(parser); + } else if (tagName.equals("lastIssuedKeyId")) { + lastIssuedKeyId = Long.parseLong(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("lastIssuedKeySetId")) { + lastIssuedKeySetId = Long.parseLong(parser.getAttributeValue(null, "value")); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Could not read keys for KeySetManager!"); + } + } + } + + void readKeySetListLPw(XmlPullParser parser) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + long currentKeySetId = 0; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + final String tagName = parser.getName(); + if (tagName.equals("keyset")) { + currentKeySetId = readIdentifierLPw(parser); + mKeySets.put(currentKeySetId, new KeySet(new Binder())); + mKeySetMapping.put(currentKeySetId, new HashSet<Long>()); + } else if (tagName.equals("key-id")) { + long id = readIdentifierLPw(parser); + mKeySetMapping.get(currentKeySetId).add(id); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Could not read KeySets for KeySetManager!"); + } + } + } + + long readIdentifierLPw(XmlPullParser parser) + throws XmlPullParserException { + return Long.parseLong(parser.getAttributeValue(null, "identifier")); + } + + void readPublicKeyLPw(XmlPullParser parser) + throws XmlPullParserException { + String encodedID = parser.getAttributeValue(null, "identifier"); + long identifier = Long.parseLong(encodedID); + String encodedPublicKey = parser.getAttributeValue(null, "value"); + PublicKey pub = PackageParser.parsePublicKey(encodedPublicKey); + if (pub == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Could not read public key for KeySetManager!"); + } else { + mPublicKeys.put(identifier, pub); + } + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/pm/PackageKeySetData.java b/services/java/com/android/server/pm/PackageKeySetData.java new file mode 100644 index 0000000..01ba5ba --- /dev/null +++ b/services/java/com/android/server/pm/PackageKeySetData.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2013 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.pm; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class PackageKeySetData { + + private long[] mSigningKeySets; + + private long[] mDefinedKeySets; + + private final Map<String, Long> mKeySetAliases; + + PackageKeySetData() { + mSigningKeySets = new long[0]; + mDefinedKeySets = new long[0]; + mKeySetAliases = new HashMap<String, Long>(); + } + + PackageKeySetData(PackageKeySetData original) { + mSigningKeySets = original.getSigningKeySets().clone(); + mDefinedKeySets = original.getDefinedKeySets().clone(); + mKeySetAliases = new HashMap<String, Long>(); + mKeySetAliases.putAll(original.getAliases()); + } + + public void addSigningKeySet(long ks) { + // deduplicate + for (long knownKeySet : mSigningKeySets) { + if (ks == knownKeySet) { + return; + } + } + int end = mSigningKeySets.length; + mSigningKeySets = Arrays.copyOf(mSigningKeySets, end + 1); + mSigningKeySets[end] = ks; + } + + public void addDefinedKeySet(long ks, String alias) { + // deduplicate + for (long knownKeySet : mDefinedKeySets) { + if (ks == knownKeySet) { + return; + } + } + int end = mDefinedKeySets.length; + mDefinedKeySets = Arrays.copyOf(mDefinedKeySets, end + 1); + mDefinedKeySets[end] = ks; + mKeySetAliases.put(alias, ks); + } + + public boolean packageIsSignedBy(long ks) { + for (long signingKeySet : mSigningKeySets) { + if (ks == signingKeySet) { + return true; + } + } + return false; + } + + public long[] getSigningKeySets() { + return mSigningKeySets; + } + + public long[] getDefinedKeySets() { + return mDefinedKeySets; + } + + public Map<String, Long> getAliases() { + return mKeySetAliases; + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 09d1426..6b63fb5 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -70,6 +70,7 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; @@ -113,10 +114,12 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.Environment.UserEnvironment; import android.security.SystemKeyStore; +import android.util.Base64; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.LogPrinter; +import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; @@ -1031,6 +1034,7 @@ public class PackageManagerService extends IPackageManager.Stub { mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false), mSdkVersion, mOnlyCore); + long startTime = SystemClock.uptimeMillis(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, @@ -3272,10 +3276,12 @@ public class PackageManagerService extends IPackageManager.Stub { pp.setOnlyCoreApps(mOnlyCore); final PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags); + if (pkg == null) { mLastScanError = pp.getParseError(); return null; } + PackageSetting ps = null; PackageSetting updatedPkg; // reader @@ -3422,6 +3428,7 @@ public class PackageManagerService extends IPackageManager.Stub { } else { resPath = pkg.mScanPath; } + codePath = pkg.mScanPath; // Set application objects path explicitly. setApplicationInfoPaths(pkg, codePath, resPath); @@ -4452,6 +4459,24 @@ public class PackageManagerService extends IPackageManager.Stub { } } + // Add the package's KeySets to the global KeySetManager + KeySetManager ksm = mSettings.mKeySetManager; + try { + ksm.addSigningKeySetToPackage(pkg.packageName, pkg.mSigningKeys); + if (pkg.mKeySetMapping != null) { + for (Map.Entry<String, Set<PublicKey>> entry : pkg.mKeySetMapping.entrySet()) { + if (entry.getValue() != null) { + ksm.addDefinedKeySetToPackage(pkg.packageName, + entry.getValue(), entry.getKey()); + } + } + } + } catch (NullPointerException e) { + Slog.e(TAG, "Could not add KeySet to " + pkg.packageName, e); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Could not add KeySet to malformed package" + pkg.packageName, e); + } + int N = pkg.providers.size(); StringBuilder r = null; int i; @@ -8751,7 +8776,9 @@ public class PackageManagerService extends IPackageManager.Stub { removePackageDataLI(ps, outInfo, flags, writeSettings); return true; } + boolean ret = false; + mSettings.mKeySetManager.removeAppKeySetData(packageName); if (isSystemApp(ps)) { if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package:" + ps.name); // When an updated system application is deleted we delete the existing resources as well and @@ -8764,6 +8791,7 @@ public class PackageManagerService extends IPackageManager.Stub { ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, outInfo, writeSettings); } + return ret; } @@ -9565,6 +9593,8 @@ public class PackageManagerService extends IPackageManager.Stub { public static final int DUMP_PREFERRED_XML = 1 << 10; + public static final int DUMP_KEYSETS = 1 << 11; + public static final int OPTION_SHOW_FILTERS = 1 << 0; private int mTypes; @@ -9662,6 +9692,7 @@ public class PackageManagerService extends IPackageManager.Stub { pw.println(" m[essages]: print collected runtime messages"); pw.println(" v[erifiers]: print package verifier info"); pw.println(" <package.name>: info about given package"); + pw.println(" k[eysets]: print known keysets"); return; } else if ("-f".equals(opt)) { dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS); @@ -9703,6 +9734,8 @@ public class PackageManagerService extends IPackageManager.Stub { dumpState.setDump(DumpState.DUMP_MESSAGES); } else if ("v".equals(cmd) || "verifiers".equals(cmd)) { dumpState.setDump(DumpState.DUMP_VERIFIERS); + } else if ("k".equals(cmd) || "keysets".equals(cmd)) { + dumpState.setDump(DumpState.DUMP_KEYSETS); } } @@ -9846,7 +9879,14 @@ public class PackageManagerService extends IPackageManager.Stub { } } } - + + if (dumpState.isDumping(DumpState.DUMP_KEYSETS)) { + if (dumpState.onTitlePrinted()) { + pw.println(" "); + } + mSettings.mKeySetManager.dump(pw); + } + if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) { mSettings.dumpPackagesLPr(pw, packageName, dumpState); } diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java index ae1b213..2a723b4 100644 --- a/services/java/com/android/server/pm/PackageSettingBase.java +++ b/services/java/com/android/server/pm/PackageSettingBase.java @@ -65,6 +65,8 @@ class PackageSettingBase extends GrantedPermissions { boolean permissionsFixed; boolean haveGids; + PackageKeySetData keySetData = new PackageKeySetData(); + private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState(); // Whether this package is currently stopped, thus can not be @@ -120,6 +122,9 @@ class PackageSettingBase extends GrantedPermissions { origPackage = base.origPackage; installerPackageName = base.installerPackageName; + + keySetData = new PackageKeySetData(base.keySetData); + } void init(File codePath, File resourcePath, String nativeLibraryPathString, @@ -170,6 +175,7 @@ class PackageSettingBase extends GrantedPermissions { userState.put(base.userState.keyAt(i), base.userState.valueAt(i)); } installStatus = base.installStatus; + keySetData = base.keySetData; } private PackageUserState modifyUserState(int userId) { diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index e645078..f1c5c07 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -43,6 +43,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; +import android.content.pm.KeySet; import android.content.pm.PackageCleanItem; import android.content.pm.PackageManager; import android.content.pm.PackageParser; @@ -57,6 +58,7 @@ import android.os.FileUtils; import android.os.Process; import android.os.UserHandle; import android.util.Log; +import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; @@ -67,6 +69,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.security.PublicKey; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -118,6 +121,8 @@ final class Settings { private final HashMap<String, PackageSetting> mDisabledSysPackages = new HashMap<String, PackageSetting>(); + private static int mFirstAvailableUid = 0; + // These are the last platform API version we were using for // the apps installed on internal and external storage. It is // used to grant newer permissions one time during a system upgrade. @@ -176,6 +181,9 @@ final class Settings { private final Context mContext; private final File mSystemDir; + + public final KeySetManager mKeySetManager = new KeySetManager(mPackages); + Settings(Context context) { this(context, Environment.getDataDirectory()); } @@ -728,6 +736,7 @@ final class Settings { } else { mOtherUserIds.remove(uid); } + setFirstAvailableUid(uid+1); } private void replaceUserIdLPw(int uid, Object obj) { @@ -1324,6 +1333,8 @@ final class Settings { } } + mKeySetManager.writeKeySetManagerLPr(serializer); + serializer.endTag(null, "packages"); serializer.endDocument(); @@ -1511,9 +1522,31 @@ final class Settings { serializer.endTag(null, "perms"); } + writeSigningKeySetsLPr(serializer, pkg.keySetData); + writeKeySetAliasesLPr(serializer, pkg.keySetData); + serializer.endTag(null, "package"); } + void writeSigningKeySetsLPr(XmlSerializer serializer, + PackageKeySetData data) throws IOException { + for (long id : data.getSigningKeySets()) { + serializer.startTag(null, "signing-keyset"); + serializer.attribute(null, "identifier", Long.toString(id)); + serializer.endTag(null, "signing-keyset"); + } + } + + void writeKeySetAliasesLPr(XmlSerializer serializer, + PackageKeySetData data) throws IOException { + for (Map.Entry<String, Long> e: data.getAliases().entrySet()) { + serializer.startTag(null, "defined-keyset"); + serializer.attribute(null, "alias", e.getKey()); + serializer.attribute(null, "identifier", Long.toString(e.getValue())); + serializer.endTag(null, "defined-keyset"); + } + } + void writePermissionLPr(XmlSerializer serializer, BasePermission bp) throws XmlPullParserException, java.io.IOException { if (bp.type != BasePermission.TYPE_BUILTIN && bp.sourcePackage != null) { @@ -1691,6 +1724,8 @@ final class Settings { } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) { final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT); mReadExternalStorageEnforced = "1".equals(enforcement); + } else if (tagName.equals("keyset-settings")) { + mKeySetManager.readKeySetsLPw(parser); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: " + parser.getName()); @@ -2286,12 +2321,22 @@ final class Settings { } else if (tagName.equals("perms")) { readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions); packageSetting.permissionsFixed = true; + } else if (tagName.equals("signing-keyset")) { + long id = Long.parseLong(parser.getAttributeValue(null, "identifier")); + packageSetting.keySetData.addSigningKeySet(id); + Slog.e(TAG, "Adding signing keyset " + Long.toString(id) + " to " + name); + } else if (tagName.equals("defined-keyset")) { + long id = Long.parseLong(parser.getAttributeValue(null, "identifier")); + String alias = parser.getAttributeValue(null, "alias"); + packageSetting.keySetData.addDefinedKeySet(id, alias); } else { PackageManagerService.reportSettingsProblem(Log.WARN, "Unknown element under <package>: " + parser.getName()); XmlUtils.skipCurrentTag(parser); } } + + } else { XmlUtils.skipCurrentTag(parser); } @@ -2470,11 +2515,18 @@ final class Settings { file.delete(); } + // This should be called (at least) whenever an application is removed + private void setFirstAvailableUid(int uid) { + if (uid > mFirstAvailableUid) { + mFirstAvailableUid = uid; + } + } + // Returns -1 if we could not find an available UserId to assign private int newUserIdLPw(Object obj) { // Let's be stupidly inefficient for now... final int N = mUserIds.size(); - for (int i = 0; i < N; i++) { + for (int i = mFirstAvailableUid; i < N; i++) { if (mUserIds.get(i) == null) { mUserIds.set(i, obj); return Process.FIRST_APPLICATION_UID + i; diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java index 297324b..bbfec97 100644 --- a/services/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/java/com/android/server/wm/AppWindowAnimator.java @@ -5,7 +5,6 @@ package com.android.server.wm; import android.graphics.Matrix; import android.util.Slog; import android.view.Display; -import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManagerPolicy; import android.view.animation.Animation; diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java index fbb5013..8cc1d02 100644 --- a/services/java/com/android/server/wm/AppWindowToken.java +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -30,6 +30,10 @@ import android.view.View; import android.view.WindowManager; import java.io.PrintWriter; +import java.util.ArrayList; + +class AppTokenList extends ArrayList<AppWindowToken> { +} /** * Version of WindowToken that is specifically for a particular application (or diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java index 59e4b0e..0cd3136 100644 --- a/services/java/com/android/server/wm/DisplayContent.java +++ b/services/java/com/android/server/wm/DisplayContent.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.graphics.Rect; import android.view.Display; import android.view.DisplayInfo; @@ -33,6 +34,7 @@ class DisplayContentList extends ArrayList<DisplayContent> { * WindowManagerService.mWindowMap. */ class DisplayContent { +// private final static String TAG = "DisplayContent"; /** Unique identifier of this stack. */ private final int mDisplayId; @@ -67,6 +69,25 @@ class DisplayContent { final boolean isDefaultDisplay; /** + * Window tokens that are in the process of exiting, but still + * on screen for animations. + */ + final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>(); + + /** + * Application tokens that are in the process of exiting, but still + * on screen for animations. + */ + final AppTokenList mExitingAppTokens = new AppTokenList(); + + ArrayList<StackBox> mStackBoxes = new ArrayList<StackBox>(); + + /** + * Sorted most recent at top, oldest at [0]. + */ + ArrayList<Task> mTmpTasks = new ArrayList<Task>(); + + /** * @param display May not be null. */ DisplayContent(Display display) { @@ -92,10 +113,78 @@ class DisplayContent { return mDisplayInfo; } + ArrayList<Task> getTasks() { + mTmpTasks.clear(); + int numBoxes = mStackBoxes.size(); + for (int boxNdx = 0; boxNdx < numBoxes; ++boxNdx) { + mTmpTasks.addAll(mStackBoxes.get(boxNdx).getTasks()); + } + return mTmpTasks; + } + public void updateDisplayInfo() { mDisplay.getDisplayInfo(mDisplayInfo); } + int numTokens() { + getTasks(); + int count = 0; + for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) { + count += mTmpTasks.get(taskNdx).mAppTokens.size(); + } + return count; + } + + TaskStack createStack(int stackId, int relativeStackId, int position, float weight) { + TaskStack newStack = null; + if (mStackBoxes.isEmpty()) { + StackBox newBox = new StackBox(this, new Rect(0, 0, mDisplayInfo.logicalWidth, + mDisplayInfo.logicalHeight)); + mStackBoxes.add(newBox); + newStack = new TaskStack(stackId, newBox); + newBox.mStack = newStack; + } else { + int stackBoxNdx; + for (stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) { + final StackBox box = mStackBoxes.get(stackBoxNdx); + if (position == StackBox.TASK_STACK_GOES_OVER + || position == StackBox.TASK_STACK_GOES_UNDER) { + // Position indicates a new box is added at top level only. + final TaskStack stack = box.mStack; + if (stack != null && stack.mStackId == relativeStackId) { + final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0; + StackBox newBox = new StackBox(this, box.mBounds); + newStack = new TaskStack(stackId, newBox); + newBox.mStack = newStack; + mStackBoxes.add(stackBoxNdx + offset, newBox); + break; + } + } else { + // Remaining position values indicate a box must be split. + newStack = box.split(stackId, relativeStackId, position, weight); + if (newStack != null) { + break; + } + } + } + if (stackBoxNdx >= 0) { + throw new IllegalArgumentException("createStack: stackId " + stackId + " not found."); + } + } + return newStack; + } + + boolean resizeStack(int stackId, float weight) { + int stackBoxNdx; + for (stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) { + final StackBox box = mStackBoxes.get(stackBoxNdx); + if (box.resize(stackId, weight)) { + return true; + } + } + return false; + } + public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId); final String subPrefix = " " + prefix; @@ -119,7 +208,44 @@ class DisplayContent { pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight); pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth); pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight); - pw.print(subPrefix); pw.print("layoutNeeded="); pw.print(layoutNeeded); + pw.print(subPrefix); pw.print("layoutNeeded="); pw.println(layoutNeeded); + int ndx = numTokens(); + if (ndx > 0) { + pw.println(); + pw.println(" Application tokens in Z order:"); + getTasks(); + for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = mTmpTasks.get(taskNdx).mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + pw.print(" App #"); pw.print(ndx--); + pw.print(' '); pw.print(wtoken); pw.println(":"); + wtoken.dump(pw, " "); + } + } + } + if (mExitingTokens.size() > 0) { + pw.println(); + pw.println(" Exiting tokens:"); + for (int i=mExitingTokens.size()-1; i>=0; i--) { + WindowToken token = mExitingTokens.get(i); + pw.print(" Exiting #"); pw.print(i); + pw.print(' '); pw.print(token); + pw.println(':'); + token.dump(pw, " "); + } + } + if (mExitingAppTokens.size() > 0) { + pw.println(); + pw.println(" Exiting application tokens:"); + for (int i=mExitingAppTokens.size()-1; i>=0; i--) { + WindowToken token = mExitingAppTokens.get(i); + pw.print(" Exiting App #"); pw.print(i); + pw.print(' '); pw.print(token); + pw.println(':'); + token.dump(pw, " "); + } + } pw.println(); } } diff --git a/services/java/com/android/server/wm/StackBox.java b/services/java/com/android/server/wm/StackBox.java new file mode 100644 index 0000000..bd007e5 --- /dev/null +++ b/services/java/com/android/server/wm/StackBox.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2013 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.wm; + +import android.graphics.Rect; + +import java.util.ArrayList; + +public class StackBox { + /** For use with {@link WindowManagerService#createStack} */ + public static final int TASK_STACK_GOES_BEFORE = 0; + public static final int TASK_STACK_GOES_AFTER = 1; + public static final int TASK_STACK_GOES_ABOVE = 2; + public static final int TASK_STACK_GOES_BELOW = 3; + public static final int TASK_STACK_GOES_OVER = 4; + public static final int TASK_STACK_GOES_UNDER = 5; + + final DisplayContent mDisplayContent; + StackBox mParent; + boolean mVertical; + StackBox mFirst; + StackBox mSecond; + float mWeight; + TaskStack mStack; + Rect mBounds; + boolean layoutNeeded; + ArrayList<Task> mTmpTasks = new ArrayList<Task>(); + + StackBox(DisplayContent displayContent, Rect bounds) { + mDisplayContent = displayContent; + mBounds = bounds; + } + + /** Propagate #layoutNeeded bottom up. */ + void makeDirty() { + layoutNeeded = true; + if (mParent != null) { + mParent.makeDirty(); + } + } + + /** Propagate #layoutNeeded top down. */ + void makeClean() { + layoutNeeded = false; + if (mFirst != null) { + mFirst.makeClean(); + mSecond.makeClean(); + } + } + + TaskStack split(int stackId, int relativeStackId, int position, float weight) { + if (mStack != null) { + if (mStack.mStackId == relativeStackId) { + // Found it! + TaskStack stack = new TaskStack(stackId, this); + TaskStack firstStack; + TaskStack secondStack; + int width, height, split; + switch (position) { + default: + case TASK_STACK_GOES_BEFORE: + case TASK_STACK_GOES_AFTER: + mVertical = false; + width = (int)(weight * mBounds.width()); + height = mBounds.height(); + if (position == TASK_STACK_GOES_BEFORE) { + firstStack = stack; + secondStack = mStack; + split = mBounds.left + width; + } else { + firstStack = mStack; + secondStack = stack; + split = mBounds.right - width; + } + break; + case TASK_STACK_GOES_ABOVE: + case TASK_STACK_GOES_BELOW: + mVertical = true; + width = mBounds.width(); + height = (int)(weight * mBounds.height()); + if (position == TASK_STACK_GOES_ABOVE) { + firstStack = stack; + secondStack = mStack; + split = mBounds.top + height; + } else { + firstStack = mStack; + secondStack = stack; + split = mBounds.bottom - height; + } + break; + } + mFirst = new StackBox(mDisplayContent, new Rect(mBounds.left, mBounds.top, + mVertical ? mBounds.right : split, mVertical ? split : mBounds.bottom)); + mFirst.mStack = firstStack; + mSecond = new StackBox(mDisplayContent, new Rect(mVertical ? mBounds.left : split, + mVertical ? split : mBounds.top, mBounds.right, mBounds.bottom)); + mSecond.mStack = secondStack; + mStack = null; + return stack; + } + return null; + } + + // Propagate the split to see if the target task stack is in either sub box. + TaskStack stack = mFirst.split(stackId, relativeStackId, position, weight); + if (stack != null) { + return stack; + } + return mSecond.split(stackId, relativeStackId, position, weight); + } + + TaskStack merge(int position) { + TaskStack stack = null; + if (mFirst != null) { + switch (position) { + default: + case TASK_STACK_GOES_BEFORE: + case TASK_STACK_GOES_ABOVE: + stack = mFirst.merge(position); + stack.merge(mSecond.merge(position)); + break; + case TASK_STACK_GOES_AFTER: + case TASK_STACK_GOES_BELOW: + stack = mSecond.merge(position); + stack.merge(mFirst.merge(position)); + break; + } + return stack; + } + return mStack; + } + + boolean merge(int stackId, int position, StackBox primary, StackBox secondary) { + TaskStack stack = primary.mStack; + if (stack != null && stack.mStackId == stackId) { + stack.merge(secondary.merge(position)); + mStack = stack; + mFirst = null; + mSecond = null; + return true; + } + return false; + } + + boolean merge(int stackId, int position) { + if (mFirst != null) { + if (merge(stackId, position, mFirst, mSecond) + || merge(stackId, position, mSecond, mFirst) + || mFirst.merge(stackId, position) + || mSecond.merge(stackId, position)) { + return true; + } + } + return false; + } + + ArrayList<Task> getTasks() { + mTmpTasks.clear(); + if (mStack != null) { + mTmpTasks.addAll(mStack.getTasks()); + } else { + mTmpTasks.addAll(mFirst.getTasks()); + mTmpTasks.addAll(mSecond.getTasks()); + } + return mTmpTasks; + } + + void absorb(StackBox box) { + mFirst = box.mFirst; + mSecond = box.mSecond; + mStack = box.mStack; + layoutNeeded = true; + } + + void removeStack() { + if (mParent != null) { + if (mParent.mFirst == this) { + mParent.absorb(mParent.mSecond); + } else { + mParent.absorb(mParent.mFirst); + } + mParent.makeDirty(); + } + } + + boolean addTaskToStack(Task task, int stackId, boolean toTop) { + if (mStack != null) { + if (mStack.mStackId == stackId) { + mStack.addTask(task, toTop); + return true; + } + return false; + } + return mFirst.addTaskToStack(task, stackId, toTop) + || mSecond.addTaskToStack(task, stackId, toTop); + } + + boolean resize(int stackId, float weight) { + return false; + } +} diff --git a/services/java/com/android/server/wm/Task.java b/services/java/com/android/server/wm/Task.java new file mode 100644 index 0000000..81245c6 --- /dev/null +++ b/services/java/com/android/server/wm/Task.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 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.wm; + +class Task { +// private final String TAG = "TaskGroup"; + TaskStack mStack; + final AppTokenList mAppTokens = new AppTokenList(); + final int taskId; + + Task(AppWindowToken wtoken, TaskStack stack) { + taskId = wtoken.groupId; + mAppTokens.add(wtoken); + mStack = stack; + } + + DisplayContent getDisplayContent() { + return mStack.getDisplayContent(); + } + + void addAppToken(int addPos, AppWindowToken wtoken) { + mAppTokens.add(addPos, wtoken); + } + + boolean removeAppToken(AppWindowToken wtoken) { + mAppTokens.remove(wtoken); + if (mAppTokens.size() == 0) { + mStack.removeTask(this); + return true; + } + return false; + } + + @Override + public String toString() { + return "id=" + taskId + " appTokens=" + mAppTokens; + } +} diff --git a/services/java/com/android/server/wm/TaskGroup.java b/services/java/com/android/server/wm/TaskGroup.java new file mode 100644 index 0000000..1f1dd58 --- /dev/null +++ b/services/java/com/android/server/wm/TaskGroup.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 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.wm; + +import android.view.IApplicationToken; + +import java.util.ArrayList; + +public class TaskGroup { + public int taskId = -1; + public ArrayList<IApplicationToken> tokens = new ArrayList<IApplicationToken>(); + + @Override + public String toString() { + return "id=" + taskId + " tokens=" + tokens; + } +} diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java new file mode 100644 index 0000000..4d34b36 --- /dev/null +++ b/services/java/com/android/server/wm/TaskStack.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 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.wm; + +import java.util.ArrayList; + +public class TaskStack { + final int mStackId; + final DisplayContent mDisplayContent; + private ArrayList<Task> mTasks = new ArrayList<Task>(); + int mLayer; + StackBox mParent; + + TaskStack(int stackId, StackBox parent) { + mStackId = stackId; + mParent = parent; + mDisplayContent = mParent.mDisplayContent; + } + + DisplayContent getDisplayContent() { + return mDisplayContent; + } + + ArrayList<Task> getTasks() { + return mTasks; + } + + ArrayList<Task> merge(TaskStack stack) { + ArrayList<Task> taskLists = stack.mTasks; + taskLists.addAll(mTasks); + mTasks = taskLists; + return taskLists; + } + + void addTask(Task task, boolean toTop) { + mParent.makeDirty(); + mTasks.add(toTop ? mTasks.size() : 0, task); + } + + void moveTaskToTop(Task task) { + mTasks.remove(task); + addTask(task, true); + } + + void moveTaskToBottom(Task task) { + mTasks.remove(task); + addTask(task, false); + } + + boolean removeTask(Task task) { + mParent.makeDirty(); + if (mTasks.remove(task)) { + if (mTasks.size() == 0) { + mParent.removeStack(); + } + return true; + } + return false; + } + + int numTokens() { + int count = 0; + for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { + count += mTasks.get(taskNdx).mAppTokens.size(); + } + return count; + } +} diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java index 3964782..b2aa8eb 100644 --- a/services/java/com/android/server/wm/WindowAnimator.java +++ b/services/java/com/android/server/wm/WindowAnimator.java @@ -21,7 +21,6 @@ import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.TypedValue; import android.view.Display; -import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManagerPolicy; import android.view.animation.Animation; @@ -172,28 +171,34 @@ public class WindowAnimator { } } - private void updateAppWindowsLocked() { + private void updateAppWindowsLocked(int displayId) { int i; - final ArrayList<AppWindowToken> appTokens = mService.mAnimatingAppTokens; - final int NAT = appTokens.size(); - for (i=0; i<NAT; i++) { - final AppWindowAnimator appAnimator = appTokens.get(i).mAppAnimator; - final boolean wasAnimating = appAnimator.animation != null - && appAnimator.animation != AppWindowAnimator.sDummyAnimation; - if (appAnimator.stepAnimationLocked(mCurrentTime)) { - mAnimating = true; - } else if (wasAnimating) { - // stopped animating, do one more pass through the layout - setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, - "appToken " + appAnimator.mAppToken + " done"); - if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG, - "updateWindowsApps...: done animating " + appAnimator.mAppToken); + final DisplayContent displayContent = mService.getDisplayContentLocked(displayId); + final ArrayList<Task> tasks = displayContent.getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowAnimator appAnimator = tokens.get(tokenNdx).mAppAnimator; + final boolean wasAnimating = appAnimator.animation != null + && appAnimator.animation != AppWindowAnimator.sDummyAnimation; + if (appAnimator.stepAnimationLocked(mCurrentTime)) { + mAnimating = true; + } else if (wasAnimating) { + // stopped animating, do one more pass through the layout + setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, + "appToken " + appAnimator.mAppToken + " done"); + if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG, + "updateWindowsApps...: done animating " + appAnimator.mAppToken); + } } } - final int NEAT = mService.mExitingAppTokens.size(); + final AppTokenList exitingAppTokens = displayContent.mExitingAppTokens; + final int NEAT = exitingAppTokens.size(); for (i=0; i<NEAT; i++) { - final AppWindowAnimator appAnimator = mService.mExitingAppTokens.get(i).mAppAnimator; + final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator; final boolean wasAnimating = appAnimator.animation != null && appAnimator.animation != AppWindowAnimator.sDummyAnimation; if (appAnimator.stepAnimationLocked(mCurrentTime)) { @@ -454,39 +459,43 @@ public class WindowAnimator { /** See if any windows have been drawn, so they (and others associated with them) can now be * shown. */ - private void testTokenMayBeDrawnLocked() { + private void testTokenMayBeDrawnLocked(int displayId) { // See if any windows have been drawn, so they (and others // associated with them) can now be shown. - final ArrayList<AppWindowToken> appTokens = mService.mAnimatingAppTokens; - final int NT = appTokens.size(); - for (int i=0; i<NT; i++) { - AppWindowToken wtoken = appTokens.get(i); - AppWindowAnimator appAnimator = wtoken.mAppAnimator; - final boolean allDrawn = wtoken.allDrawn; - if (allDrawn != appAnimator.allDrawn) { - appAnimator.allDrawn = allDrawn; - if (allDrawn) { - // The token has now changed state to having all - // windows shown... what to do, what to do? - if (appAnimator.freezingScreen) { - appAnimator.showAllWindowsLocked(); - mService.unsetAppFreezingScreenLocked(wtoken, false, true); - if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG, - "Setting mOrientationChangeComplete=true because wtoken " - + wtoken + " numInteresting=" + wtoken.numInterestingWindows - + " numDrawn=" + wtoken.numDrawnWindows); - // This will set mOrientationChangeComplete and cause a pass through layout. - setAppLayoutChanges(appAnimator, - WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, - "testTokenMayBeDrawnLocked: freezingScreen"); - } else { - setAppLayoutChanges(appAnimator, - WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM, - "testTokenMayBeDrawnLocked"); - - // We can now show all of the drawn windows! - if (!mService.mOpeningApps.contains(wtoken)) { - mAnimating |= appAnimator.showAllWindowsLocked(); + final ArrayList<Task> tasks = mService.getDisplayContentLocked(displayId).getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + AppWindowAnimator appAnimator = wtoken.mAppAnimator; + final boolean allDrawn = wtoken.allDrawn; + if (allDrawn != appAnimator.allDrawn) { + appAnimator.allDrawn = allDrawn; + if (allDrawn) { + // The token has now changed state to having all + // windows shown... what to do, what to do? + if (appAnimator.freezingScreen) { + appAnimator.showAllWindowsLocked(); + mService.unsetAppFreezingScreenLocked(wtoken, false, true); + if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG, + "Setting mOrientationChangeComplete=true because wtoken " + + wtoken + " numInteresting=" + wtoken.numInterestingWindows + + " numDrawn=" + wtoken.numDrawnWindows); + // This will set mOrientationChangeComplete and cause a pass through layout. + setAppLayoutChanges(appAnimator, + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, + "testTokenMayBeDrawnLocked: freezingScreen"); + } else { + setAppLayoutChanges(appAnimator, + WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM, + "testTokenMayBeDrawnLocked"); + + // We can now show all of the drawn windows! + if (!mService.mOpeningApps.contains(wtoken)) { + mAnimating |= appAnimator.showAllWindowsLocked(); + } } } } @@ -530,11 +539,10 @@ public class WindowAnimator { SurfaceControl.openTransaction(); SurfaceControl.setAnimationTransaction(); try { - updateAppWindowsLocked(); - final int numDisplays = mDisplayContentsAnimators.size(); for (int i = 0; i < numDisplays; i++) { final int displayId = mDisplayContentsAnimators.keyAt(i); + updateAppWindowsLocked(displayId); DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i); final ScreenRotationAnimation screenRotationAnimation = @@ -560,10 +568,11 @@ public class WindowAnimator { } } - testTokenMayBeDrawnLocked(); - for (int i = 0; i < numDisplays; i++) { final int displayId = mDisplayContentsAnimators.keyAt(i); + + testTokenMayBeDrawnLocked(displayId); + DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i); final ScreenRotationAnimation screenRotationAnimation = diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 56f4de5..5c3058d 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -198,6 +198,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_LAYOUT_REPEATS = true; static final boolean DEBUG_SURFACE_TRACE = false; static final boolean DEBUG_WINDOW_TRACE = false; + static final boolean DEBUG_TASK_MOVEMENT = false; static final boolean SHOW_SURFACE_ALLOC = false; static final boolean SHOW_TRANSACTIONS = false; static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS; @@ -207,6 +208,9 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean PROFILE_ORIENTATION = false; static final boolean localLOGV = DEBUG; + final static boolean REVERSE_ITERATOR = true; + final static boolean FORWARD_ITERATOR = false; + /** How much to multiply the policy's type layer, to reserve room * for multiple windows of the same type and Z-ordering adjustment * with TYPE_LAYER_OFFSET. */ @@ -333,34 +337,7 @@ public class WindowManagerService extends IWindowManager.Stub /** * Mapping from a token IBinder to a WindowToken object. */ - final HashMap<IBinder, WindowToken> mTokenMap = - new HashMap<IBinder, WindowToken>(); - - /** - * Window tokens that are in the process of exiting, but still - * on screen for animations. - */ - final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>(); - - /** - * List controlling the ordering of windows in different applications which must - * be kept in sync with ActivityManager. - */ - final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>(); - - /** - * AppWindowTokens in the Z order they were in at the start of an animation. Between - * animations this list is maintained in the exact order of mAppTokens. If tokens - * are added to mAppTokens during an animation an attempt is made to insert them at the same - * logical location in this list. Note that this list is always in sync with mWindows. - */ - ArrayList<AppWindowToken> mAnimatingAppTokens = new ArrayList<AppWindowToken>(); - - /** - * Application tokens that are in the process of exiting, but still - * on screen for animations. - */ - final ArrayList<AppWindowToken> mExitingAppTokens = new ArrayList<AppWindowToken>(); + final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>(); /** * List of window tokens that have finished starting their application, @@ -445,9 +422,11 @@ public class WindowManagerService extends IWindowManager.Stub String mLastANRState; - /** All DisplayDontents in the world, kept here */ + /** All DisplayContents in the world, kept here */ private SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>(); + private final AllWindowsIterator mTmpWindowsIterator = new AllWindowsIterator(); + int mRotation = 0; int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean mAltOrientation = false; @@ -623,6 +602,9 @@ public class WindowManagerService extends IWindowManager.Stub final WindowAnimator mAnimator; + SparseArray<Task> mTaskIdToTask = new SparseArray<Task>(); + SparseArray<TaskStack> mStackIdToStack = new SparseArray<TaskStack>(); + final class DragInputEventReceiver extends InputEventReceiver { public DragInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); @@ -852,10 +834,14 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowBefore(WindowState pos, WindowState window) { final WindowList windows = pos.getWindowList(); - final int i = windows.indexOf(pos); + int i = windows.indexOf(pos); if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( TAG, "Adding window " + window + " at " + i + " of " + windows.size() + " (before " + pos + ")"); + if (i < 0) { + Slog.w(TAG, "placeWindowBefore: Unable to find " + pos + " in " + windows); + i = 0; + } windows.add(i, window); mWindowsChanged = true; } @@ -912,218 +898,260 @@ public class WindowManagerService extends IWindowManager.Stub return -1; } - private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) { + private int addAppWindowToListLocked(final WindowState win) { final IWindow client = win.mClient; final WindowToken token = win.mToken; final DisplayContent displayContent = win.mDisplayContent; final WindowList windows = win.getWindowList(); final int N = windows.size(); - final WindowState attached = win.mAttachedWindow; - int i; WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); - if (attached == null) { - int tokenWindowsPos = 0; - int windowListPos = tokenWindowList.size(); - if (token.appWindowToken != null) { - int index = windowListPos - 1; - if (index >= 0) { - // If this application has existing windows, we - // simply place the new window on top of them... but - // keep the starting window on top. - if (win.mAttrs.type == TYPE_BASE_APPLICATION) { - // Base windows go behind everything else. - WindowState lowestWindow = tokenWindowList.get(0); - placeWindowBefore(lowestWindow, win); - tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows); - } else { - AppWindowToken atoken = win.mAppToken; - WindowState lastWindow = tokenWindowList.get(index); - if (atoken != null && lastWindow == atoken.startingWindow) { - placeWindowBefore(lastWindow, win); - tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows); - } else { - int newIdx = findIdxBasedOnAppTokens(win); - //there is a window above this one associated with the same - //apptoken note that the window could be a floating window - //that was created later or a window at the top of the list of - //windows associated with this token. - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { - Slog.v(TAG, "Adding window " + win + " at " - + (newIdx + 1) + " of " + N); - } - windows.add(newIdx + 1, win); - if (newIdx < 0) { - // No window from token found on win's display. - tokenWindowsPos = 0; - } else { - tokenWindowsPos = indexOfWinInWindowList( - windows.get(newIdx), token.windows) + 1; - } - mWindowsChanged = true; - } - } + int tokenWindowsPos = 0; + int windowListPos = tokenWindowList.size(); + if (!tokenWindowList.isEmpty()) { + // If this application has existing windows, we + // simply place the new window on top of them... but + // keep the starting window on top. + if (win.mAttrs.type == TYPE_BASE_APPLICATION) { + // Base windows go behind everything else. + WindowState lowestWindow = tokenWindowList.get(0); + placeWindowBefore(lowestWindow, win); + tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows); + } else { + AppWindowToken atoken = win.mAppToken; + WindowState lastWindow = tokenWindowList.get(windowListPos - 1); + if (atoken != null && lastWindow == atoken.startingWindow) { + placeWindowBefore(lastWindow, win); + tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows); } else { - // No windows from this token on this display - if (localLOGV) Slog.v( - TAG, "Figuring out where to add app window " - + client.asBinder() + " (token=" + token + ")"); - // Figure out where the window should go, based on the - // order of applications. - final int NA = mAnimatingAppTokens.size(); - WindowState pos = null; - for (i=NA-1; i>=0; i--) { - AppWindowToken t = mAnimatingAppTokens.get(i); - if (t == token) { - i--; - break; - } - - // We haven't reached the token yet; if this token - // is not going to the bottom and has windows on this display, we can - // use it as an anchor for when we do reach the token. - tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent); - if (!t.sendingToBottom && tokenWindowList.size() > 0) { - pos = tokenWindowList.get(0); - } - } - // We now know the index into the apps. If we found - // an app window above, that gives us the position; else - // we need to look some more. - if (pos != null) { - // Move behind any windows attached to this one. - WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); - if (atoken != null) { - tokenWindowList = - getTokenWindowsOnDisplay(atoken, win.mDisplayContent); - final int NC = tokenWindowList.size(); - if (NC > 0) { - WindowState bottom = tokenWindowList.get(0); - if (bottom.mSubLayer < 0) { - pos = bottom; - } - } - } - placeWindowBefore(pos, win); + int newIdx = findIdxBasedOnAppTokens(win); + //there is a window above this one associated with the same + //apptoken note that the window could be a floating window + //that was created later or a window at the top of the list of + //windows associated with this token. + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Adding window " + win + " at " + (newIdx + 1) + " of " + N); + windows.add(newIdx + 1, win); + if (newIdx < 0) { + // No window from token found on win's display. + tokenWindowsPos = 0; } else { - // Continue looking down until we find the first - // token that has windows on this display. - while (i >= 0) { - AppWindowToken t = mAnimatingAppTokens.get(i); - tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent); - final int NW = tokenWindowList.size(); - if (NW > 0) { - pos = tokenWindowList.get(NW-1); - break; - } - i--; - } - if (pos != null) { - // Move in front of any windows attached to this - // one. - WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); - if (atoken != null) { - final int NC = atoken.windows.size(); - if (NC > 0) { - WindowState top = atoken.windows.get(NC-1); - if (top.mSubLayer >= 0) { - pos = top; - } - } - } - placeWindowAfter(pos, win); - } else { - // Just search for the start of this layer. - final int myLayer = win.mBaseLayer; - for (i=0; i<N; i++) { - WindowState w = windows.get(i); - if (w.mBaseLayer > myLayer) { - break; - } - } - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { - Slog.v(TAG, "Adding window " + win + " at " - + i + " of " + N); - } - windows.add(i, win); - mWindowsChanged = true; - } + tokenWindowsPos = indexOfWinInWindowList( + windows.get(newIdx), token.windows) + 1; } + mWindowsChanged = true; } - } else { - // Figure out where window should go, based on layer. - final int myLayer = win.mBaseLayer; - for (i=N-1; i>=0; i--) { - if (windows.get(i).mBaseLayer <= myLayer) { - break; + } + return tokenWindowsPos; + } + + // No windows from this token on this display + if (localLOGV) Slog.v(TAG, "Figuring out where to add app window " + client.asBinder() + + " (token=" + token + ")"); + // Figure out where the window should go, based on the + // order of applications. + WindowState pos = null; + + final ArrayList<Task> tasks = win.getStack().getTasks(); + int taskNdx; + int tokenNdx = -1; + for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken t = tokens.get(tokenNdx); + if (t == token) { + --tokenNdx; + if (tokenNdx < 0) { + --taskNdx; + if (taskNdx >= 0) { + tokenNdx = tasks.get(taskNdx).mAppTokens.size() - 1; + } } + break; } - i++; - if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v( - TAG, "Adding window " + win + " at " - + i + " of " + N); - windows.add(i, win); - mWindowsChanged = true; + + // We haven't reached the token yet; if this token + // is not going to the bottom and has windows on this display, we can + // use it as an anchor for when we do reach the token. + tokenWindowList = getTokenWindowsOnDisplay(t, displayContent); + if (!t.sendingToBottom && tokenWindowList.size() > 0) { + pos = tokenWindowList.get(0); + } + } + if (tokenNdx >= 0) { + // early exit + break; } + } - if (addToToken) { - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); - token.windows.add(tokenWindowsPos, win); + // We now know the index into the apps. If we found + // an app window above, that gives us the position; else + // we need to look some more. + if (pos != null) { + // Move behind any windows attached to this one. + WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); + if (atoken != null) { + tokenWindowList = + getTokenWindowsOnDisplay(atoken, displayContent); + final int NC = tokenWindowList.size(); + if (NC > 0) { + WindowState bottom = tokenWindowList.get(0); + if (bottom.mSubLayer < 0) { + pos = bottom; + } + } + } + placeWindowBefore(pos, win); + return tokenWindowsPos; + } + + // Continue looking down until we find the first + // token that has windows on this display. + for ( ; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + for ( ; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken t = tokens.get(tokenNdx); + tokenWindowList = getTokenWindowsOnDisplay(t, displayContent); + final int NW = tokenWindowList.size(); + if (NW > 0) { + pos = tokenWindowList.get(NW-1); + break; + } + } + if (tokenNdx >= 0) { + // found + break; } + } - } else { - // Figure out this window's ordering relative to the window - // it is attached to. - final int NA = tokenWindowList.size(); - final int sublayer = win.mSubLayer; - int largestSublayer = Integer.MIN_VALUE; - WindowState windowWithLargestSublayer = null; - for (i=0; i<NA; i++) { - WindowState w = tokenWindowList.get(i); - final int wSublayer = w.mSubLayer; - if (wSublayer >= largestSublayer) { - largestSublayer = wSublayer; - windowWithLargestSublayer = w; - } - if (sublayer < 0) { - // For negative sublayers, we go below all windows - // in the same sublayer. - if (wSublayer >= sublayer) { - if (addToToken) { - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); - token.windows.add(i, win); - } - placeWindowBefore(wSublayer >= 0 ? attached : w, win); - break; - } - } else { - // For positive sublayers, we go above all windows - // in the same sublayer. - if (wSublayer > sublayer) { - if (addToToken) { - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); - token.windows.add(i, win); - } - placeWindowBefore(w, win); - break; + if (pos != null) { + // Move in front of any windows attached to this + // one. + WindowToken atoken = mTokenMap.get(pos.mClient.asBinder()); + if (atoken != null) { + final int NC = atoken.windows.size(); + if (NC > 0) { + WindowState top = atoken.windows.get(NC-1); + if (top.mSubLayer >= 0) { + pos = top; } } } - if (i >= NA) { - if (addToToken) { - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); - token.windows.add(win); + placeWindowAfter(pos, win); + return tokenWindowsPos; + } + + // Just search for the start of this layer. + final int myLayer = win.mBaseLayer; + int i; + for (i = 0; i < N; i++) { + WindowState w = windows.get(i); + if (w.mBaseLayer > myLayer) { + break; + } + } + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Adding window " + win + " at " + i + " of " + N); + windows.add(i, win); + mWindowsChanged = true; + return tokenWindowsPos; + } + + private void addFreeWindowToListLocked(final WindowState win) { + final WindowList windows = win.getWindowList(); + + // Figure out where window should go, based on layer. + final int myLayer = win.mBaseLayer; + int i; + for (i = windows.size() - 1; i >= 0; i--) { + if (windows.get(i).mBaseLayer <= myLayer) { + break; + } + } + i++; + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Adding window " + win + " at " + i + " of " + windows.size()); + windows.add(i, win); + mWindowsChanged = true; + } + + private void addAttachedWindowToListLocked(final WindowState win, boolean addToToken) { + final WindowToken token = win.mToken; + final DisplayContent displayContent = win.mDisplayContent; + final WindowState attached = win.mAttachedWindow; + + WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent); + + // Figure out this window's ordering relative to the window + // it is attached to. + final int NA = tokenWindowList.size(); + final int sublayer = win.mSubLayer; + int largestSublayer = Integer.MIN_VALUE; + WindowState windowWithLargestSublayer = null; + int i; + for (i = 0; i < NA; i++) { + WindowState w = tokenWindowList.get(i); + final int wSublayer = w.mSubLayer; + if (wSublayer >= largestSublayer) { + largestSublayer = wSublayer; + windowWithLargestSublayer = w; + } + if (sublayer < 0) { + // For negative sublayers, we go below all windows + // in the same sublayer. + if (wSublayer >= sublayer) { + if (addToToken) { + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); + token.windows.add(i, win); + } + placeWindowBefore(wSublayer >= 0 ? attached : w, win); + break; } - if (sublayer < 0) { - placeWindowBefore(attached, win); - } else { - placeWindowAfter(largestSublayer >= 0 - ? windowWithLargestSublayer - : attached, - win); + } else { + // For positive sublayers, we go above all windows + // in the same sublayer. + if (wSublayer > sublayer) { + if (addToToken) { + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); + token.windows.add(i, win); + } + placeWindowBefore(w, win); + break; } } } + if (i >= NA) { + if (addToToken) { + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); + token.windows.add(win); + } + if (sublayer < 0) { + placeWindowBefore(attached, win); + } else { + placeWindowAfter(largestSublayer >= 0 + ? windowWithLargestSublayer + : attached, + win); + } + } + } + + private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) { + if (win.mAttachedWindow == null) { + final WindowToken token = win.mToken; + int tokenWindowsPos = 0; + if (token.appWindowToken != null) { + tokenWindowsPos = addAppWindowToListLocked(win); + } else { + addFreeWindowToListLocked(win); + } + if (addToToken) { + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); + token.windows.add(tokenWindowsPos, win); + } + } else { + addAttachedWindowToListLocked(win, addToToken); + } if (win.mAppToken != null && addToToken) { win.mAppToken.allAppWindows.add(win); @@ -1538,10 +1566,6 @@ public class WindowManagerService extends IWindowManager.Stub return true; } - void adjustInputMethodDialogsLocked() { - moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); - } - final boolean isWallpaperVisible(WindowState wallpaperTarget) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured=" + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??") @@ -2215,7 +2239,7 @@ public class WindowManagerService extends IWindowManager.Stub } else if (type == TYPE_INPUT_METHOD_DIALOG) { mInputMethodDialogs.add(win); addWindowToListInOrderLocked(win, true); - adjustInputMethodDialogsLocked(); + moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); imMayMove = false; } else { addWindowToListInOrderLocked(win, true); @@ -2487,22 +2511,15 @@ public class WindowManagerService extends IWindowManager.Stub public void updateAppOpsState() { synchronized(mWindowMap) { - boolean changed = false; - for (int i=0; i<mDisplayContents.size(); i++) { - DisplayContent display = mDisplayContents.valueAt(i); - WindowList windows = display.getWindowList(); - for (int j=0; j<windows.size(); j++) { - final WindowState win = windows.get(j); - if (win.mAppOp != AppOpsManager.OP_NONE) { - changed |= win.setAppOpVisibilityLw(mAppOps.checkOpNoThrow(win.mAppOp, - win.getOwningUid(), - win.getOwningPackage()) == AppOpsManager.MODE_ALLOWED); - } + mTmpWindowsIterator.reset(FORWARD_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + final WindowState win = mTmpWindowsIterator.next(); + if (win.mAppOp != AppOpsManager.OP_NONE) { + final int mode = mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(), + win.getOwningPackage()); + win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED); } } - if (changed) { - scheduleAnimationLocked(); - } } } @@ -3149,32 +3166,64 @@ public class WindowManagerService extends IWindowManager.Stub // Application Window Tokens // ------------------------------------------------------------- - public void validateAppTokens(List<IBinder> tokens) { - int v = tokens.size()-1; - int m = mAppTokens.size()-1; - while (v >= 0 && m >= 0) { - AppWindowToken atoken = mAppTokens.get(m); - if (atoken.removed) { - m--; - continue; + public void validateAppTokens(List<TaskGroup> tasks) { + synchronized (mWindowMap) { + int t = tasks.size() - 1; + if (t < 0) { + Slog.w(TAG, "validateAppTokens: empty task list"); + return; } - if (tokens.get(v) != atoken.token) { - Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v) - + " @ " + v + ", internal is " + atoken.token + " @ " + m); + + TaskGroup task = tasks.get(0); + int taskId = task.taskId; + Task targetTask = mTaskIdToTask.get(taskId); + DisplayContent displayContent = targetTask.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG, "validateAppTokens: no Display for taskId=" + taskId); + return; } - v--; - m--; - } - while (v >= 0) { - Slog.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v); - v--; - } - while (m >= 0) { - AppWindowToken atoken = mAppTokens.get(m); - if (!atoken.removed) { - Slog.w(TAG, "Invalid internal atoken: " + atoken.token + " @ " + m); + + final ArrayList<Task> localTasks = displayContent.getTasks(); + int taskNdx; + for (taskNdx = localTasks.size() - 1; taskNdx >= 0 && t >= 0; --taskNdx, --t) { + AppTokenList localTokens = localTasks.get(taskNdx).mAppTokens; + task = tasks.get(t); + List<IApplicationToken> tokens = task.tokens; + + DisplayContent lastDisplayContent = displayContent; + displayContent = mTaskIdToTask.get(taskId).getDisplayContent(); + if (displayContent != lastDisplayContent) { + Slog.w(TAG, "validateAppTokens: displayContent changed in TaskGroup list!"); + return; + } + + int tokenNdx; + int v; + for (tokenNdx = localTokens.size() - 1, v = task.tokens.size() - 1; + tokenNdx >= 0 && v >= 0; ) { + final AppWindowToken atoken = localTokens.get(tokenNdx); + if (atoken.removed) { + --tokenNdx; + continue; + } + if (tokens.get(v) != atoken.token) { + break; + } + --tokenNdx; + v--; + } + + if (tokenNdx >= 0 || v >= 0) { + break; + } + } + + if (taskNdx >= 0 || t >= 0) { + Slog.w(TAG, "validateAppTokens: Mismatch! ActivityManager=" + tasks); + Slog.w(TAG, "validateAppTokens: Mismatch! WindowManager=" + + displayContent.getTasks()); + Slog.w(TAG, "validateAppTokens: Mismatch! Callers=" + Debug.getCallers(4)); } - m--; } } @@ -3238,6 +3287,7 @@ public class WindowManagerService extends IWindowManager.Stub final long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { + DisplayContent displayContent = null; WindowToken wtoken = mTokenMap.remove(token); if (wtoken != null) { boolean delayed = false; @@ -3247,6 +3297,7 @@ public class WindowManagerService extends IWindowManager.Stub for (int i=0; i<N; i++) { WindowState win = wtoken.windows.get(i); + displayContent = win.mDisplayContent; if (win.mWinAnimator.isAnimating()) { delayed = true; @@ -3256,13 +3307,12 @@ public class WindowManagerService extends IWindowManager.Stub win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false); //TODO (multidisplay): Magnification is supported only for the default - if (mDisplayMagnifier != null - && win.getDisplayId() == Display.DEFAULT_DISPLAY) { + if (mDisplayMagnifier != null && win.isDefaultDisplay()) { mDisplayMagnifier.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_EXIT); } changed = true; - win.mDisplayContent.layoutNeeded = true; + displayContent.layoutNeeded = true; } } @@ -3275,7 +3325,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (delayed) { - mExitingTokens.add(wtoken); + displayContent.mExitingTokens.add(wtoken); } else if (wtoken.windowType == TYPE_WALLPAPER) { mWallpaperTokens.remove(wtoken); } @@ -3289,27 +3339,9 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); } - /** - * Find the location to insert a new AppWindowToken into the window-ordered app token list. - * Note that mAppTokens.size() == mAnimatingAppTokens.size() + 1. - * @param addPos The location the token was inserted into in mAppTokens. - * @param atoken The token to insert. - */ - private void addAppTokenToAnimating(final int addPos, final AppWindowToken atoken) { - if (addPos == 0 || addPos == mAnimatingAppTokens.size()) { - // It was inserted into the beginning or end of mAppTokens. Honor that. - mAnimatingAppTokens.add(addPos, atoken); - return; - } - // Find the item immediately above the mAppTokens insertion point and put the token - // immediately below that one in mAnimatingAppTokens. - final AppWindowToken aboveAnchor = mAppTokens.get(addPos + 1); - mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), atoken); - } - @Override - public void addAppToken(int addPos, IApplicationToken token, - int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked) { + public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, + int requestedOrientation, boolean fullscreen, boolean showWhenLocked) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3337,14 +3369,26 @@ public class WindowManagerService extends IWindowManager.Stub } atoken = new AppWindowToken(this, token); atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; - atoken.groupId = groupId; + atoken.groupId = taskId; atoken.appFullscreen = fullscreen; atoken.showWhenLocked = showWhenLocked; atoken.requestedOrientation = requestedOrientation; if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken + " at " + addPos); - mAppTokens.add(addPos, atoken); - addAppTokenToAnimating(addPos, atoken); + + Task task = mTaskIdToTask.get(taskId); + if (task == null) { + TaskStack stack = mStackIdToStack.get(stackId); + if (stack == null) { + throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId); + } + task = new Task(atoken, stack); + stack.addTask(task, true); + mTaskIdToTask.put(taskId, task); + } else { + task.addAppToken(addPos, atoken); + } + mTokenMap.put(token.asBinder(), atoken); // Application tokens start out hidden. @@ -3363,12 +3407,21 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized(mWindowMap) { - AppWindowToken atoken = findAppWindowToken(token); + final AppWindowToken atoken = findAppWindowToken(token); if (atoken == null) { Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token); return; } + Task oldTask = mTaskIdToTask.get(atoken.groupId); + oldTask.removeAppToken(atoken); + atoken.groupId = groupId; + Task newTask = mTaskIdToTask.get(groupId); + if (newTask == null) { + throw new IllegalStateException("setAppGroupId: groupId=" + groupId + + " does not exist"); + } + newTask.mAppTokens.add(atoken); } } @@ -3409,72 +3462,76 @@ public class WindowManagerService extends IWindowManager.Stub } public int getOrientationFromAppTokensLocked() { - int curGroup = 0; int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; boolean findingBehind = false; - boolean haveGroup = false; boolean lastFullscreen = false; - for (int pos = mAppTokens.size() - 1; pos >= 0; pos--) { - AppWindowToken atoken = mAppTokens.get(pos); - - if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + atoken); - - // if we're about to tear down this window and not seek for - // the behind activity, don't use it for orientation - if (!findingBehind - && (!atoken.hidden && atoken.hiddenRequested)) { - if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken - + " -- going to hide"); - continue; - } - - if (haveGroup == true && curGroup != atoken.groupId) { - // If we have hit a new application group, and the bottom - // of the previous group didn't explicitly say to use - // the orientation behind it, and the last app was - // full screen, then we'll stick with the - // user's orientation. - if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND - && lastFullscreen) { + // TODO: Multi window. + DisplayContent displayContent = getDefaultDisplayContentLocked(); + final ArrayList<Task> tasks = displayContent.getTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int firstToken = tokens.size() - 1; + for (int tokenNdx = firstToken; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken atoken = tokens.get(tokenNdx); + + if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + atoken); + + // if we're about to tear down this window and not seek for + // the behind activity, don't use it for orientation + if (!findingBehind + && (!atoken.hidden && atoken.hiddenRequested)) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken + + " -- going to hide"); + continue; + } + + if (tokenNdx == firstToken) { + // If we have hit a new Task, and the bottom + // of the previous group didn't explicitly say to use + // the orientation behind it, and the last app was + // full screen, then we'll stick with the + // user's orientation. + if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND + && lastFullscreen) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + + " -- end of group, return " + lastOrientation); + return lastOrientation; + } + } + + // We ignore any hidden applications on the top. + if (atoken.hiddenRequested || atoken.willBeHidden) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken + + " -- hidden on top"); + continue; + } + + if (tokenNdx == 0) { + // Last token in this task. + lastOrientation = atoken.requestedOrientation; + } + + int or = atoken.requestedOrientation; + // If this application is fullscreen, and didn't explicitly say + // to use the orientation behind it, then just take whatever + // orientation it has and ignores whatever is under it. + lastFullscreen = atoken.appFullscreen; + if (lastFullscreen + && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken - + " -- end of group, return " + lastOrientation); - return lastOrientation; + + " -- full screen, return " + or); + return or; } + // If this application has requested an explicit orientation, + // then use it. + if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken + + " -- explicitly set, return " + or); + return or; + } + findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); } - - // We ignore any hidden applications on the top. - if (atoken.hiddenRequested || atoken.willBeHidden) { - if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken - + " -- hidden on top"); - continue; - } - - if (!haveGroup) { - haveGroup = true; - curGroup = atoken.groupId; - lastOrientation = atoken.requestedOrientation; - } - - int or = atoken.requestedOrientation; - // If this application is fullscreen, and didn't explicitly say - // to use the orientation behind it, then just take whatever - // orientation it has and ignores whatever is under it. - lastFullscreen = atoken.appFullscreen; - if (lastFullscreen - && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { - if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken - + " -- full screen, return " + or); - return or; - } - // If this application has requested an explicit orientation, - // then use it. - if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED - && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { - if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken - + " -- explicitly set, return " + or); - return or; - } - findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); } if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation"); return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -4330,11 +4387,13 @@ public class WindowManagerService extends IWindowManager.Stub TAG, "Removing app " + wtoken + " delayed=" + delayed + " animation=" + wtoken.mAppAnimator.animation + " animating=" + wtoken.mAppAnimator.animating); + final Task task = mTaskIdToTask.get(wtoken.groupId); + DisplayContent displayContent = task.getDisplayContent(); if (delayed) { // set the token aside because it has an active animation to be finished if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "removeAppToken make exiting: " + wtoken); - mExitingAppTokens.add(wtoken); + displayContent.mExitingAppTokens.add(wtoken); } else { // Make sure there is no animation running on this token, // so any windows associated with it will be removed as @@ -4344,8 +4403,10 @@ public class WindowManagerService extends IWindowManager.Stub } if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "removeAppToken: " + wtoken); - mAppTokens.remove(wtoken); - mAnimatingAppTokens.remove(wtoken); + + if (task.removeAppToken(wtoken)) { + mTaskIdToTask.delete(wtoken.groupId); + } wtoken.removed = true; if (wtoken.startingData != null) { startingToken = wtoken; @@ -4397,78 +4458,91 @@ public class WindowManagerService extends IWindowManager.Stub } void dumpAppTokensLocked() { - for (int i=mAppTokens.size()-1; i>=0; i--) { - Slog.v(TAG, " #" + i + ": " + mAppTokens.get(i).token); - } - } - - void dumpAnimatingAppTokensLocked() { - for (int i=mAnimatingAppTokens.size()-1; i>=0; i--) { - Slog.v(TAG, " #" + i + ": " + mAnimatingAppTokens.get(i).token); + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + DisplayContent displayContent = iterator.next(); + Slog.v(TAG, " Display " + displayContent.getDisplayId()); + final ArrayList<Task> tasks = displayContent.getTasks(); + int i = displayContent.numTokens(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + Slog.v(TAG, " #" + --i + ": " + wtoken.token); + } + } } } void dumpWindowsLocked() { int i = 0; - final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR); - while (iterator.hasNext()) { - final WindowState w = iterator.next(); + mTmpWindowsIterator.reset(REVERSE_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + final WindowState w = mTmpWindowsIterator.next(); Slog.v(TAG, " #" + i++ + ": " + w); } } - private int findWindowOffsetLocked(WindowList windows, int tokenPos) { - final int NW = windows.size(); - - if (tokenPos >= mAnimatingAppTokens.size()) { - int i = NW; - while (i > 0) { - i--; - WindowState win = windows.get(i); - if (win.getAppToken() != null) { - return i+1; - } - } + private int findAppWindowInsertionPointLocked(AppWindowToken target) { + final int taskId = target.groupId; + Task targetTask = mTaskIdToTask.get(taskId); + if (targetTask == null) { + Slog.w(TAG, "findAppWindowInsertionPointLocked: no Task for " + target + " taskId=" + + taskId); + return 0; } + DisplayContent displayContent = targetTask.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG, "findAppWindowInsertionPointLocked: no DisplayContent for " + target); + return 0; + } + final WindowList windows = displayContent.getWindowList(); + final int NW = windows.size(); - while (tokenPos > 0) { - // Find the first app token below the new position that has - // a window displayed. - final AppWindowToken wtoken = mAppTokens.get(tokenPos-1); - if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows @ " - + tokenPos + " -- " + wtoken.token); - if (wtoken.sendingToBottom) { - if (DEBUG_REORDER) Slog.v(TAG, - "Skipping token -- currently sending to bottom"); - tokenPos--; + boolean found = false; + final ArrayList<Task> tasks = displayContent.getTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + final Task task = tasks.get(taskNdx); + if (!found && task.taskId != taskId) { continue; } - int i = wtoken.windows.size(); - while (i > 0) { - i--; - WindowState win = wtoken.windows.get(i); - int j = win.mChildWindows.size(); - while (j > 0) { - j--; - WindowState cwin = win.mChildWindows.get(j); - if (cwin.mSubLayer >= 0) { - for (int pos=NW-1; pos>=0; pos--) { - if (windows.get(pos) == cwin) { - if (DEBUG_REORDER) Slog.v(TAG, - "Found child win @" + (pos+1)); - return pos+1; + AppTokenList tokens = task.mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + if (!found && wtoken == target) { + found = true; + } + if (found) { + // Find the first app token below the new position that has + // a window displayed. + if (DEBUG_REORDER) Slog.v(TAG, "Looking for lower windows in " + wtoken.token); + if (wtoken.sendingToBottom) { + if (DEBUG_REORDER) Slog.v(TAG, "Skipping token -- currently sending to bottom"); + continue; + } + for (int i = wtoken.windows.size() - 1; i >= 0; --i) { + WindowState win = wtoken.windows.get(i); + for (int j = win.mChildWindows.size() - 1; j >= 0; --j) { + WindowState cwin = win.mChildWindows.get(j); + if (cwin.mSubLayer >= 0) { + for (int pos = NW - 1; pos >= 0; pos--) { + if (windows.get(pos) == cwin) { + if (DEBUG_REORDER) Slog.v(TAG, + "Found child win @" + (pos + 1)); + return pos + 1; + } + } + } + } + for (int pos = NW - 1; pos >= 0; pos--) { + if (windows.get(pos) == win) { + if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos + 1)); + return pos + 1; } } - } - } - for (int pos=NW-1; pos>=0; pos--) { - if (windows.get(pos) == win) { - if (DEBUG_REORDER) Slog.v(TAG, "Found win @" + (pos+1)); - return pos+1; } } } - tokenPos--; } return 0; @@ -4517,198 +4591,124 @@ public class WindowManagerService extends IWindowManager.Stub return index; } - @Override - public void moveAppToken(int index, IBinder token) { - if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, - "moveAppToken()")) { - throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); - } - - synchronized(mWindowMap) { - if (DEBUG_REORDER) Slog.v(TAG, "Initial app tokens:"); - if (DEBUG_REORDER) dumpAppTokensLocked(); - final AppWindowToken wtoken = findAppWindowToken(token); - final int oldIndex = mAppTokens.indexOf(wtoken); - if (DEBUG_TOKEN_MOVEMENT || DEBUG_REORDER) Slog.v(TAG, - "Start moving token " + wtoken + " initially at " - + oldIndex); - if (oldIndex > index && mAppTransition.isTransitionSet()) { - // animation towards back has not started, copy old list for duration of animation. - mAnimatingAppTokens.clear(); - mAnimatingAppTokens.addAll(mAppTokens); - } - if (wtoken == null || !mAppTokens.remove(wtoken)) { - Slog.w(TAG, "Attempting to reorder token that doesn't exist: " - + token + " (" + wtoken + ")"); - return; - } - mAppTokens.add(index, wtoken); - if (DEBUG_REORDER) Slog.v(TAG, "Moved " + token + " to " + index + ":"); - else if (DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "Moved " + token + " to " + index); - if (DEBUG_REORDER) dumpAppTokensLocked(); - if (!mAppTransition.isTransitionSet()) { - // Not animating, bring animating app list in line with mAppTokens. - mAnimatingAppTokens.clear(); - mAnimatingAppTokens.addAll(mAppTokens); - - // Bring window ordering, window focus and input window in line with new app token - final long origId = Binder.clearCallingIdentity(); - if (DEBUG_REORDER) Slog.v(TAG, "Removing windows in " + token + ":"); - if (DEBUG_REORDER) dumpWindowsLocked(); - if (tmpRemoveAppWindowsLocked(wtoken)) { - if (DEBUG_REORDER) Slog.v(TAG, "Adding windows back in:"); - if (DEBUG_REORDER) dumpWindowsLocked(); - DisplayContentsIterator iterator = new DisplayContentsIterator(); - while(iterator.hasNext()) { - final DisplayContent displayContent = iterator.next(); - final WindowList windows = displayContent.getWindowList(); - final int pos = findWindowOffsetLocked(windows, index); - final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken); - if (pos != newPos) { - displayContent.layoutNeeded = true; - } - } - if (DEBUG_REORDER) Slog.v(TAG, "Final window list:"); - if (DEBUG_REORDER) dumpWindowsLocked(); - updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, - false /*updateInputWindows*/); - mInputMonitor.setUpdateInputWindowsNeededLw(); - performLayoutAndPlaceSurfacesLocked(); - mInputMonitor.updateInputWindowsLw(false /*force*/); - } - Binder.restoreCallingIdentity(origId); - } - } - } - - private void removeAppTokensLocked(List<IBinder> tokens) { - // XXX This should be done more efficiently! - // (take advantage of the fact that both lists should be - // ordered in the same way.) - int N = tokens.size(); - for (int i=0; i<N; i++) { - IBinder token = tokens.get(i); - final AppWindowToken wtoken = findAppWindowToken(token); - if (DEBUG_REORDER || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, - "Temporarily removing " + wtoken + " from " + mAppTokens.indexOf(wtoken)); - if (!mAppTokens.remove(wtoken)) { - Slog.w(TAG, "Attempting to reorder token that doesn't exist: " - + token + " (" + wtoken + ")"); - i--; - N--; - } - } - } + private void moveTaskWindowsLocked(Task task) { + DisplayContent displayContent = task.getDisplayContent(); - private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) { // First remove all of the windows from the list. - final int N = tokens.size(); - int i; - for (i=0; i<N; i++) { - WindowToken token = mTokenMap.get(tokens.get(i)); - if (token != null) { - tmpRemoveAppWindowsLocked(token); - } + AppTokenList tokens = task.mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = numTokens - 1; tokenNdx >= 0; --tokenNdx) { + tmpRemoveAppWindowsLocked(tokens.get(tokenNdx)); } // And now add them back at the correct place. - DisplayContentsIterator iterator = new DisplayContentsIterator(); - while (iterator.hasNext()) { - final DisplayContent displayContent = iterator.next(); - final WindowList windows = displayContent.getWindowList(); - // Where to start adding? - int pos = findWindowOffsetLocked(windows, tokenPos); - for (i=0; i<N; i++) { - WindowToken token = mTokenMap.get(tokens.get(i)); - if (token != null) { - final int newPos = reAddAppWindowsLocked(displayContent, pos, token); - if (newPos != pos) { - displayContent.layoutNeeded = true; - } - pos = newPos; + // Where to start adding? + int pos = findAppWindowInsertionPointLocked(tokens.get(0)); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + if (wtoken != null) { + final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken); + if (newPos != pos) { + displayContent.layoutNeeded = true; } + pos = newPos; } - if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, - false /*updateInputWindows*/)) { - assignLayersLocked(windows); - } + } + if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/)) { + assignLayersLocked(displayContent.getWindowList()); } + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, + false /*updateInputWindows*/); mInputMonitor.setUpdateInputWindowsNeededLw(); - - // Note that the above updateFocusedWindowLocked used to sit here. - performLayoutAndPlaceSurfacesLocked(); mInputMonitor.updateInputWindowsLw(false /*force*/); //dump(); } - @Override - public void moveAppTokensToTop(List<IBinder> tokens) { - if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, - "moveAppTokensToTop()")) { - throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + public void moveTaskToTop(int taskId) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized(mWindowMap) { + Task task = mTaskIdToTask.get(taskId); + if (task == null) { + Slog.e(TAG, "moveTaskToTop: taskId=" + taskId + " not found in mTaskIdToTask"); + return; + } + task.mStack.moveTaskToTop(task); + moveTaskWindowsLocked(task); + } + } finally { + Binder.restoreCallingIdentity(origId); } + } + public void moveTaskToBottom(int taskId) { final long origId = Binder.clearCallingIdentity(); - synchronized(mWindowMap) { - removeAppTokensLocked(tokens); - final int N = tokens.size(); - for (int i=0; i<N; i++) { - AppWindowToken wt = findAppWindowToken(tokens.get(i)); - if (wt != null) { - if (DEBUG_TOKEN_MOVEMENT || DEBUG_REORDER) Slog.v(TAG, - "Adding next to top: " + wt); - mAppTokens.add(wt); - if (mAppTransition.isTransitionSet()) { - wt.sendingToBottom = false; - } + try { + synchronized(mWindowMap) { + Task task = mTaskIdToTask.get(taskId); + if (task == null) { + Slog.e(TAG, "moveTaskToBottom: taskId=" + taskId + + " not found in mTaskIdToTask"); + return; } + task.mStack.moveTaskToBottom(task); + moveTaskWindowsLocked(task); } + } finally { + Binder.restoreCallingIdentity(origId); + } + } - mAnimatingAppTokens.clear(); - mAnimatingAppTokens.addAll(mAppTokens); - moveAppWindowsLocked(tokens, mAppTokens.size()); + /** + * Create a new TaskStack and place it next to an existing stack. + * @param stackId The unique identifier of the new stack. + * @param relativeStackId The existing stack that this stack goes before or after. + * @param position One of: + * {@link StackBox#TASK_STACK_GOES_BEFORE} + * {@link StackBox#TASK_STACK_GOES_AFTER} + * {@link StackBox#TASK_STACK_GOES_ABOVE} + * {@link StackBox#TASK_STACK_GOES_BELOW} + * {@link StackBox#TASK_STACK_GOES_UNDER} + * {@link StackBox#TASK_STACK_GOES_OVER} + * @param weight Relative weight for determining how big to make the new TaskStack. + */ + public void createStack(int stackId, int relativeStackId, int position, float weight) { + // TODO: Create a stack on other displays. + synchronized (mWindowMap) { + TaskStack stack = getDefaultDisplayContentLocked().createStack(stackId, + relativeStackId, position, weight); + mStackIdToStack.put(stackId, stack); + performLayoutAndPlaceSurfacesLocked(); } - Binder.restoreCallingIdentity(origId); } - @Override - public void moveAppTokensToBottom(List<IBinder> tokens) { - if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, - "moveAppTokensToBottom()")) { - throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + public void moveTaskToStack(int taskId, int stackId, boolean toTop) { + synchronized (mWindowMap) { + Task task = mTaskIdToTask.get(taskId); + task.mStack.removeTask(task); + TaskStack newStack = mStackIdToStack.get(stackId); + newStack.addTask(task, toTop); + performLayoutAndPlaceSurfacesLocked(); } + } - final long origId = Binder.clearCallingIdentity(); - synchronized(mWindowMap) { - final int N = tokens.size(); - if (N > 0) { - // animating towards back, hang onto old list for duration of animation. - mAnimatingAppTokens.clear(); - mAnimatingAppTokens.addAll(mAppTokens); - } - removeAppTokensLocked(tokens); - int pos = 0; - for (int i=0; i<N; i++) { - AppWindowToken wt = findAppWindowToken(tokens.get(i)); - if (wt != null) { - if (DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, - "Adding next to bottom: " + wt + " at " + pos); - mAppTokens.add(pos, wt); - if (mAppTransition.isTransitionSet()) { - wt.sendingToBottom = true; - } - pos++; + public void resizeStack(int stackId, float weight) { + synchronized (mWindowMap) { + Task task = null; + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + if (iterator.next().resizeStack(stackId, weight)) { + break; + } else if (!iterator.hasNext()) { + throw new IllegalArgumentException("resizeStack: stackId " + stackId + + " not found."); } } - - mAnimatingAppTokens.clear(); - mAnimatingAppTokens.addAll(mAppTokens); - moveAppWindowsLocked(tokens, 0); } - Binder.restoreCallingIdentity(origId); } // ------------------------------------------------------------- @@ -4829,9 +4829,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void closeSystemDialogs(String reason) { synchronized(mWindowMap) { - final AllWindowsIterator iterator = new AllWindowsIterator(); - while (iterator.hasNext()) { - final WindowState w = iterator.next(); + mTmpWindowsIterator.reset(FORWARD_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + final WindowState w = mTmpWindowsIterator.next(); if (w.mHasSurface) { try { w.mClient.closeSystemDialogs(reason); @@ -5225,9 +5225,9 @@ public class WindowManagerService extends IWindowManager.Stub // the background..) if (on) { boolean isVisible = false; - final AllWindowsIterator iterator = new AllWindowsIterator(); - while (iterator.hasNext()) { - final WindowState ws = iterator.next(); + mTmpWindowsIterator.reset(FORWARD_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + final WindowState ws = mTmpWindowsIterator.next(); if (ws.mSession.mPid == pid && ws.isVisibleLw()) { isVisible = true; break; @@ -6122,9 +6122,9 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized (mWindowMap) { - final AllWindowsIterator iterator = new AllWindowsIterator(); - while (iterator.hasNext()) { - final WindowState w = iterator.next(); + mTmpWindowsIterator.reset(FORWARD_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + final WindowState w = mTmpWindowsIterator.next(); if (System.identityHashCode(w) == hashCode) { return w; } @@ -6673,10 +6673,10 @@ public class WindowManagerService extends IWindowManager.Stub // TODO(multidisplay): Call isScreenOn for each display. private void sendScreenStatusToClientsLocked() { final boolean on = mPowerManager.isScreenOn(); - final AllWindowsIterator iterator = new AllWindowsIterator(); - while (iterator.hasNext()) { + mTmpWindowsIterator.reset(FORWARD_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { try { - iterator.next().mClient.dispatchScreenState(on); + mTmpWindowsIterator.next().mClient.dispatchScreenState(on); } catch (RemoteException e) { // Ignored } @@ -6960,8 +6960,6 @@ public class WindowManagerService extends IWindowManager.Stub if (mAppTransition.isTransitionSet()) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** APP TRANSITION TIMEOUT"); mAppTransition.setTimeout(); - mAnimatingAppTokens.clear(); - mAnimatingAppTokens.addAll(mAppTokens); performLayoutAndPlaceSurfacesLocked(); } } @@ -7006,13 +7004,16 @@ public class WindowManagerService extends IWindowManager.Stub case APP_FREEZE_TIMEOUT: { synchronized (mWindowMap) { Slog.w(TAG, "App freeze timeout expired."); - int i = mAppTokens.size(); - while (i > 0) { - i--; - AppWindowToken tok = mAppTokens.get(i); - if (tok.mAppAnimator.freezingScreen) { - Slog.w(TAG, "Force clearing freeze: " + tok); - unsetAppFreezingScreenLocked(tok, true, true); + DisplayContent displayContent = getDefaultDisplayContentLocked(); + final ArrayList<Task> tasks = displayContent.getTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + AppWindowToken tok = tokens.get(tokenNdx); + if (tok.mAppAnimator.freezingScreen) { + Slog.w(TAG, "Force clearing freeze: " + tok); + unsetAppFreezingScreenLocked(tok, true, true); + } } } } @@ -7408,6 +7409,7 @@ public class WindowManagerService extends IWindowManager.Stub performLayoutAndPlaceSurfacesLocked(); } + @Override public void setOverscan(int displayId, int left, int top, int right, int bottom) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) != @@ -7526,15 +7528,22 @@ public class WindowManagerService extends IWindowManager.Stub // in the main app list, but still have windows shown. We put them // in the back because now that the animation is over we no longer // will care about them. - int NT = mExitingAppTokens.size(); + AppTokenList exitingAppTokens = displayContent.mExitingAppTokens; + int NT = exitingAppTokens.size(); for (int j=0; j<NT; j++) { - i = reAddAppWindowsLocked(displayContent, i, mExitingAppTokens.get(j)); + i = reAddAppWindowsLocked(displayContent, i, exitingAppTokens.get(j)); } // And add in the still active app tokens in Z order. - NT = mAnimatingAppTokens.size(); - for (int j=0; j<NT; j++) { - i = reAddAppWindowsLocked(displayContent, i, mAnimatingAppTokens.get(j)); + final ArrayList<Task> tasks = displayContent.getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + i = reAddAppWindowsLocked(displayContent, i, wtoken); + } } i -= lastBelow; @@ -7554,7 +7563,7 @@ public class WindowManagerService extends IWindowManager.Stub } } Slog.w(TAG, "Current app token list:"); - dumpAnimatingAppTokensLocked(); + dumpAppTokensLocked(); Slog.w(TAG, "Final window list:"); dumpWindowsLocked(); } @@ -8204,11 +8213,17 @@ public class WindowManagerService extends IWindowManager.Stub mAppTransition.setIdle(); // Restore window app tokens to the ActivityManager views - for (int i = mAnimatingAppTokens.size() - 1; i >= 0; i--) { - mAnimatingAppTokens.get(i).sendingToBottom = false; + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + final ArrayList<Task> tasks = displayContent.getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + wtoken.sendingToBottom = false; + } } - mAnimatingAppTokens.clear(); - mAnimatingAppTokens.addAll(mAppTokens); rebuildAppWindowListLocked(); changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; @@ -8365,21 +8380,25 @@ public class WindowManagerService extends IWindowManager.Stub } } - private void updateAllDrawnLocked() { + private void updateAllDrawnLocked(DisplayContent displayContent) { // See if any windows have been drawn, so they (and others // associated with them) can now be shown. - final ArrayList<AppWindowToken> appTokens = mAnimatingAppTokens; - final int NT = appTokens.size(); - for (int i=0; i<NT; i++) { - AppWindowToken wtoken = appTokens.get(i); - if (!wtoken.allDrawn) { - int numInteresting = wtoken.numInterestingWindows; - if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) { - if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, - "allDrawn: " + wtoken - + " interesting=" + numInteresting - + " drawn=" + wtoken.numDrawnWindows); - wtoken.allDrawn = true; + final ArrayList<Task> tasks = displayContent.getTasks(); + final int numTasks = tasks.size(); + for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) { + final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; + final int numTokens = tokens.size(); + for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + if (!wtoken.allDrawn) { + int numInteresting = wtoken.numInterestingWindows; + if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) { + if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, + "allDrawn: " + wtoken + + " interesting=" + numInteresting + + " drawn=" + wtoken.numDrawnWindows); + wtoken.allDrawn = true; + } } } } @@ -8403,13 +8422,17 @@ public class WindowManagerService extends IWindowManager.Stub } // Initialize state of exiting tokens. - for (i=mExitingTokens.size()-1; i>=0; i--) { - mExitingTokens.get(i).hasVisible = false; - } + DisplayContentsIterator iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + final DisplayContent displayContent = iterator.next(); + for (i=displayContent.mExitingTokens.size()-1; i>=0; i--) { + displayContent.mExitingTokens.get(i).hasVisible = false; + } - // Initialize state of exiting applications. - for (i=mExitingAppTokens.size()-1; i>=0; i--) { - mExitingAppTokens.get(i).hasVisible = false; + // Initialize state of exiting applications. + for (i=displayContent.mExitingAppTokens.size()-1; i>=0; i--) { + displayContent.mExitingAppTokens.get(i).hasVisible = false; + } } mInnerFields.mHoldScreen = null; @@ -8438,10 +8461,10 @@ public class WindowManagerService extends IWindowManager.Stub } boolean focusDisplayed = false; - boolean updateAllDrawn = false; - DisplayContentsIterator iterator = new DisplayContentsIterator(); + iterator = new DisplayContentsIterator(); while (iterator.hasNext()) { + boolean updateAllDrawn = false; final DisplayContent displayContent = iterator.next(); WindowList windows = displayContent.getWindowList(); DisplayInfo displayInfo = displayContent.getDisplayInfo(); @@ -8685,10 +8708,10 @@ public class WindowManagerService extends IWindowManager.Stub if (!mInnerFields.mDimming && mAnimator.isDimmingLocked(displayId)) { stopDimmingLocked(displayId); } - } - if (updateAllDrawn) { - updateAllDrawnLocked(); + if (updateAllDrawn) { + updateAllDrawnLocked(displayContent); + } } if (focusDisplayed) { @@ -8835,30 +8858,38 @@ public class WindowManagerService extends IWindowManager.Stub } // Time to remove any exiting tokens? - for (i=mExitingTokens.size()-1; i>=0; i--) { - WindowToken token = mExitingTokens.get(i); - if (!token.hasVisible) { - mExitingTokens.remove(i); - if (token.windowType == TYPE_WALLPAPER) { - mWallpaperTokens.remove(token); + iterator = new DisplayContentsIterator(); + while (iterator.hasNext()) { + final DisplayContent displayContent = iterator.next(); + ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens; + for (i = exitingTokens.size() - 1; i >= 0; i--) { + WindowToken token = exitingTokens.get(i); + if (!token.hasVisible) { + exitingTokens.remove(i); + if (token.windowType == TYPE_WALLPAPER) { + mWallpaperTokens.remove(token); + } } } - } - // Time to remove any exiting applications? - for (i=mExitingAppTokens.size()-1; i>=0; i--) { - AppWindowToken token = mExitingAppTokens.get(i); - if (!token.hasVisible && !mClosingApps.contains(token)) { - // Make sure there is no animation running on this token, - // so any windows associated with it will be removed as - // soon as their animations are complete - token.mAppAnimator.clearAnimation(); - token.mAppAnimator.animating = false; - if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, - "performLayout: App token exiting now removed" + token); - mAppTokens.remove(token); - mAnimatingAppTokens.remove(token); - mExitingAppTokens.remove(i); + // Time to remove any exiting applications? + AppTokenList exitingAppTokens = displayContent.mExitingAppTokens; + for (i = exitingAppTokens.size() - 1; i >= 0; i--) { + AppWindowToken token = exitingAppTokens.get(i); + if (!token.hasVisible && !mClosingApps.contains(token)) { + // Make sure there is no animation running on this token, + // so any windows associated with it will be removed as + // soon as their animations are complete + token.mAppAnimator.clearAnimation(); + token.mAppAnimator.animating = false; + if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, + "performLayout: App token exiting now removed" + token); + final Task task = mTaskIdToTask.get(token.groupId); + if (task != null && task.removeAppToken(token)) { + mTaskIdToTask.delete(token.groupId); + } + exitingAppTokens.remove(i); + } } } @@ -8878,7 +8909,7 @@ public class WindowManagerService extends IWindowManager.Stub defaultDisplay.layoutNeeded = true; } - DisplayContentsIterator iterator = new DisplayContentsIterator(); + iterator = new DisplayContentsIterator(); while (iterator.hasNext()) { DisplayContent displayContent = iterator.next(); if (displayContent.pendingLayoutChanges != 0) { @@ -9129,10 +9160,10 @@ public class WindowManagerService extends IWindowManager.Stub // window list to make sure we haven't left any dangling surfaces // around. - AllWindowsIterator iterator = new AllWindowsIterator(); + mTmpWindowsIterator.reset(FORWARD_ITERATOR); Slog.i(TAG, "Out of memory for surface! Looking for leaks..."); - while (iterator.hasNext()) { - WindowState ws = iterator.next(); + while (mTmpWindowsIterator.hasNext()) { + WindowState ws = mTmpWindowsIterator.next(); WindowStateAnimator wsa = ws.mWinAnimator; if (wsa.mSurfaceControl != null) { if (!mSessions.contains(wsa.mSession)) { @@ -9165,9 +9196,9 @@ public class WindowManagerService extends IWindowManager.Stub if (!leakedSurface) { Slog.w(TAG, "No leaked surfaces; killing applicatons!"); SparseIntArray pidCandidates = new SparseIntArray(); - iterator = new AllWindowsIterator(); - while (iterator.hasNext()) { - WindowState ws = iterator.next(); + mTmpWindowsIterator.reset(FORWARD_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + WindowState ws = mTmpWindowsIterator.next(); if (mForceRemoves.contains(ws)) { continue; } @@ -9293,8 +9324,20 @@ public class WindowManagerService extends IWindowManager.Stub } private WindowState findFocusedWindowLocked(DisplayContent displayContent) { - int nextAppIndex = mAppTokens.size()-1; - WindowToken nextApp = nextAppIndex >= 0 ? mAppTokens.get(nextAppIndex) : null; + // Set nextApp to the first app and set taskNdx and tokenNdx to point to the app following. + final ArrayList<Task> tasks = displayContent.getTasks(); + int taskNdx = tasks.size() - 1; + AppTokenList tokens = taskNdx >= 0 ? tasks.get(taskNdx).mAppTokens : null; + int tokenNdx = tokens != null ? tokens.size() - 1 : -1; + WindowToken nextApp = tokenNdx >= 0 ? tokens.get(tokenNdx) : null; + --tokenNdx; + if (tokenNdx < 0) { + --taskNdx; + if (taskNdx >= 0) { + tokens = tasks.get(taskNdx).mAppTokens; + tokenNdx = tokens.size() - 1; + } + } final WindowList windows = displayContent.getWindowList(); for (int i = windows.size() - 1; i >= 0; i--) { @@ -9310,8 +9353,8 @@ public class WindowManagerService extends IWindowManager.Stub // If this window's application has been removed, just skip it. if (thisApp != null && (thisApp.removed || thisApp.sendingToBottom)) { - if (DEBUG_FOCUS) Slog.v(TAG, "Skipping app because " + (thisApp.removed - ? "removed" : "sendingToBottom")); + if (DEBUG_FOCUS) Slog.v(TAG, "Skipping " + thisApp + " because " + + (thisApp.removed ? "removed" : "sendingToBottom")); continue; } @@ -9320,18 +9363,25 @@ public class WindowManagerService extends IWindowManager.Stub // through the app tokens until we find its app. if (thisApp != null && nextApp != null && thisApp != nextApp && win.mAttrs.type != TYPE_APPLICATION_STARTING) { - int origAppIndex = nextAppIndex; - while (nextAppIndex > 0) { - if (nextApp == mFocusedApp) { - // Whoops, we are below the focused app... no focus - // for you! - if (localLOGV || DEBUG_FOCUS) Slog.v( - TAG, "Reached focused app: " + mFocusedApp); - return null; - } - nextAppIndex--; - nextApp = mAppTokens.get(nextAppIndex); - if (nextApp == thisApp) { + final WindowToken origAppToken = nextApp; + final int origTaskNdx = taskNdx; + final int origTokenNdx = tokenNdx; + for ( ; taskNdx >= 0; --taskNdx) { + tokens = tasks.get(taskNdx).mAppTokens; + for ( ; tokenNdx >= 0; --tokenNdx) { + if (nextApp == mFocusedApp) { + // Whoops, we are below the focused app... no focus + // for you! + if (localLOGV || DEBUG_FOCUS) Slog.v( + TAG, "Reached focused app: " + mFocusedApp); + return null; + } + nextApp = tokens.get(tokenNdx); + if (nextApp == thisApp) { + break; + } + } + if (thisApp == nextApp) { break; } } @@ -9339,8 +9389,10 @@ public class WindowManagerService extends IWindowManager.Stub // Uh oh, the app token doesn't exist! This shouldn't // happen, but if it does we can get totally hosed... // so restart at the original app. - nextAppIndex = origAppIndex; - nextApp = mAppTokens.get(nextAppIndex); + nextApp = origAppToken; + // return indices to same place. + taskNdx = origTaskNdx; + tokenNdx = origTokenNdx; } } @@ -9710,15 +9762,6 @@ public class WindowManagerService extends IWindowManager.Stub } } } - if (mAppTokens.size() > 0) { - pw.println(); - pw.println(" Application tokens in Z order:"); - for (int i=mAppTokens.size()-1; i>=0; i--) { - pw.print(" App #"); pw.print(i); - pw.print(' '); pw.print(mAppTokens.get(i)); pw.println(":"); - mAppTokens.get(i).dump(pw, " "); - } - } if (mFinishedStarting.size() > 0) { pw.println(); pw.println(" Finishing start of application tokens:"); @@ -9734,51 +9777,6 @@ public class WindowManagerService extends IWindowManager.Stub } } } - if (mExitingTokens.size() > 0) { - pw.println(); - pw.println(" Exiting tokens:"); - for (int i=mExitingTokens.size()-1; i>=0; i--) { - WindowToken token = mExitingTokens.get(i); - pw.print(" Exiting #"); pw.print(i); - pw.print(' '); pw.print(token); - if (dumpAll) { - pw.println(':'); - token.dump(pw, " "); - } else { - pw.println(); - } - } - } - if (mExitingAppTokens.size() > 0) { - pw.println(); - pw.println(" Exiting application tokens:"); - for (int i=mExitingAppTokens.size()-1; i>=0; i--) { - WindowToken token = mExitingAppTokens.get(i); - pw.print(" Exiting App #"); pw.print(i); - pw.print(' '); pw.print(token); - if (dumpAll) { - pw.println(':'); - token.dump(pw, " "); - } else { - pw.println(); - } - } - } - if (mAppTransition.isRunning() && mAnimatingAppTokens.size() > 0) { - pw.println(); - pw.println(" Application tokens during animation:"); - for (int i=mAnimatingAppTokens.size()-1; i>=0; i--) { - WindowToken token = mAnimatingAppTokens.get(i); - pw.print(" App moving to bottom #"); pw.print(i); - pw.print(' '); pw.print(token); - if (dumpAll) { - pw.println(':'); - token.dump(pw, " "); - } else { - pw.println(); - } - } - } if (mOpeningApps.size() > 0 || mClosingApps.size() > 0) { pw.println(); if (mOpeningApps.size() > 0) { @@ -9823,9 +9821,9 @@ public class WindowManagerService extends IWindowManager.Stub void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll, ArrayList<WindowState> windows) { int j = 0; - final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR); - while (iterator.hasNext()) { - final WindowState w = iterator.next(); + mTmpWindowsIterator.reset(REVERSE_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + final WindowState w = mTmpWindowsIterator.next(); if (windows == null || windows.contains(w)) { pw.print(" Window #"); pw.print(j++); pw.print(' '); pw.print(w); pw.println(":"); @@ -10011,9 +10009,9 @@ public class WindowManagerService extends IWindowManager.Stub WindowList windows = new WindowList(); if ("visible".equals(name)) { synchronized(mWindowMap) { - final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR); - while (iterator.hasNext()) { - final WindowState w = iterator.next(); + mTmpWindowsIterator.reset(REVERSE_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + final WindowState w = mTmpWindowsIterator.next(); if (w.mWinAnimator.mSurfaceShown) { windows.add(w); } @@ -10028,9 +10026,9 @@ public class WindowManagerService extends IWindowManager.Stub } catch (RuntimeException e) { } synchronized(mWindowMap) { - final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR); - while (iterator.hasNext()) { - final WindowState w = iterator.next(); + mTmpWindowsIterator.reset(REVERSE_ITERATOR); + while (mTmpWindowsIterator.hasNext()) { + final WindowState w = mTmpWindowsIterator.next(); if (name != null) { if (w.mAttrs.getTitle().toString().contains(name)) { windows.add(w); @@ -10283,6 +10281,10 @@ public class WindowManagerService extends IWindowManager.Stub class DisplayContentsIterator implements Iterator<DisplayContent> { private int cur; + void reset() { + cur = 0; + } + @Override public boolean hasNext() { return cur < mDisplayContents.size(); @@ -10302,7 +10304,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - final static boolean REVERSE_ITERATOR = true; class AllWindowsIterator implements Iterator<WindowState> { private DisplayContent mDisplayContent; private DisplayContentsIterator mDisplayContentsIterator; @@ -10311,19 +10312,33 @@ public class WindowManagerService extends IWindowManager.Stub private boolean mReverse; AllWindowsIterator() { - mDisplayContentsIterator = new DisplayContentsIterator(); - mDisplayContent = mDisplayContentsIterator.next(); - mWindowList = mDisplayContent.getWindowList(); + this(false); } AllWindowsIterator(boolean reverse) { - this(); + mDisplayContentsIterator = new DisplayContentsIterator(); + reset(reverse); + } + + void reset(boolean reverse) { mReverse = reverse; - mWindowListIndex = reverse ? mWindowList.size() - 1 : 0; + mDisplayContentsIterator.reset(); + if (mDisplayContentsIterator.hasNext()) { + mDisplayContent = mDisplayContentsIterator.next(); + mWindowList = mDisplayContent.getWindowList(); + mWindowListIndex = reverse ? mWindowList.size() - 1 : 0; + } else { + mDisplayContent = null; + mWindowList = null; + mWindowListIndex = 0; + } } @Override public boolean hasNext() { + if (mDisplayContent == null) { + return false; + } if (mReverse) { return mWindowListIndex >= 0; } diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index 506fcec..e471e46 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -55,6 +55,12 @@ import java.io.PrintWriter; import java.util.ArrayList; class WindowList extends ArrayList<WindowState> { + WindowList() { + super(); + } + WindowList(WindowList windows) { + super(windows); + } } /** @@ -679,6 +685,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mDisplayContent.getDisplayId(); } + TaskStack getStack() { + if (mAppToken != null) { + return mService.mTaskIdToTask.get(mAppToken.groupId).mStack; + } + return null; + } + public long getInputDispatchingTimeoutNanos() { return mAppToken != null ? mAppToken.inputDispatchingTimeoutNanos @@ -1109,7 +1122,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { return true; } - public boolean setAppOpVisibilityLw(boolean state) { + public void setAppOpVisibilityLw(boolean state) { if (mAppOpVisibility != state) { mAppOpVisibility = state; if (state) { @@ -1119,13 +1132,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { // ops modifies they should only be hidden by policy due to the // lock screen, and the user won't be changing this if locked. // Plus it will quickly be fixed the next time we do a layout. - showLw(true, false); + showLw(true, true); } else { - hideLw(true, false); + hideLw(true, true); } - return true; } - return false; } @Override diff --git a/services/java/com/android/server/wm/WindowToken.java b/services/java/com/android/server/wm/WindowToken.java index bd0ace8..2267123 100644 --- a/services/java/com/android/server/wm/WindowToken.java +++ b/services/java/com/android/server/wm/WindowToken.java @@ -19,7 +19,6 @@ package com.android.server.wm; import android.os.IBinder; import java.io.PrintWriter; -import java.util.ArrayList; /** * Container of a set of related windows in the window manager. Often this |