summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/AppWidgetServiceImpl.java42
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java7
-rw-r--r--services/java/com/android/server/LockSettingsService.java26
-rw-r--r--services/java/com/android/server/SystemServer.java462
-rw-r--r--services/java/com/android/server/Watchdog.java6
-rw-r--r--services/java/com/android/server/accounts/AccountManagerService.java10
-rw-r--r--services/java/com/android/server/am/ActiveServices.java2
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java1576
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java103
-rw-r--r--services/java/com/android/server/am/ActivityStack.java3883
-rw-r--r--services/java/com/android/server/am/ActivityStackSupervisor.java1822
-rw-r--r--services/java/com/android/server/am/CompatModePackages.java29
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java2
-rw-r--r--services/java/com/android/server/am/PendingThumbnailsRecord.java4
-rw-r--r--services/java/com/android/server/am/TaskRecord.java189
-rw-r--r--services/java/com/android/server/am/UriPermission.java230
-rw-r--r--services/java/com/android/server/am/UriPermissionOwner.java32
-rw-r--r--services/java/com/android/server/content/ContentService.java5
-rw-r--r--services/java/com/android/server/pm/KeySetManager.java555
-rw-r--r--services/java/com/android/server/pm/PackageKeySetData.java125
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java41
-rw-r--r--services/java/com/android/server/pm/PackageSettingBase.java6
-rw-r--r--services/java/com/android/server/pm/Settings.java54
-rw-r--r--services/java/com/android/server/wm/AppWindowAnimator.java1
-rw-r--r--services/java/com/android/server/wm/AppWindowToken.java4
-rw-r--r--services/java/com/android/server/wm/DisplayContent.java197
-rw-r--r--services/java/com/android/server/wm/StackBox.java247
-rw-r--r--services/java/com/android/server/wm/Task.java52
-rw-r--r--services/java/com/android/server/wm/TaskGroup.java31
-rw-r--r--services/java/com/android/server/wm/TaskStack.java112
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java115
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java1560
-rw-r--r--services/java/com/android/server/wm/WindowState.java21
-rw-r--r--services/java/com/android/server/wm/WindowToken.java1
34 files changed, 7080 insertions, 4472 deletions
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index fb2828b..69ae846 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;
@@ -1654,7 +1657,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++) {
@@ -1723,6 +1726,7 @@ class AppWidgetServiceImpl {
@SuppressWarnings("unused")
void readStateFromFileLocked(FileInputStream stream) {
boolean success = false;
+ int version = 0;
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(stream, null);
@@ -1734,7 +1738,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");
@@ -1873,6 +1884,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.");
@@ -1886,6 +1899,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 a28c387..d19433e 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -792,10 +792,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/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a30fc3b..def65a8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -42,6 +42,7 @@ import android.util.Log;
import android.util.Slog;
import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.server.accessibility.AccessibilityManagerService;
@@ -196,8 +197,9 @@ class ServerThread extends Thread {
}
});
- // Critical services...
+ // bootstrap services
boolean onlyCore = false;
+ boolean firstBoot = false;
try {
// Wait for installd to finished starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
@@ -212,7 +214,21 @@ class ServerThread extends Thread {
Slog.i(TAG, "Activity Manager");
context = ActivityManagerService.main(factoryTest);
+ } catch (RuntimeException e) {
+ Slog.e("System", "******************************************");
+ Slog.e("System", "************ Failure starting bootstrap service", e);
+ }
+ boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
+ boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
+ boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
+ boolean disableTelephony = SystemProperties.getBoolean("config.disable_telephony", false);
+ boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
+ boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
+ boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
+ boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
+
+ try {
Slog.i(TAG, "Display Manager");
display = new DisplayManagerService(context, wmHandler, uiHandler);
ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
@@ -246,7 +262,6 @@ class ServerThread extends Thread {
pm = PackageManagerService.main(context, installer,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
- boolean firstBoot = false;
try {
firstBoot = pm.isFirstBoot();
} catch (RemoteException e) {
@@ -265,6 +280,7 @@ class ServerThread extends Thread {
// The AccountManager must come before the ContentService
try {
+ // TODO: seems like this should be disable-able, but req'd by ContentService
Slog.i(TAG, "Account Manager");
accountManager = new AccountManagerService(context);
ServiceManager.addService(Context.ACCOUNT_SERVICE, accountManager);
@@ -329,12 +345,13 @@ class ServerThread extends Thread {
Slog.i(TAG, "No Bluetooh Service (emulator)");
} else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
Slog.i(TAG, "No Bluetooth Service (factory test)");
+ } else if (disableBluetooth) {
+ Slog.i(TAG, "Bluetooth Service disabled by config");
} else {
Slog.i(TAG, "Bluetooth Manager Service");
bluetooth = new BluetoothManagerService(context);
ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
}
-
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
@@ -354,20 +371,23 @@ class ServerThread extends Thread {
// Bring up services needed for UI.
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- try {
- Slog.i(TAG, "Input Method Service");
- imm = new InputMethodManagerService(context, wm);
- ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
- } catch (Throwable e) {
- reportWtf("starting Input Manager Service", e);
- }
+ //if (!disableNonCoreServices) { // TODO: View depends on these; mock them?
+ if (true) {
+ try {
+ Slog.i(TAG, "Input Method Service");
+ imm = new InputMethodManagerService(context, wm);
+ ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
+ } catch (Throwable e) {
+ reportWtf("starting Input Manager Service", e);
+ }
- try {
- Slog.i(TAG, "Accessibility Manager");
- ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
- new AccessibilityManagerService(context));
- } catch (Throwable e) {
- reportWtf("starting Accessibility Manager", e);
+ try {
+ Slog.i(TAG, "Accessibility Manager");
+ ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
+ new AccessibilityManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting Accessibility Manager", e);
+ }
}
}
@@ -392,7 +412,8 @@ class ServerThread extends Thread {
}
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+ if (!disableStorage &&
+ !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
try {
/*
* NotificationManagerService is dependant on MountService,
@@ -406,116 +427,130 @@ class ServerThread extends Thread {
}
}
- try {
- Slog.i(TAG, "LockSettingsService");
- lockSettings = new LockSettingsService(context);
- ServiceManager.addService("lock_settings", lockSettings);
- } catch (Throwable e) {
- reportWtf("starting LockSettingsService service", e);
- }
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "LockSettingsService");
+ lockSettings = new LockSettingsService(context);
+ ServiceManager.addService("lock_settings", lockSettings);
+ } catch (Throwable e) {
+ reportWtf("starting LockSettingsService service", e);
+ }
- try {
- Slog.i(TAG, "Device Policy");
- devicePolicy = new DevicePolicyManagerService(context);
- ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
- } catch (Throwable e) {
- reportWtf("starting DevicePolicyService", e);
+ try {
+ Slog.i(TAG, "Device Policy");
+ devicePolicy = new DevicePolicyManagerService(context);
+ ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
+ } catch (Throwable e) {
+ reportWtf("starting DevicePolicyService", e);
+ }
}
- try {
- Slog.i(TAG, "Status Bar");
- statusBar = new StatusBarManagerService(context, wm);
- ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
- } catch (Throwable e) {
- reportWtf("starting StatusBarManagerService", e);
+ if (!disableSystemUI) {
+ try {
+ Slog.i(TAG, "Status Bar");
+ statusBar = new StatusBarManagerService(context, wm);
+ ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
+ } catch (Throwable e) {
+ reportWtf("starting StatusBarManagerService", e);
+ }
}
- try {
- Slog.i(TAG, "Clipboard Service");
- ServiceManager.addService(Context.CLIPBOARD_SERVICE,
- new ClipboardService(context));
- } catch (Throwable e) {
- reportWtf("starting Clipboard Service", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Clipboard Service");
+ ServiceManager.addService(Context.CLIPBOARD_SERVICE,
+ new ClipboardService(context));
+ } catch (Throwable e) {
+ reportWtf("starting Clipboard Service", e);
+ }
}
- try {
- Slog.i(TAG, "NetworkManagement Service");
- networkManagement = NetworkManagementService.create(context);
- ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
- } catch (Throwable e) {
- reportWtf("starting NetworkManagement Service", e);
+ if (!disableNetwork) {
+ try {
+ Slog.i(TAG, "NetworkManagement Service");
+ networkManagement = NetworkManagementService.create(context);
+ ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkManagement Service", e);
+ }
}
- try {
- Slog.i(TAG, "Text Service Manager Service");
- tsms = new TextServicesManagerService(context);
- ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
- } catch (Throwable e) {
- reportWtf("starting Text Service Manager Service", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Text Service Manager Service");
+ tsms = new TextServicesManagerService(context);
+ ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
+ } catch (Throwable e) {
+ reportWtf("starting Text Service Manager Service", e);
+ }
}
- try {
- Slog.i(TAG, "NetworkStats Service");
- networkStats = new NetworkStatsService(context, networkManagement, alarm);
- ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
- } catch (Throwable e) {
- reportWtf("starting NetworkStats Service", e);
- }
+ if (!disableNetwork) {
+ try {
+ Slog.i(TAG, "NetworkStats Service");
+ networkStats = new NetworkStatsService(context, networkManagement, alarm);
+ ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkStats Service", e);
+ }
- try {
- Slog.i(TAG, "NetworkPolicy Service");
- networkPolicy = new NetworkPolicyManagerService(
- context, ActivityManagerService.self(), power,
- networkStats, networkManagement);
- ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
- } catch (Throwable e) {
- reportWtf("starting NetworkPolicy Service", e);
- }
+ try {
+ Slog.i(TAG, "NetworkPolicy Service");
+ networkPolicy = new NetworkPolicyManagerService(
+ context, ActivityManagerService.self(), power,
+ networkStats, networkManagement);
+ ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkPolicy Service", e);
+ }
- try {
- Slog.i(TAG, "Wi-Fi P2pService");
- wifiP2p = new WifiP2pService(context);
- ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi P2pService", e);
- }
+ try {
+ Slog.i(TAG, "Wi-Fi P2pService");
+ wifiP2p = new WifiP2pService(context);
+ ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);
+ } catch (Throwable e) {
+ reportWtf("starting Wi-Fi P2pService", e);
+ }
- try {
- Slog.i(TAG, "Wi-Fi Service");
- wifi = new WifiService(context);
- ServiceManager.addService(Context.WIFI_SERVICE, wifi);
- } catch (Throwable e) {
- reportWtf("starting Wi-Fi Service", e);
- }
+ try {
+ Slog.i(TAG, "Wi-Fi Service");
+ wifi = new WifiService(context);
+ ServiceManager.addService(Context.WIFI_SERVICE, wifi);
+ } catch (Throwable e) {
+ reportWtf("starting Wi-Fi Service", e);
+ }
- try {
- Slog.i(TAG, "Connectivity Service");
- connectivity = new ConnectivityService(
- context, networkManagement, networkStats, networkPolicy);
- ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
- networkStats.bindConnectivityManager(connectivity);
- networkPolicy.bindConnectivityManager(connectivity);
- wifi.checkAndStartWifi();
- wifiP2p.connectivityServiceReady();
- } catch (Throwable e) {
- reportWtf("starting Connectivity Service", e);
- }
+ try {
+ Slog.i(TAG, "Connectivity Service");
+ connectivity = new ConnectivityService(
+ context, networkManagement, networkStats, networkPolicy);
+ ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
+ networkStats.bindConnectivityManager(connectivity);
+ networkPolicy.bindConnectivityManager(connectivity);
+ wifi.checkAndStartWifi();
+ wifiP2p.connectivityServiceReady();
+ } catch (Throwable e) {
+ reportWtf("starting Connectivity Service", e);
+ }
- try {
- Slog.i(TAG, "Network Service Discovery Service");
- serviceDiscovery = NsdService.create(context);
- ServiceManager.addService(
- Context.NSD_SERVICE, serviceDiscovery);
- } catch (Throwable e) {
- reportWtf("starting Service Discovery Service", e);
+ try {
+ Slog.i(TAG, "Network Service Discovery Service");
+ serviceDiscovery = NsdService.create(context);
+ ServiceManager.addService(
+ Context.NSD_SERVICE, serviceDiscovery);
+ } catch (Throwable e) {
+ reportWtf("starting Service Discovery Service", e);
+ }
}
- try {
- Slog.i(TAG, "UpdateLock Service");
- ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
- new UpdateLockService(context));
- } catch (Throwable e) {
- reportWtf("starting UpdateLockService", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "UpdateLock Service");
+ ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
+ new UpdateLockService(context));
+ } catch (Throwable e) {
+ reportWtf("starting UpdateLockService", e);
+ }
}
/*
@@ -558,28 +593,32 @@ class ServerThread extends Thread {
reportWtf("starting DeviceStorageMonitor service", e);
}
- try {
- Slog.i(TAG, "Location Manager");
- location = new LocationManagerService(context);
- ServiceManager.addService(Context.LOCATION_SERVICE, location);
- } catch (Throwable e) {
- reportWtf("starting Location Manager", e);
- }
+ if (!disableLocation) {
+ try {
+ Slog.i(TAG, "Location Manager");
+ location = new LocationManagerService(context);
+ ServiceManager.addService(Context.LOCATION_SERVICE, location);
+ } catch (Throwable e) {
+ reportWtf("starting Location Manager", e);
+ }
- try {
- Slog.i(TAG, "Country Detector");
- countryDetector = new CountryDetectorService(context);
- ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
- } catch (Throwable e) {
- reportWtf("starting Country Detector", e);
+ try {
+ Slog.i(TAG, "Country Detector");
+ countryDetector = new CountryDetectorService(context);
+ ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
+ } catch (Throwable e) {
+ reportWtf("starting Country Detector", e);
+ }
}
- try {
- Slog.i(TAG, "Search Service");
- ServiceManager.addService(Context.SEARCH_SERVICE,
- new SearchManagerService(context));
- } catch (Throwable e) {
- reportWtf("starting Search Service", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Search Service");
+ ServiceManager.addService(Context.SEARCH_SERVICE,
+ new SearchManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting Search Service", e);
+ }
}
try {
@@ -590,8 +629,8 @@ class ServerThread extends Thread {
reportWtf("starting DropBoxManagerService", e);
}
- if (context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableWallpaperService)) {
+ if (!disableNonCoreServices && context.getResources().getBoolean(
+ R.bool.config_enableWallpaperService)) {
try {
Slog.i(TAG, "Wallpaper Service");
if (!headless) {
@@ -603,7 +642,7 @@ class ServerThread extends Thread {
}
}
- if (!"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
+ if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
try {
Slog.i(TAG, "Audio Service");
ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
@@ -612,39 +651,45 @@ class ServerThread extends Thread {
}
}
- try {
- Slog.i(TAG, "Dock Observer");
- // Listen for dock station changes
- dock = new DockObserver(context);
- } catch (Throwable e) {
- reportWtf("starting DockObserver", e);
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Dock Observer");
+ // Listen for dock station changes
+ dock = new DockObserver(context);
+ } catch (Throwable e) {
+ reportWtf("starting DockObserver", e);
+ }
}
- try {
- Slog.i(TAG, "Wired Accessory Manager");
- // Listen for wired headset changes
- inputManager.setWiredAccessoryCallbacks(
- new WiredAccessoryManager(context, inputManager));
- } catch (Throwable e) {
- reportWtf("starting WiredAccessoryManager", e);
+ if (!disableMedia) {
+ try {
+ Slog.i(TAG, "Wired Accessory Manager");
+ // Listen for wired headset changes
+ inputManager.setWiredAccessoryCallbacks(
+ new WiredAccessoryManager(context, inputManager));
+ } catch (Throwable e) {
+ reportWtf("starting WiredAccessoryManager", e);
+ }
}
- try {
- Slog.i(TAG, "USB Service");
- // Manage USB host and device support
- usb = new UsbService(context);
- ServiceManager.addService(Context.USB_SERVICE, usb);
- } catch (Throwable e) {
- reportWtf("starting UsbService", e);
- }
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "USB Service");
+ // Manage USB host and device support
+ usb = new UsbService(context);
+ ServiceManager.addService(Context.USB_SERVICE, usb);
+ } catch (Throwable e) {
+ reportWtf("starting UsbService", e);
+ }
- try {
- Slog.i(TAG, "Serial Service");
- // Serial port support
- serial = new SerialService(context);
- ServiceManager.addService(Context.SERIAL_SERVICE, serial);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting SerialService", e);
+ try {
+ Slog.i(TAG, "Serial Service");
+ // Serial port support
+ serial = new SerialService(context);
+ ServiceManager.addService(Context.SERIAL_SERVICE, serial);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting SerialService", e);
+ }
}
try {
@@ -662,27 +707,29 @@ class ServerThread extends Thread {
reportWtf("starting UiModeManagerService", e);
}
- try {
- Slog.i(TAG, "Backup Service");
- ServiceManager.addService(Context.BACKUP_SERVICE,
- new BackupManagerService(context));
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting Backup Service", e);
- }
+ if (!disableNonCoreServices) {
+ try {
+ Slog.i(TAG, "Backup Service");
+ ServiceManager.addService(Context.BACKUP_SERVICE,
+ new BackupManagerService(context));
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Backup Service", e);
+ }
- try {
- Slog.i(TAG, "AppWidget Service");
- appWidget = new AppWidgetService(context);
- ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
- } catch (Throwable e) {
- reportWtf("starting AppWidget Service", e);
- }
+ try {
+ Slog.i(TAG, "AppWidget Service");
+ appWidget = new AppWidgetService(context);
+ ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
+ } catch (Throwable e) {
+ reportWtf("starting AppWidget Service", e);
+ }
- try {
- Slog.i(TAG, "Recognition Service");
- recognition = new RecognitionManagerService(context);
- } catch (Throwable e) {
- reportWtf("starting Recognition Service", e);
+ try {
+ Slog.i(TAG, "Recognition Service");
+ recognition = new RecognitionManagerService(context);
+ } catch (Throwable e) {
+ reportWtf("starting Recognition Service", e);
+ }
}
try {
@@ -704,30 +751,36 @@ class ServerThread extends Thread {
reportWtf("starting SamplingProfiler Service", e);
}
- try {
- Slog.i(TAG, "NetworkTimeUpdateService");
- networkTimeUpdater = new NetworkTimeUpdateService(context);
- } catch (Throwable e) {
- reportWtf("starting NetworkTimeUpdate service", e);
+ if (!disableNetwork) {
+ try {
+ Slog.i(TAG, "NetworkTimeUpdateService");
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkTimeUpdate service", e);
+ }
}
- try {
- Slog.i(TAG, "CommonTimeManagementService");
- commonTimeMgmtService = new CommonTimeManagementService(context);
- ServiceManager.addService("commontime_management", commonTimeMgmtService);
- } catch (Throwable e) {
- reportWtf("starting CommonTimeManagementService service", e);
+ if (!disableMedia) {
+ try {
+ Slog.i(TAG, "CommonTimeManagementService");
+ commonTimeMgmtService = new CommonTimeManagementService(context);
+ ServiceManager.addService("commontime_management", commonTimeMgmtService);
+ } catch (Throwable e) {
+ reportWtf("starting CommonTimeManagementService service", e);
+ }
}
- try {
- Slog.i(TAG, "CertBlacklister");
- CertBlacklister blacklister = new CertBlacklister(context);
- } catch (Throwable e) {
- reportWtf("starting CertBlacklister", e);
+ if (!disableNetwork) {
+ try {
+ Slog.i(TAG, "CertBlacklister");
+ CertBlacklister blacklister = new CertBlacklister(context);
+ } catch (Throwable e) {
+ reportWtf("starting CertBlacklister", e);
+ }
}
-
- if (context.getResources().getBoolean(
- com.android.internal.R.bool.config_dreamsSupported)) {
+
+ if (!disableNonCoreServices &&
+ context.getResources().getBoolean(R.bool.config_dreamsSupported)) {
try {
Slog.i(TAG, "Dreams Service");
// Dreams (interactive idle-time views, a/k/a screen savers)
@@ -768,10 +821,12 @@ class ServerThread extends Thread {
reportWtf("making Vibrator Service ready", e);
}
- try {
- lockSettings.systemReady();
- } catch (Throwable e) {
- reportWtf("making Lock Settings Service ready", e);
+ if (lockSettings != null) {
+ try {
+ lockSettings.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Lock Settings Service ready", e);
+ }
}
if (devicePolicy != null) {
@@ -867,7 +922,9 @@ class ServerThread extends Thread {
} catch (Throwable e) {
reportWtf("observing native crashes", e);
}
- if (!headless) startSystemUi(contextF);
+ if (!headless) {
+ startSystemUi(contextF);
+ }
try {
if (mountServiceF != null) mountServiceF.systemReady();
} catch (Throwable e) {
@@ -979,6 +1036,7 @@ class ServerThread extends Thread {
} catch (Throwable e) {
reportWtf("making InputManagerService ready", e);
}
+
try {
if (telephonyRegistryF != null) telephonyRegistryF.systemReady();
} catch (Throwable e) {
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 167e7af..de941f2 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -386,13 +386,11 @@ public class Watchdog extends Thread {
public void run() {
boolean waitedHalf = false;
while (true) {
- mCompleted = false;
- mHandler.sendEmptyMessage(MONITOR);
-
-
final String name;
synchronized (this) {
long timeout = TIME_TO_WAIT;
+ mCompleted = false;
+ mHandler.sendEmptyMessage(MONITOR);
// NOTE: We use uptimeMillis() here because we do not want to increment the time we
// wait while asleep. If the device is asleep then the thing that we are waiting
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index fd7cd78..55fe1c7 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -1504,7 +1504,10 @@ public class AccountManagerService
int userId) {
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
- && Binder.getCallingUid() != Process.myUid()) {
+ && Binder.getCallingUid() != 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);
}
@@ -1773,7 +1776,10 @@ public class AccountManagerService
int callingUid = Binder.getCallingUid();
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
- && callingUid != Process.myUid()) {
+ && callingUid != 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 bc1df85..7137a9d 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,12 +17,20 @@
package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import android.app.AppOpsManager;
import android.appwidget.AppWidgetManager;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessStats;
+import com.android.internal.util.FastXmlSerializer;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -33,12 +41,22 @@ import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.firewall.IntentFirewall;
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 com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
import dalvik.system.Zygote;
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
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;
@@ -125,6 +143,7 @@ import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.Time;
+import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
@@ -132,6 +151,7 @@ import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.Xml;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -166,7 +186,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-public final class ActivityManagerService extends ActivityManagerNative
+public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
private static final String USER_DATA_DIR = "/data/user/";
static final String TAG = "ActivityManager";
@@ -198,9 +218,9 @@ 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.
static final long BATTERY_STATS_TIME = 30*60*1000; // write battery stats every 30 minutes.
static final boolean MONITOR_CPU_USAGE = true;
@@ -210,14 +230,14 @@ public final class ActivityManagerService extends ActivityManagerNative
// 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;
-
+
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
// Maximum number of recent tasks that we can remember.
static final int MAX_RECENT_TASKS = 20;
-
+
// Amount of time after a call to stopAppSwitches() during which we will
// prevent further untrusted switches from happening.
static final long APP_SWITCH_DELAY_TIME = 5*1000;
@@ -270,10 +290,11 @@ 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;
+ /** Run all ActivityStacks through this */
+ ActivityStackSupervisor mStackSupervisor;
public IntentFirewall mIntentFirewall;
@@ -289,14 +310,22 @@ 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
= new ArrayList<PendingActivityLaunch>();
-
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
@@ -332,7 +361,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;
@@ -382,7 +411,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* The currently running heavy-weight process, if any.
*/
ProcessRecord mHeavyWeightProcess = null;
-
+
/**
* The last time that various processes have crashed.
*/
@@ -418,7 +447,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final SparseArray<ForegroundToken> mForegroundProcesses
= new SparseArray<ForegroundToken>();
-
+
/**
* List of records for processes that someone had tried to start before the
* system was ready. We don't start them at that point, but ensure they
@@ -460,7 +489,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* the "home" activity.
*/
ProcessRecord mHomeProcess;
-
+
/**
* This is the process holding the activity the user last visited that
* is in a different process from the one they are currently in.
@@ -505,11 +534,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
@@ -538,7 +562,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.
@@ -600,13 +625,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;
@@ -619,10 +639,27 @@ public final class ActivityManagerService extends ActivityManagerNative
= new ArrayList<ContentProviderRecord>();
/**
- * Global set of specific Uri permissions that have been granted.
+ * File storing persisted {@link #mGrantedUriPermissions}.
*/
- final private SparseArray<HashMap<Uri, UriPermission>> mGrantedUriPermissions
- = new SparseArray<HashMap<Uri, UriPermission>>();
+ private final AtomicFile mGrantFile;
+
+ /** XML constants used in {@link #mGrantFile} */
+ private static final String TAG_URI_GRANTS = "uri-grants";
+ private static final String TAG_URI_GRANT = "uri-grant";
+ private static final String ATTR_USER_HANDLE = "userHandle";
+ private static final String ATTR_SOURCE_PKG = "sourcePkg";
+ private static final String ATTR_TARGET_PKG = "targetPkg";
+ private static final String ATTR_URI = "uri";
+ private static final String ATTR_MODE_FLAGS = "modeFlags";
+
+ /**
+ * Global set of specific {@link Uri} permissions that have been granted.
+ * This optimized lookup structure maps from {@link UriPermission#targetUid}
+ * to {@link UriPermission#uri} to {@link UriPermission}.
+ */
+ @GuardedBy("this")
+ private final SparseArray<HashMap<Uri, UriPermission>>
+ mGrantedUriPermissions = new SparseArray<HashMap<Uri, UriPermission>>();
CoreSettingsObserver mCoreSettingsObserver;
@@ -647,12 +684,12 @@ public final class ActivityManagerService extends ActivityManagerNative
* any user id that can impact battery performance.
*/
final BatteryStatsService mBatteryStatsService;
-
+
/**
* Information about component usage
*/
final UsageStatsService mUsageStatsService;
-
+
/**
* Information about and control over application operations
*/
@@ -670,7 +707,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* configurations.
*/
int mConfigurationSeq = 0;
-
+
/**
* Hardware-reported OpenGLES version.
*/
@@ -686,7 +723,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* Temporary to avoid allocations. Protected by main lock.
*/
final StringBuilder mStringBuilder = new StringBuilder(256);
-
+
/**
* Used to control how we initialize the service.
*/
@@ -707,7 +744,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int mFactoryTest;
boolean mCheckedForSetup;
-
+
/**
* The time at which we will allow normal application switches again,
* after a call to {@link #stopAppSwitches()}.
@@ -719,7 +756,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* is set; any switches after that will clear the time.
*/
boolean mDidAppSwitch;
-
+
/**
* Last time (in realtime) at which we checked for power usage.
*/
@@ -752,14 +789,6 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean mShuttingDown = false;
/**
- * Task identifier that activities are currently being started
- * in. Incremented each time a new task is created.
- * todo: Replace this with a TokenSpace class that generates non-repeating
- * integers that won't wrap.
- */
- int mCurTask = 1;
-
- /**
* Current sequence id for oom_adj computation traversal.
*/
int mAdjSeq = 0;
@@ -793,13 +822,13 @@ public final class ActivityManagerService extends ActivityManagerNative
* N procs were started.
*/
int[] mProcDeaths = new int[20];
-
+
/**
* This is set if we had to do a delayed dexopt of an app before launching
* it, to increasing the ANR timeouts in that case.
*/
boolean mDidDexOpt;
-
+
String mDebugApp = null;
boolean mWaitForDebugger = false;
boolean mDebugTransient = false;
@@ -839,7 +868,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* protect all related state.
*/
final Thread mProcessStatsThread;
-
+
/**
* Used to collect process stats when showing not responding dialog.
* Protected by mProcessStatsThread.
@@ -871,7 +900,6 @@ public final class ActivityManagerService extends ActivityManagerNative
static ActivityThread mSystemThread;
private int mCurrentUserId = 0;
- private int[] mCurrentUserArray = new int[] { 0 };
private UserManagerService mUserManager;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -889,6 +917,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mAppThread = thread;
}
+ @Override
public void binderDied() {
if (localLOGV) Slog.v(
TAG, "Death received in " + this
@@ -927,6 +956,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int CONTINUE_USER_SWITCH_MSG = 35;
static final int USER_SWITCH_TIMEOUT_MSG = 36;
static final int IMMERSIVE_MODE_LOCK_MSG = 37;
+ static final int PERSIST_URI_GRANTS = 38;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -947,10 +977,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) {
@@ -985,18 +1016,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
-
+
ensureBootCompleted();
} 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);
return;
}
-
+
Intent intent = new Intent("android.intent.action.ANR");
if (!mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -1017,7 +1048,7 @@ public final class ActivityManagerService extends ActivityManagerNative
killAppAtUsersRequest(proc, null);
}
}
-
+
ensureBootCompleted();
} break;
case SHOW_STRICT_MODE_VIOLATION_MSG: {
@@ -1202,13 +1233,13 @@ public final class ActivityManagerService extends ActivityManagerNative
if (inm == null) {
return;
}
-
+
ActivityRecord root = (ActivityRecord)msg.obj;
ProcessRecord process = root.app;
if (process == null) {
return;
}
-
+
try {
Context context = mContext.createPackageContext(process.info.packageName, 0);
String text = mContext.getString(R.string.heavy_weight_notification,
@@ -1226,7 +1257,7 @@ public final class ActivityManagerService extends ActivityManagerNative
PendingIntent.getActivityAsUser(mContext, 0, root.intent,
PendingIntent.FLAG_CANCEL_CURRENT, null,
new UserHandle(root.userId)));
-
+
try {
int[] outId = new int[1];
inm.enqueueNotificationWithTag("android", "android", null,
@@ -1409,6 +1440,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
break;
}
+ case PERSIST_URI_GRANTS: {
+ writeGrantedUriPermissions();
+ break;
+ }
}
}
};
@@ -1430,7 +1465,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mSelf.mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS);
mSystemThread.installSystemApplicationInfo(info);
-
+
synchronized (mSelf) {
ProcessRecord app = mSelf.newProcessRecordLocked(
mSystemThread.getApplicationThread(), info,
@@ -1452,6 +1487,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
+ wm.createStack(HOME_STACK_ID, -1, StackBox.TASK_STACK_GOES_OVER, 1.0f);
}
public void startObservingNativeCrashes() {
@@ -1480,9 +1516,11 @@ 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.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
+ m.mStackSupervisor = new ActivityStackSupervisor(m, context, thr.mLooper);
+ m.mStackSupervisor.init(m.mCurrentUserId);
+
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
m.mAppOpsService.publish(context);
@@ -1493,14 +1531,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;
@@ -1510,6 +1548,7 @@ public final class ActivityManagerService extends ActivityManagerNative
super("ActivityManager");
}
+ @Override
public void run() {
Looper.prepare();
@@ -1630,7 +1669,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;
@@ -1653,6 +1692,9 @@ public final class ActivityManagerService extends ActivityManagerNative
mUsageStatsService = new UsageStatsService(new File(
systemDir, "usagestats").toString());
mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"));
+
+ mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
+
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
// User 0 is the first and only user that runs at boot.
@@ -1668,13 +1710,14 @@ public final class ActivityManagerService extends ActivityManagerNative
mConfigurationSeq = mConfiguration.seq = 1;
mProcessStats.init();
-
+
mCompatModePackages = new CompatModePackages(this, systemDir);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
mProcessStatsThread = new Thread("ProcessStats") {
+ @Override
public void run() {
while (true) {
try {
@@ -1793,7 +1836,7 @@ public final class ActivityManagerService extends ActivityManagerNative
(softIrq * 100) / total);
}
}
-
+
long[] cpuSpeedTimes = mProcessStats.getLastCpuSpeedTimes();
final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
synchronized(bstats) {
@@ -1843,7 +1886,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
-
+
@Override
public void batteryNeedsCpuUpdate() {
updateCpuStatsNow();
@@ -1899,16 +1942,23 @@ public final class ActivityManagerService extends ActivityManagerNative
mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
}
+ final void showAskCompatModeDialogLocked(ActivityRecord r) {
+ Message msg = Message.obtain();
+ msg.what = SHOW_COMPAT_MODE_DIALOG_MSG;
+ msg.obj = r.task.askedCompatMode ? null : r;
+ mHandler.sendMessage(msg);
+ }
+
private final void updateLruProcessInternalLocked(ProcessRecord app, int bestPos) {
// put it on the LRU to keep track of when it should be exited.
int lrui = mLruProcesses.indexOf(app);
if (lrui >= 0) mLruProcesses.remove(lrui);
-
+
int i = mLruProcesses.size()-1;
int skipTop = 0;
-
+
app.lruSeq = mLruSeq;
-
+
// compute the new weight for this process.
app.lastActivityTime = SystemClock.uptimeMillis();
if (app.activities.size() > 0) {
@@ -1946,7 +1996,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (i < 0) {
mLruProcesses.add(0, app);
}
-
+
// If the app is currently using a content provider or service,
// bump those processes as well.
if (app.connections.size() > 0) {
@@ -2004,14 +2054,14 @@ public final class ActivityManagerService extends ActivityManagerNative
} catch (RemoteException e) {
}
}
-
+
boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
return transit == AppTransition.TRANSIT_ACTIVITY_OPEN
|| transit == AppTransition.TRANSIT_TASK_OPEN
|| transit == AppTransition.TRANSIT_TASK_TO_FRONT;
}
-
+
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
@@ -2041,12 +2091,12 @@ public final class ActivityManagerService extends ActivityManagerNative
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName);
return app;
- } else {
- // An application record is attached to a previous process,
- // clean it up now.
- if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG, "App died: " + app);
- handleAppDiedLocked(app, true, true);
}
+
+ // An application record is attached to a previous process,
+ // clean it up now.
+ if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG, "App died: " + app);
+ handleAppDiedLocked(app, true, true);
}
String hostingNameStr = hostingName != null
@@ -2116,7 +2166,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean isAllowedWhileBooting(ApplicationInfo ai) {
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
-
+
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
if (app.pid > 0 && app.pid != MY_PID) {
@@ -2132,10 +2182,10 @@ public final class ActivityManagerService extends ActivityManagerNative
mProcessesOnHold.remove(app);
updateCpuStats();
-
+
System.arraycopy(mProcDeaths, 0, mProcDeaths, 1, mProcDeaths.length-1);
mProcDeaths[0] = 0;
-
+
try {
int uid = app.uid;
@@ -2218,16 +2268,16 @@ public final class ActivityManagerService extends ActivityManagerNative
app.batteryStats.incStartsLocked();
}
}
-
+
EventLog.writeEvent(EventLogTags.AM_PROC_START,
UserHandle.getUserId(uid), startResult.pid, uid,
app.processName, hostingType,
hostingNameStr != null ? hostingNameStr : "");
-
+
if (app.persistent) {
Watchdog.getInstance().processStarted(app.processName, startResult.pid);
}
-
+
StringBuilder buf = mStringBuilder;
buf.setLength(0);
buf.append("Start proc ");
@@ -2312,8 +2362,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,
- null, null, 0, 0, 0, null, 0, null, false, null);
+ mStackSupervisor.startHomeActivity(intent, aInfo);
}
}
@@ -2331,7 +2380,7 @@ public final class ActivityManagerService extends ActivityManagerNative
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags, userId);
-
+
if (info != null) {
ai = info.activityInfo;
}
@@ -2351,7 +2400,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mCheckedForSetup) {
return;
}
-
+
// We will show this screen if the current one is a different
// version than the last one shown, and we are not running in
// low-level factory test mode.
@@ -2360,12 +2409,12 @@ public final class ActivityManagerService extends ActivityManagerNative
Settings.Global.getInt(resolver,
Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
mCheckedForSetup = true;
-
+
// See if we should be showing the platform update setup UI.
Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
List<ResolveInfo> ris = mSelf.mContext.getPackageManager()
.queryIntentActivities(intent, PackageManager.GET_META_DATA);
-
+
// We don't allow third party apps to replace this.
ResolveInfo ri = null;
for (int i=0; ris != null && i<ris.size(); i++) {
@@ -2375,7 +2424,7 @@ public final class ActivityManagerService extends ActivityManagerNative
break;
}
}
-
+
if (ri != null) {
String vers = ri.activityInfo.metaData != null
? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
@@ -2390,13 +2439,13 @@ 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,
+ mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
null, null, 0, 0, 0, null, 0, null, false, null);
}
}
}
}
-
+
CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
}
@@ -2407,6 +2456,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
synchronized (this) {
@@ -2414,6 +2464,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public void setFrontActivityScreenCompatMode(int mode) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setFrontActivityScreenCompatMode");
@@ -2422,6 +2473,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public int getPackageScreenCompatMode(String packageName) {
enforceNotIsolatedCaller("getPackageScreenCompatMode");
synchronized (this) {
@@ -2429,6 +2481,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public void setPackageScreenCompatMode(String packageName, int mode) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setPackageScreenCompatMode");
@@ -2437,6 +2490,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public boolean getPackageAskScreenCompat(String packageName) {
enforceNotIsolatedCaller("getPackageAskScreenCompat");
synchronized (this) {
@@ -2444,6 +2498,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public void setPackageAskScreenCompat(String packageName, boolean ask) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setPackageAskScreenCompat");
@@ -2520,12 +2575,13 @@ 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);
+ mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, pal.startFlags,
+ doResume && i == (N-1), null);
}
mPendingActivityLaunches.clear();
}
+ @Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags,
@@ -2535,6 +2591,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,
@@ -2542,11 +2599,13 @@ 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,
+ // TODO: Switch to user app stacks here.
+ return mStackSupervisor.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,
@@ -2555,12 +2614,14 @@ 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,
+ // TODO: Switch to user app stacks here.
+ mStackSupervisor.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,
@@ -2568,12 +2629,14 @@ 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,
+ // TODO: Switch to user app stacks here.
+ int ret = mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
+ resolvedType, resultTo, resultWho, requestCode, startFlags,
null, null, null, config, options, userId);
return ret;
}
+ @Override
public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
@@ -2583,20 +2646,20 @@ public final class ActivityManagerService extends ActivityManagerNative
if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
-
+
IIntentSender sender = intent.getTarget();
if (!(sender instanceof PendingIntentRecord)) {
throw new IllegalArgumentException("Bad PendingIntent object");
}
-
+
PendingIntentRecord pir = (PendingIntentRecord)sender;
-
+
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 ==
- Binder.getCallingUid()) {
+ final ActivityStack stack = getTopStack();
+ if (stack.mResumedActivity != null &&
+ stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
}
}
@@ -2604,7 +2667,8 @@ public final class ActivityManagerService extends ActivityManagerNative
resultTo, resultWho, requestCode, flagsMask, flagsValues, options);
return ret;
}
-
+
+ @Override
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) {
// Refuse possible leaked file descriptors
@@ -2613,7 +2677,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;
@@ -2687,7 +2751,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final long origId = Binder.clearCallingIdentity();
- int res = mMainStack.startActivityLocked(r.app.thread, intent,
+ int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
r.resolvedType, aInfo, resultTo != null ? resultTo.appToken : null,
resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0,
options, false, null);
@@ -2708,19 +2772,22 @@ 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,
+ // TODO: Switch to user app stacks here.
+ int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
null, null, null, null, options, userId);
return ret;
}
+ @Override
public final int startActivities(IApplicationThread caller, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options,
int userId) {
enforceNotIsolatedCaller("startActivities");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, true, "startActivity", null);
- int ret = mMainStack.startActivities(caller, -1, callingPackage, intents,
+ // TODO: Switch to user app stacks here.
+ int ret = mStackSupervisor.startActivities(caller, -1, callingPackage, intents,
resolvedTypes, resultTo, options, userId);
return ret;
}
@@ -2731,7 +2798,8 @@ 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,
+ // TODO: Switch to user app stacks here.
+ int ret = mStackSupervisor.startActivities(null, uid, callingPackage, intents, resolvedTypes,
resultTo, options, userId);
return ret;
}
@@ -2764,31 +2832,31 @@ public final class ActivityManagerService extends ActivityManagerNative
mRecentTasks.add(0, task);
}
- public void setRequestedOrientation(IBinder token,
- int requestedOrientation) {
+ @Override
+ public void setRequestedOrientation(IBinder token, int requestedOrientation) {
synchronized (this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return;
}
final long origId = Binder.clearCallingIdentity();
mWindowManager.setAppOrientation(r.appToken, requestedOrientation);
Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
+ mConfiguration, r.mayFreezeScreenLocked(r.app) ? r.appToken : null);
if (config != null) {
r.frozenBeforeDestroy = true;
if (!updateConfigurationLocked(config, r, false, false)) {
- mMainStack.resumeTopActivityLocked(null);
+ r.task.stack.resumeTopActivityLocked(null);
}
}
Binder.restoreCallingIdentity(origId);
}
}
+ @Override
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;
}
@@ -2805,6 +2873,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) {
@@ -2812,9 +2881,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;
@@ -2830,13 +2903,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) {
@@ -2858,11 +2932,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);
}
}
@@ -2872,6 +2943,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public void crashApplication(int uid, int initialPid, String packageName,
String message) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
@@ -2930,44 +3002,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;
}
@@ -3000,21 +3077,10 @@ public final class ActivityManagerService extends ActivityManagerNative
clearProfilerLocked();
}
- // 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;
- }
-
- // Remove this application's activities from active lists.
- boolean hasVisibleActivities = mMainStack.removeHistoryRecordsForAppLocked(app);
+ mStackSupervisor.handleAppDiedLocked(app, restarting);
app.activities.clear();
-
+
if (app.instrumentationClass != null) {
Slog.w(TAG, "Crash of app " + app.processName
+ " running instrumentation " + app.instrumentationClass);
@@ -3022,19 +3088,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) {
@@ -3177,6 +3230,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Use a FileObserver to detect when traces finish writing.
// The order of traces is considered important to maintain for legibility.
FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) {
+ @Override
public synchronized void onEvent(int event, String path) { notify(); }
};
@@ -3341,7 +3395,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (MONITOR_CPU_USAGE) {
updateCpuStatsNow();
}
-
+
synchronized (this) {
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
if (mShuttingDown) {
@@ -3354,7 +3408,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
return;
}
-
+
// In case we come through here for the same app before completing
// this one, mark as anring now so we will bail out.
app.notResponding = true;
@@ -3365,11 +3419,11 @@ public final class ActivityManagerService extends ActivityManagerNative
// Dump thread traces as quickly as we can, starting with "interesting" processes.
firstPids.add(app.pid);
-
+
int parentPid = app.pid;
if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;
if (parentPid != app.pid) firstPids.add(parentPid);
-
+
if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
@@ -3452,16 +3506,16 @@ public final class ActivityManagerService extends ActivityManagerNative
Process.killProcessQuiet(app.pid);
return;
}
-
+
// Set the app's notResponding state, and look up the errorReportReceiver
makeAppNotRespondingLocked(app,
activity != null ? activity.shortComponentName : null,
annotation != null ? "ANR " + annotation : "ANR",
info.toString());
-
+
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
- HashMap map = new HashMap();
+ HashMap<String, Object> map = new HashMap<String, Object>();
msg.what = SHOW_NOT_RESPONDING_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
@@ -3469,7 +3523,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (activity != null) {
map.put("activity", activity);
}
-
+
mHandler.sendMessage(msg);
}
}
@@ -3497,7 +3551,8 @@ public final class ActivityManagerService extends ActivityManagerNative
});
}
}
-
+
+ @Override
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
@@ -3538,8 +3593,12 @@ public final class ActivityManagerService extends ActivityManagerNative
}
try {
- //clear application user data
+ // Clear application user data
pm.clearApplicationUserData(packageName, observer, userId);
+
+ // Remove all permissions granted from/to this package
+ removeUriPermissionsForPackageLocked(packageName, userId, true);
+
Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
Uri.fromParts("package", packageName, null));
intent.putExtra(Intent.EXTRA_UID, pkgUid);
@@ -3553,6 +3612,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return true;
}
+ @Override
public void killBackgroundProcesses(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED &&
@@ -3589,6 +3649,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public void killAllBackgroundProcesses() {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3631,6 +3692,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3679,6 +3741,7 @@ public final class ActivityManagerService extends ActivityManagerNative
/*
* The pkg name and app id have to be specified.
*/
+ @Override
public void killApplicationWithAppId(String pkg, int appid) {
if (pkg == null) {
return;
@@ -3703,6 +3766,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
@@ -3740,21 +3804,15 @@ 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);
- }
- }
+ mStackSupervisor.closeSystemDialogsLocked();
broadcastIntentLocked(null, null, intent, null,
null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, -1,
Process.SYSTEM_UID, UserHandle.USER_ALL);
}
- public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
- throws RemoteException {
+ @Override
+ public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids) {
enforceNotIsolatedCaller("getProcessMemoryInfo");
Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
for (int i=pids.length-1; i>=0; i--) {
@@ -3764,7 +3822,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return infos;
}
- public long[] getProcessPss(int[] pids) throws RemoteException {
+ @Override
+ public long[] getProcessPss(int[] pids) {
enforceNotIsolatedCaller("getProcessPss");
long[] pss = new long[pids.length];
for (int i=pids.length-1; i>=0; i--) {
@@ -3773,6 +3832,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return pss;
}
+ @Override
public void killApplicationProcess(String processName, int uid) {
if (processName == null) {
return;
@@ -3956,37 +4016,12 @@ 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)) {
- 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--;
- }
+
+ if (mStackSupervisor.forceStopPackageLocked(name, doit, evenPersistent, userId)) {
+ if (!doit) {
+ return true;
}
+ didSomething = true;
}
if (mServices.forceStopLocked(name, userId, evenPersistent, doit)) {
@@ -4014,6 +4049,9 @@ public final class ActivityManagerService extends ActivityManagerNative
removeDyingProviderLocked(null, providers.get(i), true);
}
+ // Remove transient permissions granted from/to this package/user
+ removeUriPermissionsForPackageLocked(name, userId, false);
+
if (name == null) {
// Remove pending intents. For now we only do this when force
// stopping users, because we have some problems when doing this
@@ -4074,11 +4112,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
if (mBooted) {
- mMainStack.resumeTopActivityLocked(null);
- mMainStack.scheduleIdleLocked();
+ mStackSupervisor.resumeTopActivityLocked();
+ mStackSupervisor.scheduleIdleLocked();
}
}
-
+
return didSomething;
}
@@ -4213,7 +4251,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (localLOGV) Slog.v(
TAG, "Binding process pid " + pid + " to record " + app);
- String processName = app.processName;
+ final String processName = app.processName;
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
@@ -4239,7 +4277,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
- List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
+ List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
if (!normalMode) {
Slog.i(TAG, "Launching preboot mode app: " + app);
@@ -4328,23 +4366,13 @@ 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);
- 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)) {
- didSomething = true;
- }
- } catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting activity "
- + hr.intent.getComponent().flattenToShortString(), e);
- badApp = true;
+ if (normalMode) {
+ try {
+ if (mStackSupervisor.attachApplicationLocked(app, mHeadless)) {
+ didSomething = true;
}
- } else {
- mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
+ } catch (Exception e) {
+ badApp = true;
}
}
@@ -4360,7 +4388,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
- didSomething = sendPendingBroadcastsLocked(app);
+ didSomething |= sendPendingBroadcastsLocked(app);
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
badApp = true;
@@ -4395,6 +4423,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return true;
}
+ @Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
@@ -4404,13 +4433,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) {
+ synchronized (this) {
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack != null) {
+ ActivityRecord r = stack.activityIdleInternalLocked(token, false, config);
+ if (stopProfiling) {
+ if ((mProfileProc == r.app) && (mProfileFd != null)) {
try {
mProfileFd.close();
} catch (IOException e) {
@@ -4433,11 +4464,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
public void showBootMessage(final CharSequence msg, final boolean always) {
enforceNotIsolatedCaller("showBootMessage");
mWindowManager.showBootMessage(msg, always);
}
+ @Override
public void dismissKeyguardOnNextActivity() {
enforceNotIsolatedCaller("dismissKeyguardOnNextActivity");
final long token = Binder.clearCallingIdentity();
@@ -4447,7 +4480,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mLockScreenShown = false;
comeOutOfSleepIfNeededLocked();
}
- mMainStack.dismissKeyguardOnNextActivityLocked();
+ mStackSupervisor.setDismissKeyguard(true);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -4533,18 +4566,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(
@@ -4560,9 +4606,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);
}
}
@@ -4575,9 +4621,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) {
@@ -4595,16 +4647,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;
}
@@ -4614,7 +4667,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;
}
@@ -4692,7 +4745,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,
@@ -4701,7 +4754,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;
}
@@ -5123,6 +5176,38 @@ public final class ActivityManagerService extends ActivityManagerNative
return readMet && writeMet;
}
+ private ProviderInfo getProviderInfoLocked(String authority, int userHandle) {
+ ProviderInfo pi = null;
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
+ if (cpr != null) {
+ pi = cpr.info;
+ } else {
+ try {
+ pi = AppGlobals.getPackageManager().resolveContentProvider(
+ authority, PackageManager.GET_URI_PERMISSION_PATTERNS, userHandle);
+ } catch (RemoteException ex) {
+ }
+ }
+ return pi;
+ }
+
+ private UriPermission findOrCreateUriPermissionLocked(
+ String sourcePkg, String targetPkg, int targetUid, Uri uri) {
+ HashMap<Uri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
+ if (targetUris == null) {
+ targetUris = Maps.newHashMap();
+ mGrantedUriPermissions.put(targetUid, targetUris);
+ }
+
+ UriPermission perm = targetUris.get(uri);
+ if (perm == null) {
+ perm = new UriPermission(sourcePkg, targetPkg, targetUid, uri);
+ targetUris.put(uri, perm);
+ }
+
+ return perm;
+ }
+
private final boolean checkUriPermissionLocked(Uri uri, int uid,
int modeFlags) {
// Root gets to do everything.
@@ -5169,6 +5254,7 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
Uri uri, int modeFlags, int lastTargetUid) {
+ final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -5189,20 +5275,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return -1;
}
- String name = uri.getAuthority();
- ProviderInfo pi = null;
- ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
- UserHandle.getUserId(callingUid));
- if (cpr != null) {
- pi = cpr.info;
- } else {
- try {
- pi = pm.resolveContentProvider(name,
- PackageManager.GET_URI_PERMISSION_PATTERNS,
- UserHandle.getUserId(callingUid));
- } catch (RemoteException ex) {
- }
- }
+ final String authority = uri.getAuthority();
+ final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid));
if (pi == null) {
Slog.w(TAG, "No content provider found for permission check: " + uri.toSafeString());
return -1;
@@ -5224,7 +5298,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (targetUid >= 0) {
// First... does the target actually need this permission?
- if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
+ if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags) && !persist) {
// No need to grant the target this permission.
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Target " + targetPkg + " already has full permission to " + uri);
@@ -5243,7 +5317,7 @@ public final class ActivityManagerService extends ActivityManagerNative
allowed = false;
}
}
- if (allowed) {
+ if (allowed && !persist) {
return -1;
}
}
@@ -5295,8 +5369,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
- Uri uri, int modeFlags, UriPermissionOwner owner) {
+ void grantUriPermissionUncheckedLocked(
+ int targetUid, String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+ final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -5309,32 +5384,20 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
"Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
-
- HashMap<Uri, UriPermission> targetUris
- = mGrantedUriPermissions.get(targetUid);
- if (targetUris == null) {
- targetUris = new HashMap<Uri, UriPermission>();
- mGrantedUriPermissions.put(targetUid, targetUris);
- }
- UriPermission perm = targetUris.get(uri);
- if (perm == null) {
- perm = new UriPermission(targetUid, uri);
- targetUris.put(uri, perm);
+ final String authority = uri.getAuthority();
+ final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(targetUid));
+ if (pi == null) {
+ Slog.w(TAG, "No content provider found for grant: " + uri.toSafeString());
+ return;
}
- perm.modeFlags |= modeFlags;
- if (owner == null) {
- perm.globalModeFlags |= modeFlags;
- } else {
- if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
- perm.readOwners.add(owner);
- owner.addReadPermission(perm);
- }
- if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
- perm.writeOwners.add(owner);
- owner.addWritePermission(perm);
- }
+ final UriPermission perm = findOrCreateUriPermissionLocked(
+ pi.packageName, targetPkg, targetUid, uri);
+ final boolean persistChanged = perm.grantModes(modeFlags, persist, owner);
+ if (persistChanged) {
+ mHandler.removeMessages(PERSIST_URI_GRANTS);
+ mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget();
}
}
@@ -5357,10 +5420,10 @@ public final class ActivityManagerService extends ActivityManagerNative
final int targetUid;
final int flags;
- NeededUriGrants(String _targetPkg, int _targetUid, int _flags) {
- targetPkg = _targetPkg;
- targetUid = _targetUid;
- flags = _flags;
+ NeededUriGrants(String targetPkg, int targetUid, int flags) {
+ this.targetPkg = targetPkg;
+ this.targetUid = targetUid;
+ this.flags = flags;
}
}
@@ -5388,11 +5451,11 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
if (data != null) {
- int target = checkGrantUriPermissionLocked(callingUid, targetPkg, data,
+ int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, data,
mode, needed != null ? needed.targetUid : -1);
- if (target > 0) {
+ if (targetUid > 0) {
if (needed == null) {
- needed = new NeededUriGrants(targetPkg, target, mode);
+ needed = new NeededUriGrants(targetPkg, targetUid, mode);
}
needed.add(data);
}
@@ -5401,12 +5464,12 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<clip.getItemCount(); i++) {
Uri uri = clip.getItemAt(i).getUri();
if (uri != null) {
- int target = -1;
- target = checkGrantUriPermissionLocked(callingUid, targetPkg, uri,
+ int targetUid = -1;
+ targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri,
mode, needed != null ? needed.targetUid : -1);
- if (target > 0) {
+ if (targetUid > 0) {
if (needed == null) {
- needed = new NeededUriGrants(targetPkg, target, mode);
+ needed = new NeededUriGrants(targetPkg, targetUid, mode);
}
needed.add(uri);
}
@@ -5476,44 +5539,25 @@ public final class ActivityManagerService extends ActivityManagerNative
if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION
|Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) {
HashMap<Uri, UriPermission> perms
- = mGrantedUriPermissions.get(perm.uid);
+ = mGrantedUriPermissions.get(perm.targetUid);
if (perms != null) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Removing " + perm.uid + " permission to " + perm.uri);
+ "Removing " + perm.targetUid + " permission to " + perm.uri);
perms.remove(perm.uri);
if (perms.size() == 0) {
- mGrantedUriPermissions.remove(perm.uid);
+ mGrantedUriPermissions.remove(perm.targetUid);
}
}
}
}
- private void revokeUriPermissionLocked(int callingUid, Uri uri,
- int modeFlags) {
- modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- if (modeFlags == 0) {
- return;
- }
+ private void revokeUriPermissionLocked(
+ int callingUid, Uri uri, int modeFlags, boolean persist) {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + uri);
- if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Revoking all granted permissions to " + uri);
-
final IPackageManager pm = AppGlobals.getPackageManager();
-
final String authority = uri.getAuthority();
- ProviderInfo pi = null;
- int userId = UserHandle.getUserId(callingUid);
- ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
- if (cpr != null) {
- pi = cpr.info;
- } else {
- try {
- pi = pm.resolveContentProvider(authority,
- PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
- } catch (RemoteException ex) {
- }
- }
+ final ProviderInfo pi = getProviderInfoLocked(authority, UserHandle.getUserId(callingUid));
if (pi == null) {
Slog.w(TAG, "No content provider found for permission revoke: " + uri.toSafeString());
return;
@@ -5529,6 +5573,8 @@ public final class ActivityManagerService extends ActivityManagerNative
//}
}
+ boolean persistChanged = false;
+
// Go through all of the permissions and remove any that match.
final List<String> SEGMENTS = uri.getPathSegments();
if (SEGMENTS != null) {
@@ -5558,8 +5604,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Revoking " + perm.uid + " permission to " + perm.uri);
- perm.clearModes(modeFlags);
+ "Revoking " + perm.targetUid + " permission to " + perm.uri);
+ persistChanged |= perm.clearModes(modeFlags, persist);
if (perm.modeFlags == 0) {
it.remove();
}
@@ -5572,6 +5618,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
+
+ if (persistChanged) {
+ mHandler.removeMessages(PERSIST_URI_GRANTS);
+ mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget();
+ }
}
public void revokeUriPermission(IApplicationThread caller, Uri uri,
@@ -5589,6 +5640,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
+ final boolean persist = (modeFlags & Intent.FLAG_PERSIST_GRANT_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (modeFlags == 0) {
@@ -5596,26 +5648,64 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final IPackageManager pm = AppGlobals.getPackageManager();
-
final String authority = uri.getAuthority();
- ProviderInfo pi = null;
- ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, r.userId);
- if (cpr != null) {
- pi = cpr.info;
- } else {
- try {
- pi = pm.resolveContentProvider(authority,
- PackageManager.GET_URI_PERMISSION_PATTERNS, r.userId);
- } catch (RemoteException ex) {
- }
- }
+ final ProviderInfo pi = getProviderInfoLocked(authority, r.userId);
if (pi == null) {
Slog.w(TAG, "No content provider found for permission revoke: "
+ uri.toSafeString());
return;
}
- revokeUriPermissionLocked(r.uid, uri, modeFlags);
+ revokeUriPermissionLocked(r.uid, uri, modeFlags, persist);
+ }
+ }
+
+ /**
+ * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the
+ * given package.
+ *
+ * @param packageName Package name to match, or {@code null} to apply to all
+ * packages.
+ * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
+ * to all users.
+ * @param persist If persistent grants should be removed.
+ */
+ private void removeUriPermissionsForPackageLocked(
+ String packageName, int userHandle, boolean persist) {
+ if (userHandle == UserHandle.USER_ALL && packageName == null) {
+ throw new IllegalArgumentException("Must narrow by either package or user");
+ }
+
+ boolean persistChanged = false;
+
+ final int size = mGrantedUriPermissions.size();
+ for (int i = 0; i < size; i++) {
+ // Only inspect grants matching user
+ if (userHandle == UserHandle.USER_ALL
+ || userHandle == UserHandle.getUserId(mGrantedUriPermissions.keyAt(i))) {
+ final Iterator<UriPermission> it = mGrantedUriPermissions.valueAt(i)
+ .values().iterator();
+ while (it.hasNext()) {
+ final UriPermission perm = it.next();
+
+ // Only inspect grants matching package
+ if (packageName == null || perm.sourcePkg.equals(packageName)
+ || perm.targetPkg.equals(packageName)) {
+ persistChanged |= perm.clearModes(~0, persist);
+
+ // Only remove when no modes remain; any persisted grants
+ // will keep this alive.
+ if (perm.modeFlags == 0) {
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+
+ if (persistChanged) {
+ mHandler.removeMessages(PERSIST_URI_GRANTS);
+ mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget();
}
}
@@ -5670,6 +5760,103 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ private void writeGrantedUriPermissions() {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG, "writeGrantedUriPermissions()");
+
+ // Snapshot permissions so we can persist without lock
+ ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
+ synchronized (this) {
+ final int size = mGrantedUriPermissions.size();
+ for (int i = 0 ; i < size; i++) {
+ for (UriPermission perm : mGrantedUriPermissions.valueAt(i).values()) {
+ if (perm.persistedModeFlags != 0) {
+ persist.add(perm.snapshot());
+ }
+ }
+ }
+ }
+
+ FileOutputStream fos = null;
+ try {
+ fos = mGrantFile.startWrite();
+
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, TAG_URI_GRANTS);
+ for (UriPermission.Snapshot perm : persist) {
+ out.startTag(null, TAG_URI_GRANT);
+ writeIntAttribute(out, ATTR_USER_HANDLE, perm.userHandle);
+ out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
+ out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
+ out.attribute(null, ATTR_URI, String.valueOf(perm.uri));
+ writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
+ out.endTag(null, TAG_URI_GRANT);
+ }
+ out.endTag(null, TAG_URI_GRANTS);
+ out.endDocument();
+
+ mGrantFile.finishWrite(fos);
+ } catch (IOException e) {
+ if (fos != null) {
+ mGrantFile.failWrite(fos);
+ }
+ }
+ }
+
+ private void readGrantedUriPermissionsLocked() {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG, "readGrantedUriPermissions()");
+
+ FileInputStream fis = null;
+ try {
+ fis = mGrantFile.openRead();
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(fis, null);
+
+ int type;
+ while ((type = in.next()) != END_DOCUMENT) {
+ final String tag = in.getName();
+ if (type == START_TAG) {
+ if (TAG_URI_GRANT.equals(tag)) {
+ final int userHandle = readIntAttribute(in, ATTR_USER_HANDLE);
+ final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
+ final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
+ final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
+ final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
+
+ // Sanity check that provider still belongs to source package
+ final ProviderInfo pi = getProviderInfoLocked(
+ uri.getAuthority(), userHandle);
+ if (pi != null && sourcePkg.equals(pi.packageName)) {
+ int targetUid = -1;
+ try {
+ targetUid = AppGlobals.getPackageManager()
+ .getPackageUid(targetPkg, userHandle);
+ } catch (RemoteException e) {
+ }
+ if (targetUid != -1) {
+ final UriPermission perm = findOrCreateUriPermissionLocked(
+ sourcePkg, targetPkg, targetUid, uri);
+ perm.grantModes(modeFlags, true, null);
+ }
+ } else {
+ Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
+ + " but instead found " + pi);
+ }
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Missing grants is okay
+ } catch (IOException e) {
+ Log.wtf(TAG, "Failed reading Uri grants", e);
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "Failed reading Uri grants", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ }
+
public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
synchronized (this) {
ProcessRecord app =
@@ -5704,12 +5891,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) {
@@ -5735,88 +5922,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 = mStackSupervisor.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);
@@ -5910,49 +6029,56 @@ 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;
+ return null;
}
+ @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);
}
@@ -5960,6 +6086,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) {
+ mRecentTasks.remove(tr);
+ mStackSupervisor.removeTask(tr);
final boolean killProcesses = (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0;
Intent baseIntent = new Intent(
tr.intent != null ? tr.intent : tr.affinityIntent);
@@ -6006,43 +6134,30 @@ 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) {
+ 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!
+ 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);
@@ -6051,46 +6166,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()");
@@ -6103,34 +6182,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = taskForIdLocked(task);
- if (tr != null) {
- 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(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);
- return;
- }
- }
+ mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -6138,21 +6190,25 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public void moveTaskToBack(int task) {
+ @Override
+ public void moveTaskToBack(int taskId) {
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(taskId);
+ 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(taskId, null);
+ Binder.restoreCallingIdentity(origId);
}
- final long origId = Binder.clearCallingIdentity();
- mMainStack.moveTaskToBackLocked(task, null);
- Binder.restoreCallingIdentity(origId);
}
}
@@ -6165,19 +6221,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()");
@@ -6197,27 +6255,33 @@ 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);
+ @Override
+ public int createStack(int relativeStackId, int position, float weight) {
+ synchronized (this) {
+ int stackId = mStackSupervisor.createStack(relativeStackId, position, weight);
+ mWindowManager.createStack(stackId, relativeStackId, position, weight);
+ return stackId;
}
}
- 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;
- }
- return -1;
- }
- lastTask = r.task;
+ @Override
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+ synchronized (this) {
+ mStackSupervisor.moveTaskToStack(taskId, stackId, toTop);
+ mWindowManager.moveTaskToStack(taskId, stackId, toTop);
}
+ }
- return -1;
+ @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);
+ }
}
// =========================================================
@@ -6241,7 +6305,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
if (r == null) {
- r = mMainStack.isInStackLocked(token);
+ r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return;
}
@@ -7125,13 +7189,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 {
+ getTopStack().unhandledBackLocked();
+ } finally {
Binder.restoreCallingIdentity(origId);
}
}
@@ -7172,7 +7233,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Actually is sleeping or shutting down or whatever else in the future
// is an inactive state.
- public boolean isSleeping() {
+ public boolean isSleepingOrShuttingDown() {
return mSleeping || mShuttingDown;
}
@@ -7189,7 +7250,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!mSleeping) {
mSleeping = true;
- mMainStack.stopIfSleepingLocked();
+ mStackSupervisor.goingToSleepLocked();
// Initialize the wake times of all processes.
checkExcessivePowerUsageLocked(false);
@@ -7200,42 +7261,26 @@ 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) {
- }
- }
- }
+ timedout = mStackSupervisor.shutdownLocked(timeout);
}
mAppOpsService.shutdown();
mUsageStatsService.shutdown();
mBatteryStatsService.shutdown();
-
+
return timedout;
}
@@ -7248,9 +7293,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);
}
}
@@ -7261,8 +7306,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!mWentToSleep && !mLockScreenShown) {
if (mSleeping) {
mSleeping = false;
- mMainStack.awakeFromSleepingLocked();
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.comeOutOfSleepIfNeededLocked();
}
}
}
@@ -7553,7 +7597,7 @@ public final class ActivityManagerService extends ActivityManagerNative
PendingActivityExtras pae;
Bundle extras = new Bundle();
synchronized (this) {
- ActivityRecord activity = mMainStack.mResumedActivity;
+ ActivityRecord activity = getTopStack().mResumedActivity;
if (activity == null) {
Slog.w(TAG, "getTopActivityExtras failed: no resumed activity");
return null;
@@ -7620,7 +7664,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();
}
@@ -7638,7 +7682,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();
}
@@ -7649,7 +7693,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("startActivity");
synchronized (this) {
- ActivityRecord r = mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = getTopStack().topRunningActivityLocked(null);
return (r != null) ? r.immersive : false;
}
}
@@ -8098,6 +8142,10 @@ public final class ActivityManagerService extends ActivityManagerNative
retrieveSettings();
+ synchronized (this) {
+ readGrantedUriPermissionsLocked();
+ }
+
if (goingCallback != null) goingCallback.run();
synchronized (this) {
@@ -8159,7 +8207,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} finally {
Binder.restoreCallingIdentity(ident);
}
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivityLocked();
sendUserSwitchBroadcastsLocked(-1, mCurrentUserId);
}
}
@@ -8254,15 +8302,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " 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);
- }
- }
+ mStackSupervisor.handleAppCrashLocked(app);
if (!app.persistent) {
// We don't want to start this process again until the user
// explicitly does so... but for persistent process, we really
@@ -8282,38 +8322,12 @@ 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);
+ mStackSupervisor.resumeTopActivityLocked();
return false;
}
- mMainStack.resumeTopActivityLocked(null);
+ mStackSupervisor.resumeTopActivityLocked();
} 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);
- }
- }
- }
- }
+ mStackSupervisor.finishTopRunningActivityLocked(app);
}
// Bump up the crash count of any services currently running in the proc.
@@ -8333,9 +8347,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 {
@@ -9339,50 +9353,14 @@ 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) {
- pw.println(" ");
- pw.println(" Activities waiting to finish:");
- dumpHistoryList(fd, pw, mMainStack.mFinishingActivities, " ", "Fin", false,
- !dumpAll, false, dumpPackage);
- }
+
+ mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, 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(" ");
+
+ mStackSupervisor.dump(pw, " ");
if (mRecentTasks.size() > 0) {
pw.println();
@@ -9404,12 +9382,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
-
- if (dumpAll) {
- pw.println(" ");
- pw.println(" mCurTask: " + mCurTask);
- }
-
+
return true;
}
@@ -9626,7 +9599,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
pw.println(" mConfiguration: " + mConfiguration);
if (dumpAll) {
- pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange);
+ pw.println(" mConfigWillChange: " + getTopStack().mConfigWillChange);
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
for (Map.Entry<String, Integer> entry
@@ -9686,8 +9659,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=" + getTopStack().mGoingToSleep);
+ pw.println(" mLaunchingActivity=" + getTopStack().mLaunchingActivity);
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
pw.println(" mNumNonHiddenProcs=" + mNumNonHiddenProcs
+ " mNumHiddenProcs=" + mNumHiddenProcs
@@ -9871,32 +9844,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 = mStackSupervisor.getDumpActivitiesLocked(name);
}
if (activities.size() <= 0) {
@@ -10148,75 +10099,6 @@ public final class ActivityManagerService extends ActivityManagerNative
return needSep;
}
- private 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;
- boolean needNL = false;
- final String innerPrefix = prefix + " ";
- final String[] args = new String[0];
- for (int i=list.size()-1; i>=0; i--) {
- final ActivityRecord r = (ActivityRecord)list.get(i);
- if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
- continue;
- }
- final boolean full = !brief && (complete || !r.isInHistory());
- if (needNL) {
- pw.println(" ");
- needNL = false;
- }
- if (lastTask != r.task) {
- lastTask = r.task;
- pw.print(prefix);
- pw.print(full ? "* " : " ");
- pw.println(lastTask);
- if (full) {
- lastTask.dump(pw, prefix + " ");
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- if (lastTask.intent != null) {
- pw.print(prefix); pw.print(" ");
- pw.println(lastTask.intent.toInsecureStringWithClip());
- }
- }
- }
- pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(r);
- if (full) {
- r.dump(pw, innerPrefix);
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
- if (r.app != null) {
- pw.print(innerPrefix); pw.println(r.app);
- }
- }
- if (client && r.app != null && r.app.thread != null) {
- // flush anything that is already in the PrintWriter since the thread is going
- // to write to the file descriptor directly
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
- r.appToken, innerPrefix, args);
- // Short timeout, since blocking here can
- // deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println(innerPrefix + "Failure while dumping the activity: " + e);
- } catch (RemoteException e) {
- pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
- }
- needNL = true;
- }
- }
- }
-
private static String buildOomTag(String prefix, String space, int val, int base) {
if (val == base) {
if (space == null) return prefix;
@@ -11938,6 +11820,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
mAppOpsService.packageRemoved(
intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
+
+ // Remove all permissions granted from/to this package
+ removeUriPermissionsForPackageLocked(ssp, userId, true);
}
}
}
@@ -12514,6 +12399,10 @@ public final class ActivityManagerService extends ActivityManagerNative
return config;
}
+ ActivityStack getTopStack() {
+ return mStackSupervisor.getTopStack();
+ }
+
public Configuration getConfiguration() {
Configuration ci;
synchronized(this) {
@@ -12575,9 +12464,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mHeadless) return true;
int changes = 0;
-
- boolean kept = true;
-
+
if (values != null) {
Configuration newConfig = new Configuration(mConfiguration);
changes = newConfig.updateFrom(values);
@@ -12655,21 +12542,23 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
-
+
+ boolean kept = true;
+ final ActivityStack mainStack = mStackSupervisor.getTopStack();
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);
+ starting = mainStack.topRunningActivityLocked(null);
}
if (starting != null) {
- kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
+ kept = mainStack.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);
+ mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
}
-
+
if (values != null && mWindowManager != null) {
mWindowManager.setNewConfiguration(mConfiguration);
}
@@ -12715,95 +12604,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;
}
}
@@ -13544,8 +13351,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
return !processingBroadcasts
- && (mSleeping || (mMainStack.mResumedActivity != null &&
- mMainStack.mResumedActivity.idle));
+ && (mSleeping || mStackSupervisor.allResumedActivitiesIdle());
}
/**
@@ -13829,14 +13635,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private final ActivityRecord resumedAppLocked() {
- ActivityRecord resumedActivity = mMainStack.mResumedActivity;
- if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = mMainStack.mPausingActivity;
- if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = mMainStack.topRunningActivityLocked(null);
- }
- }
- return resumedActivity;
+ return mStackSupervisor.resumedAppLocked();
}
final boolean updateOomAdjLocked(ProcessRecord app) {
@@ -14079,7 +13878,7 @@ 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");
+ mStackSupervisor.scheduleDestroyAllActivities(app, "trim");
}
}
}
@@ -14153,7 +13952,7 @@ 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");
+ mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");
}
}
@@ -14453,7 +14252,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);
@@ -14519,7 +14317,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- boolean haveActivities = mMainStack.switchUserLocked(userId, uss);
+ boolean haveActivities = mStackSupervisor.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..351d329 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.
@@ -128,15 +127,19 @@ final class ActivityRecord {
long lastLaunchTime; // time of last lauch of this activity
String stringName; // for caching of toString().
-
+
private boolean inHistory; // are we in the history stack?
+ final ActivityStackSupervisor mStackSupervisor;
+
+ /** Launch the home activity rather than the activity at the top of stack */
+ boolean mLaunchHomeTaskNext;
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
- pw.print(" launchedFromPackage="); pw.println(launchedFromPackage);
+ pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
pw.print(" userId="); pw.println(userId);
pw.print(prefix); pw.print("app="); pw.println(app);
pw.print(prefix); pw.println(intent.toInsecureStringWithClip());
@@ -182,7 +185,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");
@@ -269,28 +272,28 @@ final class ActivityRecord {
weakActivity = new WeakReference<ActivityRecord>(activity);
}
- @Override public void windowsDrawn() throws RemoteException {
+ @Override public void windowsDrawn() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsDrawn();
}
}
- @Override public void windowsVisible() throws RemoteException {
+ @Override public void windowsVisible() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsVisible();
}
}
- @Override public void windowsGone() throws RemoteException {
+ @Override public void windowsGone() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
activity.windowsGone();
}
}
- @Override public boolean keyDispatchingTimedOut() throws RemoteException {
+ @Override public boolean keyDispatchingTimedOut() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
return activity.keyDispatchingTimedOut();
@@ -298,7 +301,7 @@ final class ActivityRecord {
return false;
}
- @Override public long getKeyDispatchingTimeout() throws RemoteException {
+ @Override public long getKeyDispatchingTimeout() {
ActivityRecord activity = weakActivity.get();
if (activity != null) {
return activity.getKeyDispatchingTimeout();
@@ -306,6 +309,7 @@ final class ActivityRecord {
return 0;
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("Token{");
@@ -326,13 +330,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) {
+ boolean _componentSpecified, ActivityStackSupervisor supervisor) {
service = _service;
- stack = _stack;
appToken = new Token(this);
info = aInfo;
launchedFromUid = _launchedFromUid;
@@ -361,6 +364,7 @@ final class ActivityRecord {
thumbnailNeeded = false;
idle = false;
hasBeenLaunched = false;
+ mStackSupervisor = supervisor;
// This starts out true, since the initial state of an activity
// is that we have everything, and we shouldn't never consider it
@@ -468,6 +472,9 @@ final class ActivityRecord {
}
void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
+ if (task != null && task.removeActivity(this)) {
+ mStackSupervisor.removeTask(task);
+ }
if (inHistory && !finishing) {
if (task != null) {
task.numActivities--;
@@ -504,6 +511,7 @@ final class ActivityRecord {
inHistory = false;
if (task != null && !finishing) {
task.numActivities--;
+ task = null;
}
clearOptionsLocked();
}
@@ -525,6 +533,11 @@ final class ActivityRecord {
}
}
+ boolean isRootActivity() {
+ ArrayList<ActivityRecord> activities = task.mActivities;
+ return activities.size() == 0 || this == task.mActivities.get(0);
+ }
+
UriPermissionOwner getUriPermissionsLocked() {
if (uriPermissions == null) {
uriPermissions = new UriPermissionOwner(service, this);
@@ -538,7 +551,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 +576,7 @@ final class ActivityRecord {
void addNewIntentLocked(Intent intent) {
if (newIntents == null) {
- newIntents = new ArrayList();
+ newIntents = new ArrayList<Intent>();
}
newIntents.add(intent);
}
@@ -583,7 +596,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 +740,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 +752,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 +781,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 +817,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);
@@ -812,24 +828,23 @@ final class ActivityRecord {
// Instead of doing the full stop routine here, let's just
// hide any activities we now can, and let them stop when
// the normal idle happens.
- stack.processStoppingActivitiesLocked(false);
+ mStackSupervisor.processStoppingActivitiesLocked(false);
} else {
// If this activity was already idle, then we now need to
// make sure we perform the full stop of any activities
// that are waiting to do so. This is because we won't
// do that while they are still waiting for this one to
// become visible.
- final int N = stack.mWaitingVisibleActivities.size();
+ final int N = mStackSupervisor.mWaitingVisibleActivities.size();
if (N > 0) {
for (int i=0; i<N; i++) {
- ActivityRecord r = (ActivityRecord)
- stack.mWaitingVisibleActivities.get(i);
+ ActivityRecord r = mStackSupervisor.mWaitingVisibleActivities.get(i);
r.waitingVisible = false;
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG,
"Was waiting for visible: " + r);
}
- stack.mWaitingVisibleActivities.clear();
+ mStackSupervisor.mWaitingVisibleActivities.clear();
Message msg = Message.obtain();
msg.what = ActivityStack.IDLE_NOW_MSG;
stack.mHandler.sendMessage(msg);
@@ -851,6 +866,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 +916,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 +927,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 +969,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 0f1700d..568689f 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -16,33 +16,31 @@
package com.android.server.am;
-import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
-import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
+import com.android.internal.util.Objects;
+import com.android.server.am.ActivityManagerService.ItemMatcher;
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;
-import android.content.IIntentSender;
import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -54,17 +52,16 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
import android.view.Display;
-import java.io.IOException;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
@@ -78,21 +75,21 @@ final class ActivityStack {
static final boolean localLOGV = ActivityManagerService.localLOGV;
static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE;
- static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY;
+ static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY || true;
static final boolean DEBUG_USER_LEAVING = ActivityManagerService.DEBUG_USER_LEAVING;
static final boolean DEBUG_TRANSITION = ActivityManagerService.DEBUG_TRANSITION;
static final boolean DEBUG_RESULTS = ActivityManagerService.DEBUG_RESULTS;
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 DEBUG_STATES = ActivityStackSupervisor.DEBUG_STATES;
+ static final boolean DEBUG_ADD_REMOVE = ActivityStackSupervisor.DEBUG_ADD_REMOVE;
+ static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
+ static final boolean DEBUG_APP = ActivityStackSupervisor.DEBUG_APP;
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;
@@ -119,19 +116,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,20 +142,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.
@@ -168,22 +164,6 @@ final class ActivityStack {
final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<ActivityRecord>();
/**
- * List of activities that are waiting for a new activity
- * to become visible before completing whatever operation they are
- * supposed to do.
- */
- final ArrayList<ActivityRecord> mWaitingVisibleActivities
- = new ArrayList<ActivityRecord>();
-
- /**
- * List of activities that are ready to be stopped, but waiting
- * for the next activity to settle down before doing so. It contains
- * HistoryRecord objects.
- */
- final ArrayList<ActivityRecord> mStoppingActivities
- = new ArrayList<ActivityRecord>();
-
- /**
* List of activities that are in the process of going to sleep.
*/
final ArrayList<ActivityRecord> mGoingToSleepActivities
@@ -203,13 +183,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.
*/
@@ -251,50 +231,50 @@ 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.
*/
boolean mConfigWillChange;
- /**
- * Set to indicate whether to issue an onUserLeaving callback when a
- * 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.
*/
boolean mSleepTimeout = false;
/**
- * Dismiss the keyguard after the next activity is displayed?
- */
- boolean mDismissKeyguardOnNextActivity = false;
-
- /**
* Save the most recent screenshot for reuse. This keeps Recents from taking two identical
* screenshots, one for the Recents thumbnail and one for the pauseActivity thumbnail.
*/
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;
+
+ /** Run all ActivityStacks through this */
+ final ActivityStackSupervisor mStackSupervisor;
+
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;
@@ -332,7 +312,7 @@ final class ActivityStack {
switch (msg.what) {
case SLEEP_TIMEOUT_MSG: {
synchronized (mService) {
- if (mService.isSleeping()) {
+ if (mService.isSleepingOrShuttingDown()) {
Slog.w(TAG, "Sleep timeout! Sleeping now.");
mSleepTimeout = true;
checkReadyForSleepLocked();
@@ -349,9 +329,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) {
@@ -365,7 +345,9 @@ final class ActivityStack {
// so we need to be conservative and assume it isn't.
ActivityRecord r = (ActivityRecord)msg.obj;
Slog.w(TAG, "Activity idle timeout for " + r);
- activityIdleInternal(r != null ? r.appToken : null, true, null);
+ synchronized (mService) {
+ activityIdleInternalLocked(r != null ? r.appToken : null, true, null);
+ }
} break;
case LAUNCH_TICK_MSG: {
ActivityRecord r = (ActivityRecord)msg.obj;
@@ -381,11 +363,15 @@ 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;
- activityIdleInternal(r != null ? r.appToken : null, false, null);
+ synchronized (mService) {
+ activityIdleInternalLocked(r != null ? r.appToken : null, false, null);
+ }
} break;
case LAUNCH_TIMEOUT_MSG: {
if (mService.mDidDexOpt) {
@@ -403,7 +389,7 @@ final class ActivityStack {
} break;
case RESUME_TOP_ACTIVITY_MSG: {
synchronized (mService) {
- resumeTopActivityLocked(null);
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
}
} break;
case STOP_TIMEOUT_MSG: {
@@ -427,16 +413,27 @@ 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, Looper looper, int stackId,
+ ActivityStackSupervisor supervisor, int userId) {
mHandler = new ActivityStackHandler(looper);
mService = service;
mContext = context;
- mMainStack = mainStack;
PowerManager pm =
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
mLaunchingActivity.setReferenceCounted(false);
+ mStackId = stackId;
+ mStackSupervisor = supervisor;
+ mCurrentUser = userId;
}
private boolean okToShow(ActivityRecord r) {
@@ -445,25 +442,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;
}
@@ -471,88 +472,113 @@ 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 ActivityRecord topActivity() {
+ // Iterate to find the first non-empty task stack. Note that this code can
+ // be simplified 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) {
+ return activities.get(activityNdx);
+ }
+ }
+ return null;
}
- 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;
}
- private final boolean updateLRUListLocked(ActivityRecord r) {
+ final boolean updateLRUListLocked(ActivityRecord r) {
final boolean hadit = mLRUActivities.remove(r);
mLRUActivities.add(r);
return hadit;
}
+ final boolean isHomeStack() {
+ return mStackId == HOME_STACK_ID;
+ }
+
/**
* Returns the top activity in any existing task matching the given
* Intent. Returns null if no such task is found.
*/
- private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+ ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
ComponentName cls = intent.getComponent();
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;
}
}
@@ -564,18 +590,18 @@ final class ActivityStack {
* is the same as the given activity. Returns null if no such activity
* is found.
*/
- private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
+ ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
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);
@@ -587,227 +613,54 @@ final class ActivityStack {
return null;
}
- final void showAskCompatModeDialogLocked(ActivityRecord r) {
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.SHOW_COMPAT_MODE_DIALOG_MSG;
- msg.obj = r.task.askedCompatMode ? null : r;
- mService.mHandler.sendMessage(msg);
- }
-
/*
* Move the activities around in the stack to bring a user to the foreground.
* @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;
}
- final boolean realStartActivityLocked(ActivityRecord r,
- ProcessRecord app, boolean andResume, boolean checkConfig)
- throws RemoteException {
-
- r.startFreezingScreenLocked(app, 0);
- mService.mWindowManager.setAppVisibility(r.appToken, true);
-
- // schedule launch ticks to collect information about slow apps.
- r.startLaunchTickingLocked();
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order. Note that
- // as a result of this, it can call back into the activity
- // manager with a new orientation. We don't care about that,
- // because the activity is not currently running so we are
- // just restarting it anyway.
- if (checkConfig) {
- Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
- mService.mConfiguration,
- r.mayFreezeScreenLocked(app) ? r.appToken : null);
- mService.updateConfigurationLocked(config, r, false, false);
- }
-
- r.app = app;
- app.waitingToKill = null;
- r.launchCount++;
- r.lastLaunchTime = SystemClock.uptimeMillis();
-
- if (localLOGV) Slog.v(TAG, "Launching: " + r);
-
- int idx = app.activities.indexOf(r);
- if (idx < 0) {
- app.activities.add(r);
- }
- mService.updateLruProcessLocked(app, true);
-
- try {
- if (app.thread == null) {
- throw new RemoteException();
- }
- List<ResultInfo> results = null;
- List<Intent> newIntents = null;
- if (andResume) {
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
- + " icicle=" + r.icicle
- + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
- if (andResume) {
- EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
- r.userId, System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
- }
- if (r.isHomeActivity) {
- mService.mHomeProcess = app;
- }
- mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
- r.sleeping = false;
- r.forceNewConfig = false;
- showAskCompatModeDialogLocked(r);
- r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
- String profileFile = null;
- ParcelFileDescriptor profileFd = null;
- boolean profileAutoStop = false;
- if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
- if (mService.mProfileProc == null || mService.mProfileProc == app) {
- mService.mProfileProc = app;
- profileFile = mService.mProfileFile;
- profileFd = mService.mProfileFd;
- profileAutoStop = mService.mAutoStopProfiler;
- }
- }
- app.hasShownUi = true;
- app.pendingUiClean = true;
- if (profileFd != null) {
- try {
- profileFd = profileFd.dup();
- } catch (IOException e) {
- profileFd = null;
- }
- }
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
- System.identityHashCode(r), r.info,
- new Configuration(mService.mConfiguration),
- 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
- // process of the .apk, which is the only thing that will be
- // considered heavy-weight.
- if (app.processName.equals(app.info.packageName)) {
- if (mService.mHeavyWeightProcess != null
- && mService.mHeavyWeightProcess != app) {
- Log.w(TAG, "Starting new heavy weight process " + app
- + " when already running "
- + mService.mHeavyWeightProcess);
- }
- mService.mHeavyWeightProcess = app;
- Message msg = mService.mHandler.obtainMessage(
- ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
- msg.obj = r;
- mService.mHandler.sendMessage(msg);
- }
- }
-
- } catch (RemoteException e) {
- if (r.launchFailed) {
- // This is the second time we failed -- finish activity
- // and give up.
- Slog.e(TAG, "Second failure launching "
- + r.intent.getComponent().flattenToShortString()
- + ", giving up", e);
- mService.appDiedLocked(app, app.pid, app.thread);
- requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "2nd-crash", false);
- return false;
- }
-
- // This is the first time we failed -- restart process and
- // retry.
- app.activities.remove(r);
- throw e;
- }
-
- r.launchFailed = false;
- if (updateLRUListLocked(r)) {
- Slog.w(TAG, "Activity " + r
- + " being launched, but already in LRU list");
- }
-
- if (andResume) {
- // As part of the process of launching, ActivityThread also performs
- // a resume.
- r.state = ActivityState.RESUMED;
- if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + r
- + " (starting new instance)");
- r.stopped = false;
- mResumedActivity = r;
- r.task.touchActiveTime();
- if (mMainStack) {
- mService.addRecentTaskLocked(r.task);
- }
- completeResumeLocked(r);
- checkReadyForSleepLocked();
- if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle);
- } else {
- // This activity is not starting in the resumed state... which
- // should look like we asked it to pause+stop (but remain visible),
- // and it has done so and reported back the current icicle and
- // other state.
- if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r
- + " (starting in stopped state)");
- r.state = ActivityState.STOPPED;
- r.stopped = true;
- }
-
- // Launch the new version setup screen if needed. We do this -after-
- // launching the initial activity (that is, home), so that it can have
- // a chance to initialize itself while in the background, making the
- // switch back to it faster and look better.
- if (mMainStack) {
- mService.startSetupActivityLocked();
- }
-
- return true;
+ void minimalResumeActivityLocked(ActivityRecord r) {
+ r.state = ActivityState.RESUMED;
+ if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + r
+ + " (starting new instance)");
+ r.stopped = false;
+ mResumedActivity = r;
+ r.task.touchActiveTime();
+ mService.addRecentTaskLocked(r.task);
+ completeResumeLocked(r);
+ checkReadyForSleepLocked();
+ if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle);
}
- private final void startSpecificActivityLocked(ActivityRecord r,
- boolean andResume, boolean checkConfig) {
- // Is this activity's application already running?
- ProcessRecord app = mService.getProcessRecordLocked(r.processName,
- r.info.applicationInfo.uid);
-
+ void setLaunchTime(ActivityRecord r) {
if (r.launchTime == 0) {
r.launchTime = SystemClock.uptimeMillis();
if (mInitialStartTime == 0) {
@@ -816,27 +669,10 @@ final class ActivityStack {
} else if (mInitialStartTime == 0) {
mInitialStartTime = SystemClock.uptimeMillis();
}
-
- if (app != null && app.thread != null) {
- try {
- app.addPackage(r.info.packageName);
- realStartActivityLocked(r, app, andResume, checkConfig);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when starting activity "
- + r.intent.getComponent().flattenToShortString(), e);
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent(), false, false);
}
-
+
void stopIfSleepingLocked() {
- if (mService.isSleeping()) {
+ if (mService.isSleepingOrShuttingDown()) {
if (!mGoingToSleep.isHeld()) {
mGoingToSleep.acquire();
if (mLaunchingActivity.isHeld()) {
@@ -858,9 +694,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();
}
@@ -870,8 +708,9 @@ final class ActivityStack {
checkReadyForSleepLocked();
}
+ // Checked.
void checkReadyForSleepLocked() {
- if (!mService.isSleeping()) {
+ if (!mService.isSleepingOrShuttingDown()) {
// Do not care.
return;
}
@@ -890,10 +729,10 @@ final class ActivityStack {
return;
}
- if (mStoppingActivities.size() > 0) {
+ if (mStackSupervisor.mStoppingActivities.size() > 0) {
// Still need to tell some activities to stop; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop "
- + mStoppingActivities.size() + " activities");
+ + mStackSupervisor.mStoppingActivities.size() + " activities");
scheduleIdleLocked();
return;
}
@@ -902,10 +741,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);
+ }
}
}
@@ -931,7 +773,7 @@ final class ActivityStack {
if (who.noDisplay) {
return null;
}
-
+
Resources res = mService.mContext.getResources();
int w = mThumbnailWidth;
int h = mThumbnailHeight;
@@ -959,15 +801,14 @@ final class ActivityStack {
private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
if (mPausingActivity != null) {
- RuntimeException e = new RuntimeException();
Slog.e(TAG, "Trying to pause when pause is already pending for "
- + mPausingActivity, e);
+ + mPausingActivity, new RuntimeException("here").fillInStackTrace());
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
- RuntimeException e = new RuntimeException();
- Slog.e(TAG, "Trying to pause when nothing is resumed", e);
- resumeTopActivityLocked(null);
+ Slog.e(TAG, "Trying to pause when nothing is resumed",
+ new RuntimeException("here").fillInStackTrace());
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
return;
}
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
@@ -980,7 +821,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 {
@@ -989,7 +830,7 @@ final class ActivityStack {
prev.shortComponentName);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags);
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
mService.updateUsageStats(prev, false);
}
} catch (Exception e) {
@@ -1005,7 +846,7 @@ final class ActivityStack {
// If we are not going to sleep, we want to ensure the device is
// awake until the next activity is started.
- if (!mService.mSleeping && !mService.mShuttingDown) {
+ if (!mService.isSleepingOrShuttingDown()) {
mLaunchingActivity.acquire();
if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
// To be safe, don't allow the wake lock to be held for too long.
@@ -1014,7 +855,6 @@ final class ActivityStack {
}
}
-
if (mPausingActivity != null) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we're pausing the activity just because
@@ -1038,46 +878,34 @@ final class ActivityStack {
// This activity failed to schedule the
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
- resumeTopActivityLocked(null);
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
}
}
- 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)");
}
}
}
@@ -1108,7 +936,7 @@ final class ActivityStack {
} else {
if (r.configDestroy) {
destroyActivityLocked(r, true, false, "stop-config");
- resumeTopActivityLocked(null);
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
} else {
// Now that this process has stopped, we may want to consider
// it to be the previous app to try to keep around in case
@@ -1133,7 +961,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);
@@ -1142,7 +970,7 @@ final class ActivityStack {
if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
if (prev.waitingVisible) {
prev.waitingVisible = false;
- mWaitingVisibleActivities.remove(prev);
+ mStackSupervisor.mWaitingVisibleActivities.remove(prev);
if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
TAG, "Complete pause, no longer waiting: " + prev);
}
@@ -1155,8 +983,8 @@ final class ActivityStack {
if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
destroyActivityLocked(prev, true, false, "pause-config");
} else {
- mStoppingActivities.add(prev);
- if (mStoppingActivities.size() > 3) {
+ mStackSupervisor.mStoppingActivities.add(prev);
+ if (mStackSupervisor.mStoppingActivities.size() > 3) {
// If we already have a few activities waiting to stop,
// then give up on things going idle and start clearing
// them out.
@@ -1173,21 +1001,22 @@ final class ActivityStack {
mPausingActivity = null;
}
- if (!mService.isSleeping()) {
- resumeTopActivityLocked(prev);
+ final ActivityStack topStack = mStackSupervisor.getTopStack();
+ if (!mService.isSleepingOrShuttingDown()) {
+ topStack.resumeTopActivityLocked(prev);
} else {
checkReadyForSleepLocked();
- ActivityRecord top = topRunningActivityLocked(null);
+ ActivityRecord top = topStack.topRunningActivityLocked(null);
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run,
// do resume anyway to start something. Also if the top
// activity on the stack is not the just paused activity,
// we need to go ahead and resume it to ensure we complete
// an in-flight app switch.
- resumeTopActivityLocked(null);
+ topStack.resumeTopActivityLocked(null);
}
}
-
+
if (prev != null) {
prev.resumeKeyDispatchingLocked();
}
@@ -1214,6 +1043,7 @@ final class ActivityStack {
prev.cpuTimeAtResume = 0; // reset it
}
+ // Checked.
/**
* Once we know that we have asked an application to put an activity in
* the resumed state (either by launching it or explicitly telling it),
@@ -1239,17 +1069,17 @@ final class ActivityStack {
mHandler.sendMessage(msg);
}
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
+ // TODO: Should this be done for all stacks, not just mMainStack?
mService.reportResumedActivityLocked(next);
- }
-
- if (mMainStack) {
mService.setFocusedActivityLocked(next);
}
- next.resumeKeyDispatchingLocked();
- ensureActivitiesVisibleLocked(null, 0);
- mService.mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
+ if (mStackSupervisor.allResumedActivitiesComplete()) {
+ next.resumeKeyDispatchingLocked();
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ }
// Mark the point when the activity is resuming
// TODO: To be more accurate, the mark should be before the onCreate,
@@ -1263,6 +1093,7 @@ final class ActivityStack {
}
}
+ // Checked.
/**
* Make sure that all activities that need to be visible (that is, they
* currently can be seen by the user) actually are.
@@ -1275,117 +1106,108 @@ 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 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.
+ boolean aboveTop = true;
+ boolean behindFullscreen = !mStackSupervisor.isFrontStack(this);
+ int taskNdx;
+ for (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) {
+ mStackSupervisor.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.visible) {
+ if (r.fullscreen) {
+ // At this point, nothing else needs to be shown
if (DEBUG_VISBILITY) Slog.v(
- TAG, "Making invisible: " + r);
+ TAG, "Stopping: fullscreen at " + r);
+ behindFullscreen = true;
+ }
+ } else {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Make invisible? " + r + " finishing=" + r.finishing
+ + " state=" + r.state
+ + " behindFullscreen=" + behindFullscreen);
+ // Now for any activities that aren't visible to the user, make
+ // sure they no longer are keeping the screen frozen.
+ if (r.visible) {
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Making invisible: " + r);
r.visible = false;
try {
mService.mWindowManager.setAppVisibility(r.appToken, false);
if ((r.state == ActivityState.STOPPING
|| r.state == ActivityState.STOPPED)
&& r.app != null && r.app.thread != null) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Scheduling invisibility: " + r);
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Scheduling invisibility: " + r);
r.app.thread.scheduleWindowVisibility(r.appToken, false);
}
} catch (Exception e) {
@@ -1395,19 +1217,14 @@ final class ActivityStack {
+ r.intent.getComponent(), e);
}
} else {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Already invisible: " + r);
+ 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--;
}
}
+ // Checked.
/**
* Version of ensureActivitiesVisible that can easily be called anywhere.
*/
@@ -1418,7 +1235,7 @@ final class ActivityStack {
ensureActivitiesVisibleLocked(r, starting, null, configChanges);
}
}
-
+
/**
* Ensure that the top activity in the stack is resumed.
*
@@ -1432,28 +1249,28 @@ final class ActivityStack {
return resumeTopActivityLocked(prev, null);
}
+ // Checked.
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
// Find the first activity that is not finishing.
ActivityRecord next = topRunningActivityLocked(null);
// Remember how we'll process this pause/resume situation, and ensure
// that the state is reset however we wind up proceeding.
- final boolean userLeaving = mUserLeaving;
- mUserLeaving = false;
+ final boolean userLeaving = mStackSupervisor.mUserLeaving;
+ mStackSupervisor.mUserLeaving = false;
if (next == null) {
// There are no more activities! Let's just start up the
// Launcher...
- if (mMainStack) {
- ActivityOptions.abort(options);
- return mService.startHomeActivityLocked(mCurrentUser);
- }
+ ActivityOptions.abort(options);
+ return mService.startHomeActivityLocked(mCurrentUser);
}
next.delayedResume = false;
-
+
// If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
+ if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
+ mStackSupervisor.allResumedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
mService.mWindowManager.executeAppTransition();
@@ -1462,13 +1279,17 @@ final class ActivityStack {
return false;
}
+ if (prev != null && prev.mLaunchHomeTaskNext && prev.finishing &&
+ prev.task.getTopActivity() == null) {
+ prev.mLaunchHomeTaskNext = false;
+ return mService.startHomeActivityLocked(mCurrentUser);
+ }
+
// If we are sleeping, and there is no resumed activity, and the top
// activity is paused, well that is the state we want.
- if ((mService.mSleeping || mService.mShuttingDown)
+ if ((mService.isSleepingOrShuttingDown())
&& mLastPausedActivity == next
- && (next.state == ActivityState.PAUSED
- || next.state == ActivityState.STOPPED
- || next.state == ActivityState.STOPPING)) {
+ && mStackSupervisor.allPausedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
mService.mWindowManager.executeAppTransition();
@@ -1488,10 +1309,10 @@ final class ActivityStack {
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
- mStoppingActivities.remove(next);
+ mStackSupervisor.mStoppingActivities.remove(next);
mGoingToSleepActivities.remove(next);
next.sleeping = false;
- mWaitingVisibleActivities.remove(next);
+ mStackSupervisor.mWaitingVisibleActivities.remove(next);
next.updateOptionsLocked(options);
@@ -1533,10 +1354,11 @@ final class ActivityStack {
mLastStartedActivity = next;
}
}
-
+
// We need to start pausing the current activity so the top one
// can be resumed...
- if (mResumedActivity != null) {
+ final ActivityStack lastStack = mStackSupervisor.getLastStack();
+ if (lastStack.mResumedActivity != null) {
if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
// At this point we want to put the upcoming activity's process
// at the top of the LRU list, since we know we will be needing it
@@ -1547,7 +1369,7 @@ final class ActivityStack {
// happen whenever it needs to later.
mService.updateLruProcessLocked(next.app, false);
}
- startPausingLocked(userLeaving, false);
+ lastStack.startPausingLocked(userLeaving, false);
return true;
}
@@ -1569,7 +1391,7 @@ final class ActivityStack {
if (prev != null && prev != next) {
if (!prev.waitingVisible && next != null && !next.nowVisible) {
prev.waitingVisible = true;
- mWaitingVisibleActivities.add(prev);
+ mStackSupervisor.mWaitingVisibleActivities.add(prev);
if (DEBUG_SWITCH) Slog.v(
TAG, "Resuming top, waiting visible to hide: " + prev);
} else {
@@ -1642,7 +1464,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)) {
@@ -1669,35 +1491,32 @@ final class ActivityStack {
// schedule launch ticks to collect information about slow apps.
next.startLaunchTickingLocked();
- ActivityRecord lastResumedActivity = mResumedActivity;
+ ActivityRecord lastResumedActivity = lastStack.mResumedActivity;
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);
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order.
boolean updated = false;
- if (mMainStack) {
- synchronized (mService) {
- Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
- mService.mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- updated = mService.updateConfigurationLocked(config, next, false, false);
+ if (mStackSupervisor.isFrontStack(this)) {
+ Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
+ mService.mConfiguration,
+ next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
+ if (config != null) {
+ next.frozenBeforeDestroy = true;
}
+ updated = mService.updateConfigurationLocked(config, next, false, false);
}
+
if (!updated) {
// The configuration update wasn't able to keep the existing
// instance of the activity, and instead started a new one.
@@ -1712,18 +1531,21 @@ final class ActivityStack {
// Do over!
mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
}
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
mService.setFocusedActivityLocked(next);
}
- ensureActivitiesVisibleLocked(null, 0);
- mService.mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return true;
+ if (mStackSupervisor.allResumedActivitiesComplete()) {
+ ensureActivitiesVisibleLocked(null, 0);
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return true;
+ }
+ return false;
}
-
+
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) {
@@ -1741,13 +1563,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);
+ mService.showAskCompatModeDialogLocked(next);
next.app.pendingUiClean = true;
next.app.thread.scheduleResumeActivity(next.appToken,
mService.isNextTransitionForward());
-
+
checkReadyForSleepLocked();
} catch (Exception e) {
@@ -1755,12 +1577,12 @@ final class ActivityStack {
if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to "
+ lastState + ": " + next);
next.state = lastState;
- mResumedActivity = lastResumedActivity;
+ lastStack.mResumedActivity = lastResumedActivity;
Slog.i(TAG, "Restarting because process died: " + next);
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
} else {
- if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
+ if (SHOW_APP_STARTING_PREVIEW && mStackSupervisor.isFrontStack(lastStack)) {
mService.mWindowManager.setAppStartingWindow(
next.appToken, next.packageName, next.theme,
mService.compatibilityInfoForPackageLocked(
@@ -1770,7 +1592,7 @@ final class ActivityStack {
null, true);
}
}
- startSpecificActivityLocked(next, true, false);
+ mStackSupervisor.startSpecificActivityLocked(next, true, false);
return true;
}
@@ -1805,42 +1627,44 @@ final class ActivityStack {
}
if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
}
- startSpecificActivityLocked(next, true, true);
+ mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
return true;
}
- private final void startActivityLocked(ActivityRecord r, boolean newTask,
- boolean doResume, boolean keepCurTransition, Bundle options) {
- final int NH = mHistory.size();
- int addPos = -1;
-
+ final void startActivityLocked(ActivityRecord r, boolean newTask,
+ boolean doResume, boolean keepCurTransition, Bundle options) {
+ 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();
@@ -1849,8 +1673,7 @@ final class ActivityStack {
return;
}
break;
- }
- if (p.fullscreen) {
+ } else if (task.numFullscreen > 0) {
startIt = false;
}
}
@@ -1858,28 +1681,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) {
- mUserLeaving = false;
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false");
+ if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
+ mStackSupervisor.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 (!isHomeStack() || numActivities() > 0) {
// 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.
@@ -1904,8 +1725,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) {
@@ -1943,8 +1764,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);
}
@@ -1953,233 +1774,220 @@ final class ActivityStack {
}
if (doResume) {
- resumeTopActivityLocked(null);
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
}
}
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);
+ mService.mWindowManager.validateAppTokens(mStackId, mValidateAppTokens);
}
/**
* 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.
+ */
+ 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;
+ }
+ } 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.
+ target.setTask(p.task, p.thumbHolder, false);
+ if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ + " out to bottom task " + p.task);
+ } else {
+ target.setTask(createTaskRecord(mStackSupervisor.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);
}
- 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;
+
+ 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);
+ p.setTask(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.
@@ -2187,14 +1995,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
@@ -2205,1135 +2012,135 @@ 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);
+
+ 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);
p.setTask(task, null, false);
- mHistory.add(lastReparentPos, p);
- if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
- + " from " + srcPos + " to " + lastReparentPos
+ 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;
- }
-
- if (topOptions != null) {
- // If we got some ActivityOptions from an activity on top that
- // was removed from the task, propagate them to the new real top.
- if (taskTop != null) {
- taskTop.updateOptionsLocked(topOptions);
- } else {
- topOptions.abort();
- }
- }
-
- return taskTop;
- }
-
- /**
- * Perform clear operation as requested by
- * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
- * stack to the given task, then look for
- * an instance of that activity in the stack and, if found, finish all
- * activities on top of it and return the instance.
- *
- * @param newR Description of the new activity being started.
- * @return Returns the old activity that should be continued to be used,
- * or null if none was found.
- */
- private final ActivityRecord performClearTaskLocked(int taskId,
- 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);
- 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;
- }
- if (r.finishing) {
- continue;
- }
- ActivityOptions opts = r.takeOptionsLocked();
- if (opts != null) {
- ret.updateOptionsLocked(opts);
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear", false)) {
- i--;
- }
- }
-
- // 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) {
- if (!ret.finishing) {
- int index = indexOfTokenLocked(ret.appToken);
- if (index >= 0) {
- finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
- null, "clear", false);
- }
- return null;
- }
- }
-
- return ret;
- }
- }
-
- return null;
+ return taskInsertionPoint;
}
- /**
- * 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;
- }
- if (r.finishing) {
- i++;
- continue;
- }
- if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear", false)) {
- i++;
- }
- }
- }
-
- /**
- * 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;
- }
- }
- }
-
- /**
- * 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);
- if (candidate.finishing) {
- continue;
- }
- if (candidate.task.taskId != task) {
- break;
- }
- if (candidate.realActivity.equals(r.realActivity)) {
- return i;
- }
- }
-
- return -1;
- }
-
- /**
- * 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;
- newTop.frontOfTask = true;
- return newTop;
- }
-
- final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
- 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;
-
- if (caller != null) {
- callerApp = mService.getRecordForAppLocked(caller);
- if (callerApp != null) {
- callingPid = callerApp.pid;
- callingUid = callerApp.info.uid;
- } else {
- Slog.w(TAG, "Unable to find app for caller " + caller
- + " (pid=" + callingPid + ") when starting: "
- + intent.toString());
- err = ActivityManager.START_PERMISSION_DENIED;
- }
- }
-
- if (err == ActivityManager.START_SUCCESS) {
- final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
- Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
- + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
- }
-
- ActivityRecord sourceRecord = null;
- ActivityRecord resultRecord = null;
- if (resultTo != null) {
- int index = indexOfTokenLocked(resultTo);
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Will send result to " + resultTo + " (index " + index + ")");
- if (index >= 0) {
- sourceRecord = mHistory.get(index);
- if (requestCode >= 0 && !sourceRecord.finishing) {
- resultRecord = sourceRecord;
- }
- }
- }
-
- int launchFlags = intent.getFlags();
-
- if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
- && sourceRecord != null) {
- // Transfer the result target from the source activity to the new
- // one being started, including any failures.
- if (requestCode >= 0) {
- ActivityOptions.abort(options);
- return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
- }
- resultRecord = sourceRecord.resultTo;
- resultWho = sourceRecord.resultWho;
- requestCode = sourceRecord.requestCode;
- sourceRecord.resultTo = null;
- if (resultRecord != null) {
- resultRecord.removeResultsLocked(
- sourceRecord, resultWho, requestCode);
+ 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;
}
}
- if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
- // We couldn't find a class that can handle the given Intent.
- // That's the end of that!
- err = ActivityManager.START_INTENT_NOT_RESOLVED;
- }
+ final TaskRecord task = taskTop.task;
- if (err == ActivityManager.START_SUCCESS && aInfo == null) {
- // We couldn't find the specific class specified in the Intent.
- // Also the end of the line.
- err = ActivityManager.START_CLASS_NOT_FOUND;
- }
+ /** 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 (err != ActivityManager.START_SUCCESS) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- mDismissKeyguardOnNextActivity = false;
- ActivityOptions.abort(options);
- return err;
- }
-
- final int startAnyPerm = mService.checkPermission(
- START_ANY_ACTIVITY, callingPid, callingUid);
- final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
- callingUid, aInfo.applicationInfo.uid, aInfo.exported);
- if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- mDismissKeyguardOnNextActivity = false;
- String msg;
- if (!aInfo.exported) {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " not exported from uid " + aInfo.applicationInfo.uid;
- } else {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + aInfo.permission;
- }
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */
+ ActivityOptions topOptions = null;
- boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
- callerApp==null?null:callerApp.info, callingUid, callingPid, resolvedType, aInfo);
+ // Preserve the location for reparenting in the new task.
+ int reparentInsertionPoint = -1;
- if (mMainStack) {
- if (mService.mController != null) {
- try {
- // The Intent we give to the watcher has the extra data
- // stripped off, since it can contain private information.
- Intent watchIntent = intent.cloneFilter();
- abort |= !mService.mController.activityStarting(watchIntent,
- aInfo.applicationInfo.packageName);
- } catch (RemoteException e) {
- mService.mController = null;
- }
- }
- }
+ for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
+ final TaskRecord targetTask = mTaskHistory.get(i);
- if (abort) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- // We pretend to the caller that it was really started, but
- // they will just get a cancel result.
- mDismissKeyguardOnNextActivity = false;
- ActivityOptions.abort(options);
- return ActivityManager.START_SUCCESS;
- }
-
- ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid, callingPackage,
- intent, resolvedType, aInfo, mService.mConfiguration,
- resultRecord, resultWho, requestCode, componentSpecified);
- if (outActivity != null) {
- outActivity[0] = r;
- }
-
- if (mMainStack) {
- 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;
- 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
- // home (switches disabled, switch to home, mDidAppSwitch now true);
- // user taps a home icon (coming from home so allowed, we hit here
- // and now allow anyone to switch again).
- mService.mAppSwitchesAllowedTime = 0;
+ if (targetTask == task) {
+ topOptions = resetTargetTaskIfNeededLocked(task, forceReset);
+ taskFound = true;
} else {
- mService.mDidAppSwitch = true;
+ reparentInsertionPoint = resetAffinityTaskIfNeededLocked(targetTask, task,
+ taskFound, forceReset, reparentInsertionPoint);
}
-
- mService.doPendingActivityLaunchesLocked(false);
- }
-
- err = startActivityUncheckedLocked(r, sourceRecord,
- startFlags, true, options);
- if (mDismissKeyguardOnNextActivity && mPausingActivity == null) {
- // Someone asked to have the keyguard dismissed on the next
- // activity start, but we are not actually doing an activity
- // switch... just dismiss the keyguard now, because we
- // probably want to see whatever is behind it.
- mDismissKeyguardOnNextActivity = false;
- mService.mWindowManager.dismissKeyguard();
- }
- return err;
- }
-
- final void moveHomeToFrontFromLaunchLocked(int launchFlags) {
- if ((launchFlags &
- (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
- == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
- // Caller wants to appear on home activity, so before starting
- // their own activity we will bring home to the front.
- moveHomeToFrontLocked();
}
- }
- final int startActivityUncheckedLocked(ActivityRecord r,
- ActivityRecord sourceRecord, int startFlags, boolean doResume,
- Bundle options) {
- final Intent intent = r.intent;
- 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;
-
- // If the onlyIfNeeded flag is set, then we can do this if the activity
- // being launched is the same as the one making the call... or, as
- // a special case, if we do not know the caller then we count the
- // current top activity as the caller.
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- ActivityRecord checkedCaller = sourceRecord;
- if (checkedCaller == null) {
- checkedCaller = topRunningNonDelayedActivityLocked(notTop);
- }
- if (!checkedCaller.realActivity.equals(r.realActivity)) {
- // Caller is not the same as launcher, so always needed.
- startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
- }
- }
-
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
- + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // The original activity who is starting us is running as a single
- // instance... this new activity it is starting must go on its
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
-
- if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // For whatever reason this activity is being launched into a new
- // task... yet the caller has requested a result back. Well, that
- // is pretty messed up, so instead immediately send back a cancel
- // and let the new task continue launched as normal without a
- // dependency on its originator.
- Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- r.resultTo = null;
- }
+ int taskNdx = mTaskHistory.indexOf(task);
+ do {
+ taskTop = mTaskHistory.get(taskNdx--).getTopActivity();
+ } while (taskTop == null && taskNdx >= 0);
- boolean addingToTask = false;
- boolean movedHome = false;
- TaskRecord reuseTask = null;
- if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // If bring to front is requested, and no result is requested, and
- // we can find a task that was started with this same
- // component, then instead of launching bring that one to the front.
- if (r.resultTo == null) {
- // See if there is a task to bring to the front. If this is
- // a SINGLE_INSTANCE activity, there can be one and only one
- // instance of it in the history, and it is always in its own
- // unique task, so we do a special search.
- ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
- ? findTaskLocked(intent, r.info)
- : findActivityLocked(intent, r.info);
- if (taskTop != null) {
- if (taskTop.task.intent == null) {
- // This task was started because of movement of
- // the activity based on affinity... now that we
- // are actually launching it, we can assign the
- // base intent.
- taskTop.task.setIntent(intent, r.info);
- }
- // If the target task is not in the front, then we need
- // to bring it to the front... except... well, with
- // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
- // to have the same behavior as if a new instance was
- // being started, which means not bringing it to the front
- // if the caller is not itself in the front.
- ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
- if (curTop != null && curTop.task != taskTop.task) {
- r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- boolean callerAtFront = sourceRecord == null
- || curTop.task == sourceRecord.task;
- if (callerAtFront) {
- // We really do want to push this one into the
- // user's face, right now.
- movedHome = true;
- moveHomeToFrontFromLaunchLocked(launchFlags);
- moveTaskToFrontLocked(taskTop.task, r, options);
- options = null;
- }
- }
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- taskTop = resetTaskIfNeededLocked(taskTop, r);
- }
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it! And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null, options);
- } else {
- ActivityOptions.abort(options);
- }
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
- }
- if ((launchFlags &
- (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
- == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
- // The caller has requested to completely replace any
- // existing task with its new activity. Well that should
- // not be too hard...
- reuseTask = taskTop.task;
- performClearTaskLocked(taskTop.task.taskId);
- reuseTask.setIntent(r.intent, r.info);
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // In this situation we want to remove all activities
- // 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);
- if (top != null) {
- if (top.frontOfTask) {
- // Activity aliases may mean we use different
- // intents for the top activity, so make sure
- // the task now has the identity of the new
- // intent.
- top.task.setIntent(r.intent, r.info);
- }
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- top.deliverNewIntentLocked(callingUid, r.intent);
- } else {
- // A special case: we need to
- // start the activity because it is not currently
- // running, and the caller has asked to clear the
- // current task to have this activity at the top.
- addingToTask = true;
- // Now pretend like this activity is being started
- // by the top of its task, so it is put in the
- // right place.
- sourceRecord = taskTop;
- }
- } else if (r.realActivity.equals(taskTop.task.realActivity)) {
- // In this case the top activity on the task is the
- // same as the one being launched, so we take that
- // as a request to bring the task to the foreground.
- // If the top activity in the task is the root
- // activity, deliver this new intent to it if it
- // desires.
- if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
- && taskTop.realActivity.equals(r.realActivity)) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
- if (taskTop.frontOfTask) {
- taskTop.task.setIntent(r.intent, r.info);
- }
- taskTop.deliverNewIntentLocked(callingUid, r.intent);
- } else if (!r.intent.filterEquals(taskTop.task.intent)) {
- // In this case we are launching the root activity
- // of the task, but with a different intent. We
- // should start a new instance on top.
- addingToTask = true;
- sourceRecord = taskTop;
- }
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
- // In this case an activity is being launched in to an
- // existing task, without resetting that task. This
- // is typically the situation of launching an activity
- // from a notification or shortcut. We want to place
- // the new activity on top of the current task.
- addingToTask = true;
- sourceRecord = taskTop;
- } else if (!taskTop.task.rootWasReset) {
- // In this case we are launching in to an existing task
- // that has not yet been started from its front door.
- // The current task has been brought to the front.
- // Ideally, we'd probably like to place this new task
- // at the bottom of its stack, but that's a little hard
- // to do with the current organization of the code so
- // for now we'll just drop it.
- taskTop.task.setIntent(r.intent, r.info);
- }
- if (!addingToTask && reuseTask == null) {
- // We didn't do anything... but it was needed (a.k.a., client
- // don't use that intent!) And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null, options);
- } else {
- ActivityOptions.abort(options);
- }
- return ActivityManager.START_TASK_TO_FRONT;
- }
- }
- }
- }
-
- //String uri = r.intent.toURI();
- //Intent intent2 = new Intent(uri);
- //Slog.i(TAG, "Given intent: " + r.intent);
- //Slog.i(TAG, "URI is: " + uri);
- //Slog.i(TAG, "To intent: " + intent2);
-
- if (r.packageName != null) {
- // If the activity being launched is the same as the one currently
- // at the top, then we need to check if it should only be launched
- // once.
- ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
- if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
- if (top.app != null && top.app.thread != null) {
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- ActivityOptions.abort(options);
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it!
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
- }
- top.deliverNewIntentLocked(callingUid, r.intent);
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- }
- }
- }
-
- } else {
- if (r.resultTo != null) {
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- }
- ActivityOptions.abort(options);
- return ActivityManager.START_CLASS_NOT_FOUND;
- }
-
- boolean newTask = false;
- boolean keepCurTransition = false;
-
- // Should this be considered a new task?
- 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);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new task " + r.task);
+ if (topOptions != null) {
+ // If we got some ActivityOptions from an activity on top that
+ // was removed from the task, propagate them to the new real top.
+ if (taskTop != null) {
+ taskTop.updateOptionsLocked(topOptions);
} else {
- r.setTask(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);
- keepCurTransition = true;
- if (top != null) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- top.deliverNewIntentLocked(callingUid, r.intent);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- ActivityOptions.abort(options);
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- } else if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
- // 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);
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- top.updateOptionsLocked(options);
- top.deliverNewIntentLocked(callingUid, r.intent);
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
+ topOptions.abort();
}
- // 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);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in existing task " + r.task);
-
- } else {
- // 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
- ? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new guessed " + r.task);
- }
-
- mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- intent, r.getUriPermissionsLocked());
-
- if (newTask) {
- EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
- }
- logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
- startActivityLocked(r, newTask, doResume, keepCurTransition, options);
- return ActivityManager.START_SUCCESS;
- }
-
- ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
- String profileFile, ParcelFileDescriptor profileFd, int userId) {
- // Collect information about the target of the Intent.
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
}
- if (aInfo != null) {
- // Store the found target back into the intent, because now that
- // we have it we never want to do this again. For example, if the
- // user navigates back to this point in the history, we should
- // always restart the exact same activity.
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
-
- // Don't debug things in the system process
- if ((startFlags&ActivityManager.START_FLAG_DEBUG) != 0) {
- if (!aInfo.processName.equals("system")) {
- mService.setDebugApp(aInfo.processName, true, false);
- }
- }
-
- if ((startFlags&ActivityManager.START_FLAG_OPENGL_TRACES) != 0) {
- if (!aInfo.processName.equals("system")) {
- mService.setOpenGlTraceApp(aInfo.applicationInfo, aInfo.processName);
- }
- }
-
- if (profileFile != null) {
- if (!aInfo.processName.equals("system")) {
- mService.setProfileApp(aInfo.applicationInfo, aInfo.processName,
- profileFile, profileFd,
- (startFlags&ActivityManager.START_FLAG_AUTO_STOP_PROFILER) != 0);
- }
- }
- }
- return aInfo;
+ return taskTop;
}
- final int startActivityMayWait(IApplicationThread caller, int callingUid,
- String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, String profileFile,
- ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
- Bundle options, int userId) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
- // Collect information about the target of the Intent.
- ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
- profileFile, profileFd, userId);
-
- synchronized (mService) {
- int callingPid;
- if (callingUid >= 0) {
- callingPid = -1;
- } else if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
- } else {
- callingPid = callingUid = -1;
- }
-
- mConfigWillChange = config != null
- && mService.mConfiguration.diff(config) != 0;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Starting activity when config will change = " + mConfigWillChange);
-
- final long origId = Binder.clearCallingIdentity();
-
- if (mMainStack && aInfo != null &&
- (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
- // This may be a heavy-weight process! Check to see if we already
- // have another, different heavy-weight process running.
- if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
- if (mService.mHeavyWeightProcess != null &&
- (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
- !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
- int realCallingPid = callingPid;
- int realCallingUid = callingUid;
- if (caller != null) {
- ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
- if (callerApp != null) {
- realCallingPid = callerApp.pid;
- realCallingUid = callerApp.info.uid;
- } else {
- Slog.w(TAG, "Unable to find app for caller " + caller
- + " (pid=" + realCallingPid + ") when starting: "
- + intent.toString());
- ActivityOptions.abort(options);
- return ActivityManager.START_PERMISSION_DENIED;
- }
- }
-
- IIntentSender target = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, userId, null, null, 0, new Intent[] { intent },
- new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
- | PendingIntent.FLAG_ONE_SHOT, null);
-
- Intent newIntent = new Intent();
- if (requestCode >= 0) {
- // Caller is requesting a result.
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
- }
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
- new IntentSender(target));
- if (mService.mHeavyWeightProcess.activities.size() > 0) {
- ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
- hist.packageName);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
- hist.task.taskId);
- }
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
- aInfo.packageName);
- newIntent.setFlags(intent.getFlags());
- newIntent.setClassName("android",
- HeavyWeightSwitcherActivity.class.getName());
- intent = newIntent;
- resolvedType = null;
- caller = null;
- callingUid = Binder.getCallingUid();
- callingPid = Binder.getCallingPid();
- componentSpecified = true;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, null,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- aInfo = mService.getActivityInfoForUser(aInfo, userId);
- } catch (RemoteException e) {
- aInfo = null;
- }
- }
- }
- }
-
- int res = startActivityLocked(caller, intent, resolvedType,
- aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
- callingPackage, startFlags, options, componentSpecified, null);
-
- if (mConfigWillChange && mMainStack) {
- // If the caller also wants to switch to a new configuration,
- // do so now. This allows a clean switch, as we are waiting
- // for the current activity to pause (so we will not destroy
- // it), and have not yet started the next activity.
- mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
- "updateConfiguration()");
- mConfigWillChange = false;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Updating to new configuration after starting activity.");
- mService.updateConfigurationLocked(config, null, false, false);
- }
-
- Binder.restoreCallingIdentity(origId);
-
- if (outResult != null) {
- outResult.result = res;
- if (res == ActivityManager.START_SUCCESS) {
- mWaitingActivityLaunched.add(outResult);
- do {
- try {
- mService.wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- } else if (res == ActivityManager.START_TASK_TO_FRONT) {
- ActivityRecord r = this.topRunningActivityLocked(null);
- if (r.nowVisible) {
- outResult.timeout = false;
- outResult.who = new ComponentName(r.info.packageName, r.info.name);
- outResult.totalTime = 0;
- outResult.thisTime = 0;
- } else {
- outResult.thisTime = SystemClock.uptimeMillis();
- mWaitingActivityVisible.add(outResult);
- do {
- try {
- mService.wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- }
- }
+ /**
+ * 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.
+ */
+ 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;
}
-
- return res;
- }
- }
-
- final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options, int userId) {
- if (intents == null) {
- throw new NullPointerException("intents is null");
- }
- if (resolvedTypes == null) {
- throw new NullPointerException("resolvedTypes is null");
- }
- if (intents.length != resolvedTypes.length) {
- throw new IllegalArgumentException("intents are length different than resolvedTypes");
- }
-
- ActivityRecord[] outActivity = new ActivityRecord[1];
-
- int callingPid;
- if (callingUid >= 0) {
- callingPid = -1;
- } else if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
- } else {
- callingPid = callingUid = -1;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mService) {
-
- for (int i=0; i<intents.length; i++) {
- Intent intent = intents[i];
- if (intent == null) {
- continue;
- }
-
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
- // Collect information about the target of the Intent.
- ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i],
- 0, null, null, userId);
- // TODO: New, check if this is correct
- aInfo = mService.getActivityInfoForUser(aInfo, userId);
-
- if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
- & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
- throw new IllegalArgumentException(
- "FLAG_CANT_SAVE_STATE not supported here");
- }
-
- Bundle theseOptions;
- if (options != null && i == intents.length-1) {
- theseOptions = options;
- } else {
- theseOptions = null;
- }
- int res = startActivityLocked(caller, intent, resolvedTypes[i],
- aInfo, resultTo, null, -1, callingPid, callingUid, callingPackage,
- 0, theseOptions, componentSpecified, outActivity);
- if (res < 0) {
- return res;
- }
-
- resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
- }
+ if (candidate.realActivity.equals(realActivity)) {
+ return candidate;
}
- } finally {
- Binder.restoreCallingIdentity(origId);
}
-
- return ActivityManager.START_SUCCESS;
+ return null;
}
void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
@@ -3349,7 +2156,7 @@ final class ActivityStack {
}
mService.notifyAll();
}
-
+
void reportActivityVisibleLocked(ActivityRecord r) {
for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
WaitResult w = mWaitingActivityVisible.get(i);
@@ -3361,11 +2168,7 @@ final class ActivityStack {
w.thisTime = w.totalTime;
}
mService.notifyAll();
-
- if (mDismissKeyguardOnNextActivity) {
- mDismissKeyguardOnNextActivity = false;
- mService.mWindowManager.dismissKeyguard();
- }
+ mStackSupervisor.dismissKeyguard();
}
void sendActivityResultLocked(int callingUid, ActivityRecord r,
@@ -3413,7 +2216,7 @@ final class ActivityStack {
}
if (r.app != null && r.app.thread != null) {
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
if (mService.mFocusedActivity == r) {
mService.setFocusedActivityLocked(topRunningActivityLocked(null));
}
@@ -3430,7 +2233,7 @@ final class ActivityStack {
mService.mWindowManager.setAppVisibility(r.appToken, false);
}
r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
- if (mService.isSleeping()) {
+ if (mService.isSleepingOrShuttingDown()) {
r.setSleeping(true);
}
Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG);
@@ -3451,49 +2254,6 @@ final class ActivityStack {
}
}
}
-
- final ArrayList<ActivityRecord> processStoppingActivitiesLocked(
- boolean remove) {
- int N = mStoppingActivities.size();
- if (N <= 0) return null;
-
- ArrayList<ActivityRecord> stops = null;
-
- final boolean nowVisible = mResumedActivity != null
- && mResumedActivity.nowVisible
- && !mResumedActivity.waitingVisible;
- for (int i=0; i<N; i++) {
- ActivityRecord s = mStoppingActivities.get(i);
- if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
- + nowVisible + " waitingVisible=" + s.waitingVisible
- + " finishing=" + s.finishing);
- if (s.waitingVisible && nowVisible) {
- mWaitingVisibleActivities.remove(s);
- s.waitingVisible = false;
- if (s.finishing) {
- // If this activity is finishing, it is sitting on top of
- // everyone else but we now know it is no longer needed...
- // so get rid of it. Otherwise, we need to go through the
- // normal flow and hide it once we determine that it is
- // hidden by the activities in front of it.
- if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
- mService.mWindowManager.setAppVisibility(s.appToken, false);
- }
- }
- if ((!s.waitingVisible || mService.isSleeping()) && remove) {
- if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
- if (stops == null) {
- stops = new ArrayList<ActivityRecord>();
- }
- stops.add(s);
- mStoppingActivities.remove(i);
- N--;
- i--;
- }
- }
-
- return stops;
- }
final void scheduleIdleLocked() {
Message msg = Message.obtain();
@@ -3501,7 +2261,8 @@ final class ActivityStack {
mHandler.sendMessage(msg);
}
- final ActivityRecord activityIdleInternal(IBinder token, boolean fromTimeout,
+ // Checked.
+ final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
@@ -3509,139 +2270,139 @@ final class ActivityStack {
ArrayList<ActivityRecord> stops = null;
ArrayList<ActivityRecord> finishes = null;
- ArrayList<ActivityRecord> thumbnails = null;
ArrayList<UserStartedState> startingUsers = null;
int NS = 0;
int NF = 0;
- int NT = 0;
IApplicationThread sendThumbnail = null;
boolean booting = false;
boolean enableScreen = false;
boolean activityRemoved = false;
- synchronized (mService) {
- ActivityRecord r = ActivityRecord.forToken(token);
- if (r != null) {
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
- r.finishLaunchTickingLocked();
+ ActivityRecord r = ActivityRecord.forToken(token);
+ if (r != null) {
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+ r.finishLaunchTickingLocked();
+ }
+
+ // Get the activity record.
+ if (isInStackLocked(token) != null) {
+ res = r;
+
+ if (fromTimeout) {
+ reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
}
- // Get the activity record.
- int index = indexOfActivityLocked(r);
- if (index >= 0) {
- res = r;
+ // This is a hack to semi-deal with a race condition
+ // in the client where it can be constructed with a
+ // newer configuration from when we asked it to launch.
+ // We'll update with whatever configuration it now says
+ // it used to launch.
+ if (config != null) {
+ r.configuration = config;
+ }
- if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
- }
-
- // This is a hack to semi-deal with a race condition
- // in the client where it can be constructed with a
- // newer configuration from when we asked it to launch.
- // We'll update with whatever configuration it now says
- // it used to launch.
- if (config != null) {
- r.configuration = config;
- }
-
- // No longer need to keep the device awake.
- if (mResumedActivity == r && mLaunchingActivity.isHeld()) {
- mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- mLaunchingActivity.release();
- }
+ // No longer need to keep the device awake.
+ if (mResumedActivity == r && mLaunchingActivity.isHeld()) {
+ mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ mLaunchingActivity.release();
+ }
- // We are now idle. If someone is waiting for a thumbnail from
- // us, we can now deliver.
- r.idle = true;
+ // We are now idle. If someone is waiting for a thumbnail from
+ // us, we can now deliver.
+ r.idle = true;
+ if (mStackSupervisor.allResumedActivitiesIdle()) {
mService.scheduleAppGcsLocked();
- if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
- sendThumbnail = r.app.thread;
- r.thumbnailNeeded = false;
- }
+ }
+ if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
+ sendThumbnail = r.app.thread;
+ r.thumbnailNeeded = false;
+ }
- // If this activity is fullscreen, set up to hide those under it.
+ // If this activity is fullscreen, set up to hide those under it.
- if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
- ensureActivitiesVisibleLocked(null, 0);
+ if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
+ ensureActivitiesVisibleLocked(null, 0);
- //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (mMainStack) {
- if (!mService.mBooted) {
- mService.mBooted = true;
- enableScreen = true;
- }
- }
-
- } else if (fromTimeout) {
- reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
+ //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
+ if (!mService.mBooted && mStackSupervisor.isFrontStack(this)) {
+ mService.mBooted = true;
+ enableScreen = true;
}
+ } else if (fromTimeout) {
+ reportActivityLaunchedLocked(fromTimeout, null, -1, -1);
+ }
- // Atomically retrieve all of the other things to do.
- stops = processStoppingActivitiesLocked(true);
- NS = stops != null ? stops.size() : 0;
- if ((NF=mFinishingActivities.size()) > 0) {
- finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
- mFinishingActivities.clear();
- }
- if ((NT=mService.mCancelledThumbnails.size()) > 0) {
- thumbnails = new ArrayList<ActivityRecord>(mService.mCancelledThumbnails);
- mService.mCancelledThumbnails.clear();
- }
+ // Atomically retrieve all of the other things to do.
+ stops = mStackSupervisor.processStoppingActivitiesLocked(true);
+ NS = stops != null ? stops.size() : 0;
+ if ((NF=mFinishingActivities.size()) > 0) {
+ finishes = new ArrayList<ActivityRecord>(mFinishingActivities);
+ mFinishingActivities.clear();
+ }
- if (mMainStack) {
- booting = mService.mBooting;
- mService.mBooting = false;
- }
- if (mStartingUsers.size() > 0) {
- startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
- mStartingUsers.clear();
- }
+ final ArrayList<ActivityRecord> thumbnails;
+ final int NT = mCancelledThumbnails.size();
+ if (NT > 0) {
+ thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails);
+ mCancelledThumbnails.clear();
+ } else {
+ thumbnails = null;
}
- int i;
+ if (mStackSupervisor.isFrontStack(this)) {
+ booting = mService.mBooting;
+ mService.mBooting = false;
+ }
- // Send thumbnail if requested.
- if (sendThumbnail != null) {
- try {
- sendThumbnail.requestThumbnail(token);
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
- mService.sendPendingThumbnail(null, token, null, null, true);
- }
+ if (mStartingUsers.size() > 0) {
+ startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
+ mStartingUsers.clear();
}
- // 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);
- synchronized (mService) {
- if (r.finishing) {
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
- } else {
- stopActivityLocked(r);
+ // Perform the following actions from unsynchronized state.
+ final IApplicationThread thumbnailThread = sendThumbnail;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (thumbnailThread != null) {
+ try {
+ thumbnailThread.requestThumbnail(token);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
+ mService.sendPendingThumbnail(null, token, null, null, true);
+ }
+ }
+
+ // Report back to any thumbnail receivers.
+ for (int i = 0; i < NT; i++) {
+ ActivityRecord r = thumbnails.get(i);
+ mService.sendPendingThumbnail(r, null, null, null, true);
}
}
- }
+ });
- // Finish any activities that are scheduled to do so but have been
+ // Stop 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);
- synchronized (mService) {
- activityRemoved = destroyActivityLocked(r, true, false, "finish-idle");
+ for (int i = 0; i < NS; i++) {
+ r = stops.get(i);
+ if (r.finishing) {
+ finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+ } else {
+ stopActivityLocked(r);
}
}
- // Report back to any thumbnail receivers.
- for (i=0; i<NT; i++) {
- ActivityRecord r = (ActivityRecord)thumbnails.get(i);
- mService.sendPendingThumbnail(r, null, null, null, true);
+ // Finish any activities that are scheduled to do so but have been
+ // waiting for the next one to start.
+ for (int i = 0; i < NF; i++) {
+ r = finishes.get(i);
+ activityRemoved |= destroyActivityLocked(r, true, false, "finish-idle");
}
if (booting) {
mService.finishBooting();
} else if (startingUsers != null) {
- for (i=0; i<startingUsers.size(); i++) {
+ for (int i = 0; i < startingUsers.size(); i++) {
mService.finishUserSwitch(startingUsers.get(i));
}
}
@@ -3655,7 +2416,7 @@ final class ActivityStack {
}
if (activityRemoved) {
- resumeTopActivityLocked(null);
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
}
return res;
@@ -3667,63 +2428,82 @@ 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) {
- break;
- }
- if (cur.taskAffinity != null && !cur.taskAffinity.equals(r.taskAffinity)) {
+ 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;
}
- finishActivityLocked(cur, index, Activity.RESULT_CANCELED, null,
- "request-affinity", true);
- index--;
+ finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", true);
}
return true;
}
@@ -3759,17 +2539,8 @@ 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);
- }
-
- /**
- * @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 oomAdj) {
if (r.finishing) {
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
@@ -3779,53 +2550,49 @@ 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);
}
}
r.pauseKeyDispatchingLocked();
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
if (mService.mFocusedActivity == r) {
mService.setFocusedActivityLocked(topRunningActivityLocked(null));
}
}
finishActivityResultsLocked(r, resultCode, resultData);
-
+
if (mService.mPendingThumbnails.size() > 0) {
// 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;
- } else if (mResumedActivity == r) {
- boolean endTask = index <= 0
- || (mHistory.get(index-1)).task != r.task;
+ if (mResumedActivity == r) {
+ 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);
-
+
if (mPausingActivity == null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
@@ -3836,8 +2603,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);
}
@@ -3851,23 +2617,13 @@ final class ActivityStack {
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) {
// 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.
if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
- if (!mStoppingActivities.contains(r)) {
- mStoppingActivities.add(r);
- if (mStoppingActivities.size() > 3) {
+ if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+ mStackSupervisor.mStoppingActivities.add(r);
+ if (mStackSupervisor.mStoppingActivities.size() > 3) {
// If we already have a few activities waiting to stop,
// then give up on things going idle and start clearing
// them out.
@@ -3886,9 +2642,9 @@ final class ActivityStack {
}
// make sure the record is cleaned out of other places.
- mStoppingActivities.remove(r);
+ mStackSupervisor.mStoppingActivities.remove(r);
mGoingToSleepActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
+ mStackSupervisor.mWaitingVisibleActivities.remove(r);
if (mResumedActivity == r) {
mResumedActivity = null;
}
@@ -3904,19 +2660,98 @@ final class ActivityStack {
boolean activityRemoved = destroyActivityLocked(r, true,
oomAdj, "finish-imm");
if (activityRemoved) {
- resumeTopActivityLocked(null);
+ mStackSupervisor.getTopStack().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);
+ mStackSupervisor.getTopStack().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 = mStackSupervisor.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
@@ -3947,8 +2782,8 @@ final class ActivityStack {
// This could happen, for example, if we are trimming activities
// down to the max limit while they are still waiting to finish.
mFinishingActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
-
+ mStackSupervisor.mWaitingVisibleActivities.remove(r);
+
// Remove any pending results.
if (r.finishing && r.pendingResults != null) {
for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
@@ -3961,14 +2796,14 @@ final class ActivityStack {
}
if (cleanServices) {
- cleanUpActivityServicesLocked(r);
+ cleanUpActivityServicesLocked(r);
}
if (mService.mPendingThumbnails.size() > 0) {
// 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.
@@ -3991,11 +2826,13 @@ final class ActivityStack {
here.fillInStackTrace();
Slog.i(TAG, "Removing activity " + r + " from stack");
}
- mHistory.remove(r);
+ final TaskRecord task = r.task;
+ if (task != null && task.removeActivity(r)) {
+ mStackSupervisor.removeTask(task);
+ }
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;
@@ -4006,7 +2843,7 @@ final class ActivityStack {
cleanUpActivityServicesLocked(r);
r.removeUriPermissionsLocked();
}
-
+
/**
* Perform clean-up of service connections in an activity record.
*/
@@ -4031,36 +2868,39 @@ 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;
+ }
}
}
}
if (activityRemoved) {
- resumeTopActivityLocked(null);
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
}
}
@@ -4087,10 +2927,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(
@@ -4103,7 +2940,7 @@ final class ActivityStack {
}
boolean skipDestroy = false;
-
+
try {
if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
@@ -4121,7 +2958,7 @@ final class ActivityStack {
}
r.nowVisible = false;
-
+
// If the activity is finishing, we need to wait on removing it
// from the list to give it a chance to do its cleanup. During
// that time it may make calls back with its token so we need to
@@ -4158,46 +2995,43 @@ final class ActivityStack {
}
r.configChangeFlags = 0;
-
+
if (!mLRUActivities.remove(r) && hadApp) {
Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
}
-
+
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);
}
+ mStackSupervisor.getTopStack().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!");
@@ -4209,100 +3043,86 @@ final class ActivityStack {
boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
- removeHistoryRecordsForAppLocked(mStoppingActivities, app, "mStoppingActivities");
+ removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
+ "mStoppingActivities");
removeHistoryRecordsForAppLocked(mGoingToSleepActivities, app, "mGoingToSleepActivities");
- removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app,
+ removeHistoryRecordsForAppLocked(mStackSupervisor.mWaitingVisibleActivities, app,
"mWaitingVisibleActivities");
removeHistoryRecordsForAppLocked(mFinishingActivities, app, "mFinishingActivities");
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;
- }
- }
- if (homeTask != null) {
- moveTaskToFrontLocked(homeTask, null, null);
- }
- }
final void updateTransitLocked(int transit, Bundle options) {
if (options != null) {
@@ -4316,13 +3136,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) {
+ mStackSupervisor.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.
+ task.mActivities.get(0).mLaunchHomeTaskNext = true;
+ }
+ 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) {
@@ -4333,40 +3169,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);
@@ -4375,18 +3186,15 @@ final class ActivityStack {
} else {
updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
}
-
- mService.mWindowManager.moveAppTokensToTop(moved);
+
+ mService.mWindowManager.moveTaskToTop(tr.taskId);
+
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
+ EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
+
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
-
- finishTaskMoveLocked(task);
- EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, task);
- }
-
- private final void finishTaskMoveLocked(int task) {
- resumeTopActivityLocked(null);
}
/**
@@ -4402,11 +3210,11 @@ 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.
- if (mMainStack && mService.mController != null) {
+ if (mStackSupervisor.isFrontStack(this) && mService.mController != null) {
ActivityRecord next = topRunningActivityLocked(null, task);
if (next == null) {
next = topRunningActivityLocked(null, 0);
@@ -4425,41 +3233,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);
@@ -4468,20 +3254,26 @@ 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);
+ if (mResumedActivity != null && mResumedActivity.task == tr &&
+ mResumedActivity.mLaunchHomeTaskNext) {
+ mResumedActivity.mLaunchHomeTaskNext = false;
+ return mService.startHomeActivityLocked(mCurrentUser);
+ }
+
+ mStackSupervisor.getTopStack().resumeTopActivityLocked(null);
return true;
}
public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) {
- TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true);
- ActivityRecord resumed = mResumedActivity;
- if (resumed != null && resumed.thumbHolder == tr) {
- info.mainThumbnail = resumed.stack.screenshotActivities(resumed);
+ TaskAccessInfo info = getTaskAccessInfoLocked(tr, true);
+ if (mResumedActivity != null && mResumedActivity.thumbHolder == tr) {
+ info.mainThumbnail = screenshotActivities(mResumedActivity);
}
if (info.mainThumbnail == null) {
info.mainThumbnail = tr.lastThumbnail;
@@ -4490,25 +3282,27 @@ final class ActivityStack {
}
public Bitmap getTaskTopThumbnailLocked(TaskRecord tr) {
- ActivityRecord resumed = mResumedActivity;
- if (resumed != null && resumed.task == tr) {
+ if (mResumedActivity != null && mResumedActivity.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(mResumedActivity);
}
// 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);
@@ -4518,7 +3312,7 @@ final class ActivityStack {
if (subTaskIndex < 0) {
// Just remove the entire task.
- performClearTaskAtIndexLocked(taskId, info.rootIndex);
+ task.performClearTaskAtIndexLocked(info.rootIndex);
return info.root;
}
@@ -4531,19 +3325,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);
+ task.performClearTaskAtIndexLocked(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;
@@ -4563,14 +3358,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;
@@ -4583,14 +3375,14 @@ 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;
}
TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index);
- ActivityRecord resumed = mResumedActivity;
- if (resumed != null && resumed.thumbHolder == sub.holder) {
- return resumed.stack.screenshotActivities(resumed);
+ if (mResumedActivity != null && mResumedActivity.thumbHolder == sub.holder) {
+ return screenshotActivities(mResumedActivity);
}
return sub.holder.lastThumbnail;
}
@@ -4599,7 +3391,7 @@ final class ActivityStack {
return thumbs;
}
- private final void logStartActivity(int tag, ActivityRecord r,
+ static final void logStartActivity(int tag, ActivityRecord r,
TaskRecord task) {
final Uri data = r.intent.getData();
final String strData = data != null ? data.toSafeString() : null;
@@ -4768,7 +3560,7 @@ final class ActivityStack {
if (andResume) {
r.results = null;
r.newIntents = null;
- if (mMainStack) {
+ if (mStackSupervisor.isFrontStack(this)) {
mService.reportResumedActivityLocked(r);
}
r.state = ActivityState.RESUMED;
@@ -4779,8 +3571,291 @@ final class ActivityStack {
return true;
}
-
- 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 handleAppDiedLocked(ProcessRecord app, boolean restarting) {
+ if (mPausingActivity != null && mPausingActivity.app == app) {
+ if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG,
+ "App died while pausing: " + mPausingActivity);
+ mPausingActivity = null;
+ }
+ if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+ mLastPausedActivity = null;
+ }
+
+ // Remove this application's activities from active lists.
+ boolean hasVisibleActivities = removeHistoryRecordsForAppLocked(app);
+
+ if (!restarting) {
+ if (!mStackSupervisor.getTopStack().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) {
+ ensureActivitiesVisibleLocked(null, 0);
+ }
+ }
+ }
+ }
+
+ 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);
+ ActivityStackSupervisor.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;
+ }
+
+ boolean removeTask(TaskRecord task) {
+ mTaskHistory.remove(task);
+ return mTaskHistory.size() == 0;
+ }
+
+ 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 = mStackSupervisor.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;
+ }
+
+ @Override
+ public String toString() {
+ return "stackId=" + mStackId + " tasks=" + mTaskHistory;
}
}
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
new file mode 100644
index 0000000..d3e5b48
--- /dev/null
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -0,0 +1,1822 @@
+/*
+ * 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.am;
+
+import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.server.am.ActivityManagerService.localLOGV;
+import static com.android.server.am.ActivityManagerService.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerService.DEBUG_RESULTS;
+import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerService.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityManagerService.TAG;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.IApplicationThread;
+import android.app.IThumbnailReceiver;
+import android.app.PendingIntent;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.IActivityManager.WaitResult;
+import android.app.ResultInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.EventLog;
+import android.util.Slog;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
+import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.wm.StackBox;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ActivityStackSupervisor {
+ static final boolean DEBUG = ActivityManagerService.DEBUG || false;
+ static final boolean DEBUG_ADD_REMOVE = DEBUG || false;
+ static final boolean DEBUG_APP = DEBUG || false;
+ static final boolean DEBUG_SAVED_STATE = DEBUG || false;
+ static final boolean DEBUG_STATES = DEBUG || false;
+
+ public static final int HOME_STACK_ID = 0;
+
+ final ActivityManagerService mService;
+ final Context mContext;
+ final Looper mLooper;
+
+ /** Dismiss the keyguard after the next activity is displayed? */
+ private boolean mDismissKeyguardOnNextActivity = false;
+
+ /** Identifier counter for all ActivityStacks */
+ private int mLastStackId = HOME_STACK_ID;
+
+ /** Task identifier that activities are currently being started in. Incremented each time a
+ * new task is created. */
+ private int mCurTaskId = 0;
+
+ /** The current user */
+ private int mCurrentUser;
+
+ /** The stack containing the launcher app */
+ private ActivityStack mHomeStack;
+
+ /** The non-home stack currently receiving input or launching the next activity. If home is
+ * in front then mHomeStack overrides mMainStack. */
+ private ActivityStack mMainStack;
+
+ /** All the non-launcher stacks */
+ private ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
+
+ private static final int STACK_STATE_HOME_IN_FRONT = 0;
+ private static final int STACK_STATE_HOME_TO_BACK = 1;
+ private static final int STACK_STATE_HOME_IN_BACK = 2;
+ private static final int STACK_STATE_HOME_TO_FRONT = 3;
+ private int mStackState = STACK_STATE_HOME_IN_FRONT;
+
+ /** List of activities that are waiting for a new activity to become visible before completing
+ * whatever operation they are supposed to do. */
+ final ArrayList<ActivityRecord> mWaitingVisibleActivities = new ArrayList<ActivityRecord>();
+
+ /** List of activities that are ready to be stopped, but waiting for the next activity to
+ * settle down before doing so. */
+ final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<ActivityRecord>();
+
+ /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
+ * is being brought in front of us. */
+ boolean mUserLeaving = false;
+
+ public ActivityStackSupervisor(ActivityManagerService service, Context context,
+ Looper looper) {
+ mService = service;
+ mContext = context;
+ mLooper = looper;
+ }
+
+ void init(int userId) {
+ mHomeStack = new ActivityStack(mService, mContext, mLooper, HOME_STACK_ID, this, userId);
+ mStacks.add(mHomeStack);
+ }
+
+ void dismissKeyguard() {
+ if (mDismissKeyguardOnNextActivity) {
+ mDismissKeyguardOnNextActivity = false;
+ mService.mWindowManager.dismissKeyguard();
+ }
+ }
+
+ ActivityStack getTopStack() {
+ switch (mStackState) {
+ case STACK_STATE_HOME_IN_FRONT:
+ case STACK_STATE_HOME_TO_FRONT:
+ return mHomeStack;
+ case STACK_STATE_HOME_IN_BACK:
+ case STACK_STATE_HOME_TO_BACK:
+ default:
+ return mMainStack;
+ }
+ }
+
+ ActivityStack getLastStack() {
+ switch (mStackState) {
+ case STACK_STATE_HOME_IN_FRONT:
+ case STACK_STATE_HOME_TO_BACK:
+ return mHomeStack;
+ case STACK_STATE_HOME_TO_FRONT:
+ case STACK_STATE_HOME_IN_BACK:
+ default:
+ return mMainStack;
+ }
+ }
+
+ boolean isFrontStack(ActivityStack stack) {
+ return stack == getTopStack();
+ }
+
+ boolean homeIsInFront() {
+ return isFrontStack(mHomeStack);
+ }
+
+ void moveHomeStack(boolean toFront) {
+ final boolean homeInFront = isFrontStack(mHomeStack);
+ if (homeInFront ^ toFront) {
+ mStackState = homeInFront ? STACK_STATE_HOME_TO_BACK : STACK_STATE_HOME_TO_FRONT;
+ }
+ }
+
+ final void setLaunchHomeTaskNextFlag(ActivityRecord sourceRecord, ActivityRecord r,
+ ActivityStack stack) {
+ if (stack == mHomeStack) {
+ return;
+ }
+ if ((sourceRecord == null && getLastStack() == mHomeStack) ||
+ (sourceRecord != null && sourceRecord.isHomeActivity)) {
+ if (r == null) {
+ r = stack.topRunningActivityLocked(null);
+ }
+ if (r != null && !r.isHomeActivity && r.isRootActivity()) {
+ r.mLaunchHomeTaskNext = true;
+ }
+ }
+ }
+
+ void setDismissKeyguard(boolean dismiss) {
+ mDismissKeyguardOnNextActivity = dismiss;
+ }
+
+ 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;
+ }
+
+ ActivityRecord isInAnyStackLocked(IBinder token) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord r = mStacks.get(stackNdx).isInStackLocked(token);
+ if (r != null) {
+ return r;
+ }
+ }
+ return null;
+ }
+
+ int getNextTaskId() {
+ do {
+ mCurTaskId++;
+ if (mCurTaskId <= 0) {
+ mCurTaskId = 1;
+ }
+ } while (anyTaskForIdLocked(mCurTaskId) != null);
+ return mCurTaskId;
+ }
+
+ void removeTask(TaskRecord task) {
+ final ActivityStack stack = task.stack;
+ if (stack.removeTask(task) && !stack.isHomeStack()) {
+ mStacks.remove(stack);
+ final int oldStackId = stack.mStackId;
+ final int newMainStackId = mService.mWindowManager.removeStack(oldStackId);
+ if (newMainStackId == HOME_STACK_ID) {
+ return;
+ }
+ if (mMainStack.mStackId == oldStackId) {
+ mMainStack = getStack(newMainStackId);
+ }
+ }
+ }
+
+ ActivityRecord resumedAppLocked() {
+ ActivityStack stack = getTopStack();
+ ActivityRecord resumedActivity = stack.mResumedActivity;
+ if (resumedActivity == null || resumedActivity.app == null) {
+ resumedActivity = stack.mPausingActivity;
+ if (resumedActivity == null || resumedActivity.app == null) {
+ resumedActivity = stack.topRunningActivityLocked(null);
+ }
+ }
+ return resumedActivity;
+ }
+
+ boolean attachApplicationLocked(ProcessRecord app, boolean headless) throws Exception {
+ boolean didSomething = false;
+ final String processName = app.processName;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ ActivityRecord hr = stack.topRunningActivityLocked(null);
+ if (hr != null) {
+ if (hr.app == null && app.uid == hr.info.applicationInfo.uid
+ && processName.equals(hr.processName)) {
+ try {
+ if (headless) {
+ Slog.e(TAG, "Starting activities not supported on headless device: "
+ + hr);
+ } else if (realStartActivityLocked(hr, app, true, true)) {
+ didSomething = true;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception in new application when starting activity "
+ + hr.intent.getComponent().flattenToShortString(), e);
+ throw e;
+ }
+ } else {
+ stack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
+ }
+ }
+ }
+ return didSomething;
+ }
+
+ boolean allResumedActivitiesIdle() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord resumedActivity = mStacks.get(stackNdx).mResumedActivity;
+ if (resumedActivity == null || !resumedActivity.idle) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean allResumedActivitiesComplete() {
+ final boolean homeInBack = !homeIsInFront();
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.isHomeStack() ^ homeInBack) {
+ final ActivityRecord r = stack.mResumedActivity;
+ if (r != null && r.state != ActivityState.RESUMED) {
+ return false;
+ }
+ }
+ }
+ // TODO: Not sure if this should check if all Paused are complete too.
+ switch (mStackState) {
+ case STACK_STATE_HOME_TO_BACK:
+ mStackState = STACK_STATE_HOME_IN_BACK;
+ break;
+ case STACK_STATE_HOME_TO_FRONT:
+ mStackState = STACK_STATE_HOME_IN_FRONT;
+ break;
+ }
+ return true;
+ }
+
+ boolean allResumedActivitiesVisible() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ final ActivityRecord r = stack.mResumedActivity;
+ if (r != null && (!r.nowVisible || r.waitingVisible)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean allPausedActivitiesComplete() {
+ final boolean homeInBack = !homeIsInFront();
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.isHomeStack() ^ homeInBack) {
+ final ActivityRecord r = stack.mLastPausedActivity;
+ if (r != null && r.state != ActivityState.PAUSED
+ && r.state != ActivityState.STOPPED
+ && r.state != ActivityState.STOPPING) {
+ return false;
+ }
+ }
+ }
+ // TODO: Not sure if this should check if all Resumed are complete too.
+ switch (mStackState) {
+ case STACK_STATE_HOME_TO_BACK:
+ mStackState = STACK_STATE_HOME_IN_BACK;
+ break;
+ case STACK_STATE_HOME_TO_FRONT:
+ mStackState = STACK_STATE_HOME_IN_FRONT;
+ break;
+ }
+ return true;
+ }
+
+ ActivityRecord getTasksLocked(int maxNum, IThumbnailReceiver receiver,
+ PendingThumbnailsRecord pending, List<RunningTaskInfo> list) {
+ ActivityRecord r = null;
+ final int numStacks = mStacks.size();
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ final ActivityRecord ar =
+ stack.getTasksLocked(maxNum - list.size(), receiver, pending, list);
+ if (isFrontStack(stack)) {
+ r = ar;
+ }
+ }
+ return r;
+ }
+
+ ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
+ String profileFile, ParcelFileDescriptor profileFd, int userId) {
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, resolvedType,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+
+ if (aInfo != null) {
+ // Store the found target back into the intent, because now that
+ // we have it we never want to do this again. For example, if the
+ // user navigates back to this point in the history, we should
+ // always restart the exact same activity.
+ intent.setComponent(new ComponentName(
+ aInfo.applicationInfo.packageName, aInfo.name));
+
+ // Don't debug things in the system process
+ if ((startFlags&ActivityManager.START_FLAG_DEBUG) != 0) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setDebugApp(aInfo.processName, true, false);
+ }
+ }
+
+ if ((startFlags&ActivityManager.START_FLAG_OPENGL_TRACES) != 0) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setOpenGlTraceApp(aInfo.applicationInfo, aInfo.processName);
+ }
+ }
+
+ if (profileFile != null) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setProfileApp(aInfo.applicationInfo, aInfo.processName,
+ profileFile, profileFd,
+ (startFlags&ActivityManager.START_FLAG_AUTO_STOP_PROFILER) != 0);
+ }
+ }
+ }
+ return aInfo;
+ }
+
+ void startHomeActivity(Intent intent, ActivityInfo aInfo) {
+ moveHomeStack(true);
+ startActivityLocked(null, intent, null, aInfo, null, null, 0, 0, 0, null, 0,
+ null, false, null);
+ }
+
+ final int startActivityMayWait(IApplicationThread caller, int callingUid,
+ String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, int startFlags, String profileFile,
+ ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
+ Bundle options, int userId) {
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
+ profileFile, profileFd, userId);
+
+ synchronized (mService) {
+ int callingPid;
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+
+ final ActivityStack stack = getTopStack();
+ stack.mConfigWillChange = config != null
+ && mService.mConfiguration.diff(config) != 0;
+ if (DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Starting activity when config will change = " + stack.mConfigWillChange);
+
+ final long origId = Binder.clearCallingIdentity();
+
+ if (aInfo != null &&
+ (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+ // This may be a heavy-weight process! Check to see if we already
+ // have another, different heavy-weight process running.
+ if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+ if (mService.mHeavyWeightProcess != null &&
+ (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
+ !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
+ int realCallingPid = callingPid;
+ int realCallingUid = callingUid;
+ if (caller != null) {
+ ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ realCallingPid = callerApp.pid;
+ realCallingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + realCallingPid + ") when starting: "
+ + intent.toString());
+ ActivityOptions.abort(options);
+ return ActivityManager.START_PERMISSION_DENIED;
+ }
+ }
+
+ IIntentSender target = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, "android",
+ realCallingUid, userId, null, null, 0, new Intent[] { intent },
+ new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT, null);
+
+ Intent newIntent = new Intent();
+ if (requestCode >= 0) {
+ // Caller is requesting a result.
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+ new IntentSender(target));
+ if (mService.mHeavyWeightProcess.activities.size() > 0) {
+ ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
+ hist.packageName);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
+ hist.task.taskId);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+ aInfo.packageName);
+ newIntent.setFlags(intent.getFlags());
+ newIntent.setClassName("android",
+ HeavyWeightSwitcherActivity.class.getName());
+ intent = newIntent;
+ resolvedType = null;
+ caller = null;
+ callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
+ componentSpecified = true;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, null,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+ }
+ }
+ }
+
+ int res = startActivityLocked(caller, intent, resolvedType,
+ aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
+ callingPackage, startFlags, options, componentSpecified, null);
+
+ if (stack.mConfigWillChange) {
+ // If the caller also wants to switch to a new configuration,
+ // do so now. This allows a clean switch, as we are waiting
+ // for the current activity to pause (so we will not destroy
+ // it), and have not yet started the next activity.
+ mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+ "updateConfiguration()");
+ stack.mConfigWillChange = false;
+ if (DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Updating to new configuration after starting activity.");
+ mService.updateConfigurationLocked(config, null, false, false);
+ }
+
+ Binder.restoreCallingIdentity(origId);
+
+ if (outResult != null) {
+ outResult.result = res;
+ if (res == ActivityManager.START_SUCCESS) {
+ stack.mWaitingActivityLaunched.add(outResult);
+ do {
+ try {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ } else if (res == ActivityManager.START_TASK_TO_FRONT) {
+ ActivityRecord r = stack.topRunningActivityLocked(null);
+ if (r.nowVisible) {
+ outResult.timeout = false;
+ outResult.who = new ComponentName(r.info.packageName, r.info.name);
+ outResult.totalTime = 0;
+ outResult.thisTime = 0;
+ } else {
+ outResult.thisTime = SystemClock.uptimeMillis();
+ stack.mWaitingActivityVisible.add(outResult);
+ do {
+ try {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ }
+ }
+ }
+
+ return res;
+ }
+ }
+
+ final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo,
+ Bundle options, int userId) {
+ if (intents == null) {
+ throw new NullPointerException("intents is null");
+ }
+ if (resolvedTypes == null) {
+ throw new NullPointerException("resolvedTypes is null");
+ }
+ if (intents.length != resolvedTypes.length) {
+ throw new IllegalArgumentException("intents are length different than resolvedTypes");
+ }
+
+ ActivityRecord[] outActivity = new ActivityRecord[1];
+
+ int callingPid;
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+
+ for (int i=0; i<intents.length; i++) {
+ Intent intent = intents[i];
+ if (intent == null) {
+ continue;
+ }
+
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i],
+ 0, null, null, userId);
+ // TODO: New, check if this is correct
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
+
+ if (aInfo != null &&
+ (aInfo.applicationInfo.flags & ApplicationInfo.FLAG_CANT_SAVE_STATE)
+ != 0) {
+ throw new IllegalArgumentException(
+ "FLAG_CANT_SAVE_STATE not supported here");
+ }
+
+ Bundle theseOptions;
+ if (options != null && i == intents.length-1) {
+ theseOptions = options;
+ } else {
+ theseOptions = null;
+ }
+ int res = startActivityLocked(caller, intent, resolvedTypes[i],
+ aInfo, resultTo, null, -1, callingPid, callingUid, callingPackage,
+ 0, theseOptions, componentSpecified, outActivity);
+ if (res < 0) {
+ return res;
+ }
+
+ resultTo = outActivity[0] != null ? outActivity[0].appToken : null;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return ActivityManager.START_SUCCESS;
+ }
+
+ final boolean realStartActivityLocked(ActivityRecord r,
+ ProcessRecord app, boolean andResume, boolean checkConfig)
+ throws RemoteException {
+
+ r.startFreezingScreenLocked(app, 0);
+ mService.mWindowManager.setAppVisibility(r.appToken, true);
+
+ // schedule launch ticks to collect information about slow apps.
+ r.startLaunchTickingLocked();
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order. Note that
+ // as a result of this, it can call back into the activity
+ // manager with a new orientation. We don't care about that,
+ // because the activity is not currently running so we are
+ // just restarting it anyway.
+ if (checkConfig) {
+ Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
+ mService.mConfiguration,
+ r.mayFreezeScreenLocked(app) ? r.appToken : null);
+ mService.updateConfigurationLocked(config, r, false, false);
+ }
+
+ r.app = app;
+ app.waitingToKill = null;
+ r.launchCount++;
+ r.lastLaunchTime = SystemClock.uptimeMillis();
+
+ if (localLOGV) Slog.v(TAG, "Launching: " + r);
+
+ int idx = app.activities.indexOf(r);
+ if (idx < 0) {
+ app.activities.add(r);
+ }
+ mService.updateLruProcessLocked(app, true);
+
+ final ActivityStack stack = r.task.stack;
+ try {
+ if (app.thread == null) {
+ throw new RemoteException();
+ }
+ List<ResultInfo> results = null;
+ List<Intent> newIntents = null;
+ if (andResume) {
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
+ + " icicle=" + r.icicle
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
+ if (andResume) {
+ EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
+ r.userId, System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+ }
+ if (r.isHomeActivity) {
+ mService.mHomeProcess = app;
+ }
+ mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ r.sleeping = false;
+ r.forceNewConfig = false;
+ mService.showAskCompatModeDialogLocked(r);
+ r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+ String profileFile = null;
+ ParcelFileDescriptor profileFd = null;
+ boolean profileAutoStop = false;
+ if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
+ if (mService.mProfileProc == null || mService.mProfileProc == app) {
+ mService.mProfileProc = app;
+ profileFile = mService.mProfileFile;
+ profileFd = mService.mProfileFd;
+ profileAutoStop = mService.mAutoStopProfiler;
+ }
+ }
+ app.hasShownUi = true;
+ app.pendingUiClean = true;
+ if (profileFd != null) {
+ try {
+ profileFd = profileFd.dup();
+ } catch (IOException e) {
+ if (profileFd != null) {
+ try {
+ profileFd.close();
+ } catch (IOException o) {
+ }
+ profileFd = null;
+ }
+ }
+ }
+ app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
+ System.identityHashCode(r), r.info,
+ new Configuration(mService.mConfiguration),
+ 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
+ // process of the .apk, which is the only thing that will be
+ // considered heavy-weight.
+ if (app.processName.equals(app.info.packageName)) {
+ if (mService.mHeavyWeightProcess != null
+ && mService.mHeavyWeightProcess != app) {
+ Slog.w(TAG, "Starting new heavy weight process " + app
+ + " when already running "
+ + mService.mHeavyWeightProcess);
+ }
+ mService.mHeavyWeightProcess = app;
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
+ msg.obj = r;
+ mService.mHandler.sendMessage(msg);
+ }
+ }
+
+ } catch (RemoteException e) {
+ if (r.launchFailed) {
+ // This is the second time we failed -- finish activity
+ // and give up.
+ Slog.e(TAG, "Second failure launching "
+ + r.intent.getComponent().flattenToShortString()
+ + ", giving up", e);
+ mService.appDiedLocked(app, app.pid, app.thread);
+ stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
+ "2nd-crash", false);
+ return false;
+ }
+
+ // This is the first time we failed -- restart process and
+ // retry.
+ app.activities.remove(r);
+ throw e;
+ }
+
+ r.launchFailed = false;
+ if (stack.updateLRUListLocked(r)) {
+ Slog.w(TAG, "Activity " + r
+ + " being launched, but already in LRU list");
+ }
+
+ if (andResume) {
+ // As part of the process of launching, ActivityThread also performs
+ // a resume.
+ stack.minimalResumeActivityLocked(r);
+ } else {
+ // This activity is not starting in the resumed state... which
+ // should look like we asked it to pause+stop (but remain visible),
+ // and it has done so and reported back the current icicle and
+ // other state.
+ if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r
+ + " (starting in stopped state)");
+ r.state = ActivityState.STOPPED;
+ r.stopped = true;
+ }
+
+ // Launch the new version setup screen if needed. We do this -after-
+ // launching the initial activity (that is, home), so that it can have
+ // a chance to initialize itself while in the background, making the
+ // switch back to it faster and look better.
+ if (isFrontStack(stack)) {
+ mService.startSetupActivityLocked();
+ }
+
+ return true;
+ }
+
+ void startSpecificActivityLocked(ActivityRecord r,
+ boolean andResume, boolean checkConfig) {
+ // Is this activity's application already running?
+ ProcessRecord app = mService.getProcessRecordLocked(r.processName,
+ r.info.applicationInfo.uid);
+
+ r.task.stack.setLaunchTime(r);
+
+ if (app != null && app.thread != null) {
+ try {
+ app.addPackage(r.info.packageName);
+ realStartActivityLocked(r, app, andResume, checkConfig);
+ return;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when starting activity "
+ + r.intent.getComponent().flattenToShortString(), e);
+ }
+
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
+
+ mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
+ "activity", r.intent.getComponent(), false, false);
+ }
+
+ final int startActivityLocked(IApplicationThread caller,
+ Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
+ 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;
+ if (caller != null) {
+ callerApp = mService.getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ callingPid = callerApp.pid;
+ callingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + callingPid + ") when starting: "
+ + intent.toString());
+ err = ActivityManager.START_PERMISSION_DENIED;
+ }
+ }
+
+ if (err == ActivityManager.START_SUCCESS) {
+ final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
+ Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
+ + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
+ }
+
+ ActivityRecord sourceRecord = null;
+ ActivityRecord resultRecord = null;
+ if (resultTo != null) {
+ sourceRecord = isInAnyStackLocked(resultTo);
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Will send result to " + resultTo + " " + sourceRecord);
+ if (sourceRecord != null) {
+ if (requestCode >= 0 && !sourceRecord.finishing) {
+ resultRecord = sourceRecord;
+ }
+ }
+ }
+ ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
+
+ int launchFlags = intent.getFlags();
+
+ if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
+ && sourceRecord != null) {
+ // Transfer the result target from the source activity to the new
+ // one being started, including any failures.
+ if (requestCode >= 0) {
+ ActivityOptions.abort(options);
+ return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+ }
+ resultRecord = sourceRecord.resultTo;
+ resultWho = sourceRecord.resultWho;
+ requestCode = sourceRecord.requestCode;
+ sourceRecord.resultTo = null;
+ if (resultRecord != null) {
+ resultRecord.removeResultsLocked(
+ sourceRecord, resultWho, requestCode);
+ }
+ }
+
+ if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
+ // We couldn't find a class that can handle the given Intent.
+ // That's the end of that!
+ err = ActivityManager.START_INTENT_NOT_RESOLVED;
+ }
+
+ if (err == ActivityManager.START_SUCCESS && aInfo == null) {
+ // We couldn't find the specific class specified in the Intent.
+ // Also the end of the line.
+ err = ActivityManager.START_CLASS_NOT_FOUND;
+ }
+
+ if (err != ActivityManager.START_SUCCESS) {
+ if (resultRecord != null) {
+ resultStack.sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ setDismissKeyguard(false);
+ ActivityOptions.abort(options);
+ return err;
+ }
+
+ final int startAnyPerm = mService.checkPermission(
+ START_ANY_ACTIVITY, callingPid, callingUid);
+ final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
+ callingUid, aInfo.applicationInfo.uid, aInfo.exported);
+ if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
+ if (resultRecord != null) {
+ resultStack.sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ setDismissKeyguard(false);
+ String msg;
+ if (!aInfo.exported) {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " not exported from uid " + aInfo.applicationInfo.uid;
+ } else {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + aInfo.permission;
+ }
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
+ callerApp==null?null:callerApp.info, callingUid, callingPid, resolvedType, aInfo);
+
+ if (mService.mController != null) {
+ try {
+ // The Intent we give to the watcher has the extra data
+ // stripped off, since it can contain private information.
+ Intent watchIntent = intent.cloneFilter();
+ abort |= !mService.mController.activityStarting(watchIntent,
+ aInfo.applicationInfo.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ }
+ }
+
+ if (abort) {
+ if (resultRecord != null) {
+ resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ // We pretend to the caller that it was really started, but
+ // they will just get a cancel result.
+ setDismissKeyguard(false);
+ ActivityOptions.abort(options);
+ return ActivityManager.START_SUCCESS;
+ }
+
+ ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
+ intent, resolvedType, aInfo, mService.mConfiguration,
+ resultRecord, resultWho, requestCode, componentSpecified, this);
+ if (outActivity != null) {
+ outActivity[0] = r;
+ }
+
+ final ActivityStack stack = getTopStack();
+ if (stack.mResumedActivity == null
+ || stack.mResumedActivity.info.applicationInfo.uid != callingUid) {
+ if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+ PendingActivityLaunch pal =
+ new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
+ mService.mPendingActivityLaunches.add(pal);
+ setDismissKeyguard(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
+ // home (switches disabled, switch to home, mDidAppSwitch now true);
+ // user taps a home icon (coming from home so allowed, we hit here
+ // and now allow anyone to switch again).
+ mService.mAppSwitchesAllowedTime = 0;
+ } else {
+ mService.mDidAppSwitch = true;
+ }
+
+ mService.doPendingActivityLaunchesLocked(false);
+
+ err = startActivityUncheckedLocked(r, sourceRecord, startFlags, true, options);
+ if (stack.mPausingActivity == null) {
+ // Someone asked to have the keyguard dismissed on the next
+ // activity start, but we are not actually doing an activity
+ // switch... just dismiss the keyguard now, because we
+ // probably want to see whatever is behind it.
+ dismissKeyguard();
+ }
+ return err;
+ }
+
+ ActivityStack getCorrectStack(ActivityRecord r) {
+ if (!r.isHomeActivity) {
+ if (mStacks.size() == 1) {
+ // Time to create the first app stack.
+ int stackId =
+ mService.createStack(HOME_STACK_ID, StackBox.TASK_STACK_GOES_OVER, 1.0f);
+ mMainStack = getStack(stackId);
+ }
+ return mMainStack;
+ }
+ return mHomeStack;
+ }
+
+ final int startActivityUncheckedLocked(ActivityRecord r,
+ ActivityRecord sourceRecord, int startFlags, boolean doResume,
+ Bundle options) {
+ final Intent intent = r.intent;
+ 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;
+
+ // If the onlyIfNeeded flag is set, then we can do this if the activity
+ // being launched is the same as the one making the call... or, as
+ // a special case, if we do not know the caller then we count the
+ // current top activity as the caller.
+ if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ ActivityRecord checkedCaller = sourceRecord;
+ if (checkedCaller == null) {
+ checkedCaller = getTopStack().topRunningNonDelayedActivityLocked(notTop);
+ }
+ if (!checkedCaller.realActivity.equals(r.realActivity)) {
+ // Caller is not the same as launcher, so always needed.
+ startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
+ }
+ }
+
+ if (sourceRecord == null) {
+ // This activity is not being started from another... in this
+ // case we -always- start a new task.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: "
+ + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // The original activity who is starting us is running as a single
+ // instance... this new activity it is starting must go on its
+ // own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+ // The activity being started is a single instance... it always
+ // gets launched into its own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+
+ final ActivityStack sourceStack;
+ final TaskRecord sourceTask;
+ if (sourceRecord != null) {
+ sourceTask = sourceRecord.task;
+ sourceStack = sourceTask.stack;
+ } else {
+ sourceTask = null;
+ sourceStack = null;
+ }
+
+ if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ // For whatever reason this activity is being launched into a new
+ // task... yet the caller has requested a result back. Well, that
+ // is pretty messed up, so instead immediately send back a cancel
+ // and let the new task continue launched as normal without a
+ // dependency on its originator.
+ Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+ r.resultTo.task.stack.sendActivityResultLocked(-1,
+ r.resultTo, r.resultWho, r.requestCode,
+ Activity.RESULT_CANCELED, null);
+ r.resultTo = null;
+ }
+
+ boolean addingToTask = false;
+ boolean movedHome = false;
+ TaskRecord reuseTask = null;
+ ActivityStack targetStack;
+ if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
+ (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // If bring to front is requested, and no result is requested, and
+ // we can find a task that was started with this same
+ // component, then instead of launching bring that one to the front.
+ if (r.resultTo == null) {
+ // See if there is a task to bring to the front. If this is
+ // a SINGLE_INSTANCE activity, there can be one and only one
+ // instance of it in the history, and it is always in its own
+ // unique task, so we do a special search.
+ ActivityRecord intentActivity = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
+ ? findTaskLocked(intent, r.info)
+ : findActivityLocked(intent, r.info);
+ if (intentActivity != null) {
+ targetStack = intentActivity.task.stack;
+ moveHomeStack(targetStack.isHomeStack());
+ if (intentActivity.task.intent == null) {
+ // This task was started because of movement of
+ // the activity based on affinity... now that we
+ // are actually launching it, we can assign the
+ // base intent.
+ intentActivity.task.setIntent(intent, r.info);
+ }
+ // If the target task is not in the front, then we need
+ // to bring it to the front... except... well, with
+ // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
+ // to have the same behavior as if a new instance was
+ // being started, which means not bringing it to the front
+ // if the caller is not itself in the front.
+ ActivityRecord curTop =
+ targetStack.topRunningNonDelayedActivityLocked(notTop);
+ if (curTop != null && curTop.task != intentActivity.task) {
+ r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+ if (sourceRecord == null || sourceStack.topActivity() == sourceRecord) {
+ // We really do want to push this one into the
+ // user's face, right now.
+ movedHome = true;
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
+ // Caller wants to appear on home activity, so before starting
+ // their own activity we will bring home to the front.
+ r.mLaunchHomeTaskNext = true;
+ }
+ targetStack.moveTaskToFrontLocked(intentActivity.task, r, options);
+ options = null;
+ }
+ }
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
+ }
+ if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it! And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
+ targetStack.resumeTopActivityLocked(null, options);
+ } else {
+ ActivityOptions.abort(options);
+ }
+ return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+ }
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
+ // The caller has requested to completely replace any
+ // existing task with its new activity. Well that should
+ // not be too hard...
+ reuseTask = intentActivity.task;
+ reuseTask.performClearTaskLocked();
+ reuseTask.setIntent(r.intent, r.info);
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ // In this situation we want to remove all activities
+ // 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 =
+ intentActivity.task.performClearTaskLocked(r, launchFlags);
+ if (top != null) {
+ if (top.frontOfTask) {
+ // Activity aliases may mean we use different
+ // intents for the top activity, so make sure
+ // the task now has the identity of the new
+ // intent.
+ top.task.setIntent(r.intent, r.info);
+ }
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT,
+ r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ } else {
+ // A special case: we need to
+ // start the activity because it is not currently
+ // running, and the caller has asked to clear the
+ // current task to have this activity at the top.
+ addingToTask = true;
+ // Now pretend like this activity is being started
+ // by the top of its task, so it is put in the
+ // right place.
+ sourceRecord = intentActivity;
+ }
+ } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
+ // In this case the top activity on the task is the
+ // same as the one being launched, so we take that
+ // as a request to bring the task to the foreground.
+ // If the top activity in the task is the root
+ // activity, deliver this new intent to it if it
+ // desires.
+ if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
+ && intentActivity.realActivity.equals(r.realActivity)) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
+ intentActivity.task);
+ if (intentActivity.frontOfTask) {
+ intentActivity.task.setIntent(r.intent, r.info);
+ }
+ intentActivity.deliverNewIntentLocked(callingUid, r.intent);
+ } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
+ // In this case we are launching the root activity
+ // of the task, but with a different intent. We
+ // should start a new instance on top.
+ addingToTask = true;
+ sourceRecord = intentActivity;
+ }
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+ // In this case an activity is being launched in to an
+ // existing task, without resetting that task. This
+ // is typically the situation of launching an activity
+ // from a notification or shortcut. We want to place
+ // the new activity on top of the current task.
+ addingToTask = true;
+ sourceRecord = intentActivity;
+ } else if (!intentActivity.task.rootWasReset) {
+ // In this case we are launching in to an existing task
+ // that has not yet been started from its front door.
+ // The current task has been brought to the front.
+ // Ideally, we'd probably like to place this new task
+ // at the bottom of its stack, but that's a little hard
+ // to do with the current organization of the code so
+ // for now we'll just drop it.
+ intentActivity.task.setIntent(r.intent, r.info);
+ }
+ if (!addingToTask && reuseTask == null) {
+ // We didn't do anything... but it was needed (a.k.a., client
+ // don't use that intent!) And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, intentActivity, targetStack);
+ targetStack.resumeTopActivityLocked(null, options);
+ } else {
+ ActivityOptions.abort(options);
+ }
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
+ }
+ }
+ }
+
+ //String uri = r.intent.toURI();
+ //Intent intent2 = new Intent(uri);
+ //Slog.i(TAG, "Given intent: " + r.intent);
+ //Slog.i(TAG, "URI is: " + uri);
+ //Slog.i(TAG, "To intent: " + intent2);
+
+ if (r.packageName != null) {
+ // If the activity being launched is the same as the one currently
+ // at the top, then we need to check if it should only be launched
+ // once.
+ ActivityStack topStack = getTopStack();
+ ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
+ if (top != null && r.resultTo == null) {
+ if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
+ if (top.app != null && top.app.thread != null) {
+ if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top,
+ top.task);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, null, topStack);
+ topStack.resumeTopActivityLocked(null);
+ }
+ ActivityOptions.abort(options);
+ if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it!
+ return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+ }
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ return ActivityManager.START_DELIVERED_TO_TOP;
+ }
+ }
+ }
+ }
+
+ } else {
+ if (r.resultTo != null) {
+ r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
+ r.requestCode, Activity.RESULT_CANCELED, null);
+ }
+ ActivityOptions.abort(options);
+ return ActivityManager.START_CLASS_NOT_FOUND;
+ }
+
+ boolean newTask = false;
+ boolean keepCurTransition = false;
+
+ // Should this be considered a new task?
+ if (r.resultTo == null && !addingToTask
+ && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ targetStack = getCorrectStack(r);
+ moveHomeStack(targetStack.isHomeStack());
+ if (reuseTask == null) {
+ r.setTask(targetStack.createTaskRecord(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);
+ }
+ newTask = true;
+ if (!movedHome) {
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
+ // Caller wants to appear on home activity, so before starting
+ // their own activity we will bring home to the front.
+ r.mLaunchHomeTaskNext = true;
+ }
+ }
+ } else if (sourceRecord != null) {
+ targetStack = sourceRecord.task.stack;
+ moveHomeStack(targetStack.isHomeStack());
+ 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 = sourceRecord.task.performClearTaskLocked(r, launchFlags);
+ keepCurTransition = true;
+ if (top != null) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
+ targetStack.resumeTopActivityLocked(null);
+ }
+ ActivityOptions.abort(options);
+ return ActivityManager.START_DELIVERED_TO_TOP;
+ }
+ } else if (!addingToTask &&
+ (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+ // 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.
+ final ActivityRecord top =
+ targetStack.findActivityInHistoryLocked(r, sourceRecord.task);
+ if (top != null) {
+ final TaskRecord task = top.task;
+ task.moveActivityToFrontLocked(top);
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
+ top.updateOptionsLocked(options);
+ top.deliverNewIntentLocked(callingUid, r.intent);
+ if (doResume) {
+ setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
+ targetStack.resumeTopActivityLocked(null);
+ }
+ return ActivityManager.START_DELIVERED_TO_TOP;
+ }
+ }
+ // 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);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in existing task " + r.task);
+
+ } else {
+ // 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.
+ targetStack = getLastStack();
+ moveHomeStack(targetStack.isHomeStack());
+ ActivityRecord prev = targetStack.topActivity();
+ r.setTask(prev != null ? prev.task
+ : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
+ null, true);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new guessed " + r.task);
+ }
+
+ mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ intent, r.getUriPermissionsLocked());
+
+ if (newTask) {
+ EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
+ }
+ ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
+ setLaunchHomeTaskNextFlag(sourceRecord, r, targetStack);
+ targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
+ return ActivityManager.START_SUCCESS;
+ }
+
+ void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
+ // Just in case.
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ mStacks.get(stackNdx).handleAppDiedLocked(app, restarting);
+ }
+ }
+
+ void closeSystemDialogsLocked() {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.closeSystemDialogsLocked();
+ }
+ }
+
+ /**
+ * @return true if some activity was finished (or would have finished if doit were true).
+ */
+ boolean forceStopPackageLocked(String name, boolean doit, boolean evenPersistent, int userId) {
+ boolean didSomething = false;
+ 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)) {
+ didSomething = true;
+ }
+ }
+ return didSomething;
+ }
+
+ void resumeTopActivityLocked() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ mStacks.get(stackNdx).resumeTopActivityLocked(null);
+ }
+ }
+
+ void finishTopRunningActivityLocked(ProcessRecord app) {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.finishTopRunningActivityLocked(app);
+ }
+ }
+
+ void scheduleIdleLocked() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ mStacks.get(stackNdx).scheduleIdleLocked();
+ }
+ }
+
+ void findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ if (mStacks.get(stackNdx).findTaskToMoveToFrontLocked(taskId, flags, options)) {
+ return;
+ }
+ }
+ }
+
+ 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 createStack(int relativeStackId, int position, float weight) {
+ synchronized (this) {
+ while (true) {
+ if (++mLastStackId <= HOME_STACK_ID) {
+ mLastStackId = HOME_STACK_ID + 1;
+ }
+ if (getStack(mLastStackId) == null) {
+ break;
+ }
+ }
+ mStacks.add(new ActivityStack(mService, mContext, mLooper, mLastStackId, this,
+ mCurrentUser));
+ return mLastStackId;
+ }
+ }
+
+ 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);
+ }
+
+ ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord ar = mStacks.get(stackNdx).findTaskLocked(intent, info);
+ if (ar != null) {
+ return ar;
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord ar = mStacks.get(stackNdx).findActivityLocked(intent, info);
+ if (ar != null) {
+ return ar;
+ }
+ }
+ return null;
+ }
+
+ void goingToSleepLocked() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ mStacks.get(stackNdx).stopIfSleepingLocked();
+ }
+ }
+
+ boolean shutdownLocked(int timeout) {
+ boolean timedout = false;
+ 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 {
+ mService.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ return timedout;
+ }
+
+ void comeOutOfSleepIfNeededLocked() {
+ final boolean homeIsBack = !homeIsInFront();
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.isHomeStack() ^ homeIsBack) {
+ stack.awakeFromSleepingLocked();
+ stack.resumeTopActivityLocked(null);
+ }
+ }
+ }
+
+ void handleAppCrashLocked(ProcessRecord app) {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.handleAppCrashLocked(app);
+ }
+ }
+
+ void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ mStacks.get(stackNdx).ensureActivitiesVisibleLocked(starting, configChanges);
+ }
+ }
+
+ void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ stack.scheduleDestroyActivities(app, false, reason);
+ }
+ }
+
+ boolean switchUserLocked(int userId, UserStartedState uss) {
+ mCurrentUser = userId;
+ boolean homeInBack = !homeIsInFront();
+ boolean haveActivities = false;
+ final int numStacks = mStacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.isHomeStack() ^ homeInBack) {
+ haveActivities |= stack.switchUserLocked(userId, uss);
+ }
+ }
+ return haveActivities;
+ }
+
+ final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
+ int N = mStoppingActivities.size();
+ if (N <= 0) return null;
+
+ ArrayList<ActivityRecord> stops = null;
+
+ final boolean nowVisible = allResumedActivitiesVisible();
+ for (int i=0; i<N; i++) {
+ ActivityRecord s = mStoppingActivities.get(i);
+ if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible="
+ + nowVisible + " waitingVisible=" + s.waitingVisible
+ + " finishing=" + s.finishing);
+ if (s.waitingVisible && nowVisible) {
+ mWaitingVisibleActivities.remove(s);
+ s.waitingVisible = false;
+ if (s.finishing) {
+ // If this activity is finishing, it is sitting on top of
+ // everyone else but we now know it is no longer needed...
+ // so get rid of it. Otherwise, we need to go through the
+ // normal flow and hide it once we determine that it is
+ // hidden by the activities in front of it.
+ if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s);
+ mService.mWindowManager.setAppVisibility(s.appToken, false);
+ }
+ }
+ if ((!s.waitingVisible || mService.isSleepingOrShuttingDown()) && remove) {
+ if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
+ if (stops == null) {
+ stops = new ArrayList<ActivityRecord>();
+ }
+ stops.add(s);
+ mStoppingActivities.remove(i);
+ N--;
+ i--;
+ }
+ }
+
+ return stops;
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mDismissKeyguardOnNextActivity:");
+ pw.println(mDismissKeyguardOnNextActivity);
+ }
+
+ ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
+ return getTopStack().getDumpActivitiesLocked(name);
+ }
+
+ boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage) {
+ 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(" Running activities (most recent first):");
+ dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", 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.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);
+ }
+ }
+
+ if (mStoppingActivities.size() > 0) {
+ pw.println(" ");
+ pw.println(" Activities waiting to stop:");
+ dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll, false,
+ dumpPackage);
+ }
+
+ if (mWaitingVisibleActivities.size() > 0) {
+ pw.println(" ");
+ pw.println(" Activities waiting for another to become visible:");
+ dumpHistoryList(fd, pw, mWaitingVisibleActivities, " ", "Wait", false, !dumpAll,
+ false, dumpPackage);
+ }
+
+ if (dumpAll) {
+ pw.println(" ");
+ pw.println(" mCurTaskId: " + mCurTaskId);
+ }
+ return true;
+ }
+
+ static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
+ String prefix, String label, boolean complete, boolean brief, boolean client,
+ String dumpPackage) {
+ TaskRecord lastTask = null;
+ boolean needNL = false;
+ final String innerPrefix = prefix + " ";
+ final String[] args = new String[0];
+ for (int i=list.size()-1; i>=0; i--) {
+ final ActivityRecord r = list.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
+ continue;
+ }
+ final boolean full = !brief && (complete || !r.isInHistory());
+ if (needNL) {
+ pw.println(" ");
+ needNL = false;
+ }
+ if (lastTask != r.task) {
+ lastTask = r.task;
+ pw.print(prefix);
+ pw.print(full ? "* " : " ");
+ pw.println(lastTask);
+ if (full) {
+ lastTask.dump(pw, prefix + " ");
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ if (lastTask.intent != null) {
+ pw.print(prefix); pw.print(" ");
+ pw.println(lastTask.intent.toInsecureStringWithClip());
+ }
+ }
+ }
+ pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ pw.println(r);
+ if (full) {
+ r.dump(pw, innerPrefix);
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
+ if (r.app != null) {
+ pw.print(innerPrefix); pw.println(r.app);
+ }
+ }
+ if (client && r.app != null && r.app.thread != null) {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(),
+ r.appToken, innerPrefix, args);
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+ } catch (RemoteException e) {
+ pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+ }
+ needNL = true;
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index 3a6492e..64b9572 100644
--- a/services/java/com/android/server/am/CompatModePackages.java
+++ b/services/java/com/android/server/am/CompatModePackages.java
@@ -15,7 +15,6 @@ import com.android.internal.util.FastXmlSerializer;
import android.app.ActivityManager;
import android.app.AppGlobals;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.res.CompatibilityInfo;
@@ -166,7 +165,7 @@ public class CompatModePackages {
}
public boolean getFrontActivityAskCompatModeLocked() {
- ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = mService.getTopStack().topRunningActivityLocked(null);
if (r == null) {
return false;
}
@@ -178,7 +177,7 @@ public class CompatModePackages {
}
public void setFrontActivityAskCompatModeLocked(boolean ask) {
- ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = mService.getTopStack().topRunningActivityLocked(null);
if (r != null) {
setPackageAskCompatModeLocked(r.packageName, ask);
}
@@ -200,7 +199,7 @@ public class CompatModePackages {
}
public int getFrontActivityScreenCompatModeLocked() {
- ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = mService.getTopStack().topRunningActivityLocked(null);
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
}
@@ -208,7 +207,7 @@ public class CompatModePackages {
}
public void setFrontActivityScreenCompatModeLocked(int mode) {
- ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+ ActivityRecord r = mService.getTopStack().topRunningActivityLocked(null);
if (r == null) {
Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
return;
@@ -295,20 +294,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);
- }
- }
- }
+ final ActivityStack stack = mService.getTopStack();
+ ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
for (int i=mService.mLruProcesses.size()-1; i>=0; i--) {
@@ -327,10 +314,10 @@ public class CompatModePackages {
}
if (starting != null) {
- mService.mMainStack.ensureActivityConfigurationLocked(starting, 0);
+ stack.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);
+ stack.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..45bd6d5 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -16,6 +16,11 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityStack.DEBUG_ADD_REMOVE;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -23,6 +28,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 +45,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 +119,154 @@ 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;
+ }
+
+ /**
+ * Reorder the history stack so that the activity at the given index is
+ * brought to the front.
+ */
+ 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());
+
+ getTopActivity().frontOfTask = false;
+ mActivities.remove(newTop);
+ mActivities.add(newTop);
+ newTop.frontOfTask = true;
+ }
+
+ void addActivityAtBottom(ActivityRecord r) {
+ addActivityAtIndex(0, r);
+ }
+
+ void addActivityToTop(ActivityRecord r) {
+ // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
+ 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;
+ }
+
+ /**
+ * Completely remove all activities associated with an existing
+ * task starting at a specified index.
+ */
+ final void performClearTaskAtIndexLocked(int activityNdx) {
+ final ArrayList<ActivityRecord> activities = mActivities;
+ int numActivities = activities.size();
+ for ( ; activityNdx < numActivities; ++activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) {
+ --activityNdx;
+ --numActivities;
+ }
+ }
+ }
+
+ /**
+ * Completely remove all activities associated with an existing task.
+ */
+ final void performClearTaskLocked() {
+ performClearTaskAtIndexLocked(0);
+ }
+
+ /**
+ * Perform clear operation as requested by
+ * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
+ * stack to the given task, then look for
+ * an instance of that activity in the stack and, if found, finish all
+ * activities on top of it and return the instance.
+ *
+ * @param newR Description of the new activity being started.
+ * @return Returns the old activity that should be continued to be used,
+ * or null if none was found.
+ */
+ final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
+ final ArrayList<ActivityRecord> activities = mActivities;
+ int numActivities = activities.size();
+ for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.realActivity.equals(newR.realActivity)) {
+ // Here it is! Now finish everything in front...
+ ActivityRecord ret = r;
+
+ for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
+ r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ ActivityOptions opts = r.takeOptionsLocked();
+ if (opts != null) {
+ ret.updateOptionsLocked(opts);
+ }
+ if (stack.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) {
+ if (!ret.finishing) {
+ if (activities.contains(ret)) {
+ stack.finishActivityLocked(ret, Activity.RESULT_CANCELED, null,
+ "clear", false);
+ }
+ return null;
+ }
+ }
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
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 +293,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,30 +304,35 @@ class TaskRecord extends ThumbnailHolder {
pw.print((getInactiveDuration()/1000)); pw.println("s)");
}
+ @Override
public String toString() {
+ StringBuilder sb = new StringBuilder(128);
if (stringName != null) {
- return stringName;
+ sb.append(stringName);
+ sb.append(" U=");
+ sb.append(userId);
+ sb.append(" sz=");
+ sb.append(mActivities.size());
+ sb.append('}');
+ return sb.toString();
}
- StringBuilder sb = new StringBuilder(128);
sb.append("TaskRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
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(userId);
- sb.append('}');
- return stringName = sb.toString();
+ stringName = sb.toString();
+ return toString();
}
}
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index c5b1c7b..e79faf9 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -18,6 +18,11 @@ package com.android.server.am;
import android.content.Intent;
import android.net.Uri;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.google.android.collect.Sets;
import java.io.PrintWriter;
import java.util.HashSet;
@@ -31,43 +36,171 @@ import java.util.HashSet;
* src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
*/
class UriPermission {
- final int uid;
+ private static final String TAG = "UriPermission";
+
+ final int userHandle;
+ final String sourcePkg;
+ final String targetPkg;
+
+ /** Cached UID of {@link #targetPkg}; should not be persisted */
+ final int targetUid;
+
final Uri uri;
+
+ /**
+ * Allowed modes. All permission enforcement should use this field. Must
+ * always be a superset of {@link #globalModeFlags},
+ * {@link #persistedModeFlags}, {@link #mReadOwners}, and
+ * {@link #mWriteOwners}. Mutations should only be performed by the owning
+ * class.
+ */
int modeFlags = 0;
+
+ /**
+ * Allowed modes without explicit owner. Must always be a superset of
+ * {@link #persistedModeFlags}. Mutations should only be performed by the
+ * owning class.
+ */
int globalModeFlags = 0;
- final HashSet<UriPermissionOwner> readOwners = new HashSet<UriPermissionOwner>();
- final HashSet<UriPermissionOwner> writeOwners = new HashSet<UriPermissionOwner>();
-
- String stringName;
-
- UriPermission(int _uid, Uri _uri) {
- uid = _uid;
- uri = _uri;
+
+ /**
+ * Allowed modes that should be persisted across device boots. These modes
+ * have no explicit owners. Mutations should only be performed by the owning
+ * class.
+ */
+ int persistedModeFlags = 0;
+
+ private HashSet<UriPermissionOwner> mReadOwners;
+ private HashSet<UriPermissionOwner> mWriteOwners;
+
+ private String stringName;
+
+ UriPermission(String sourcePkg, String targetPkg, int targetUid, Uri uri) {
+ this.userHandle = UserHandle.getUserId(targetUid);
+ this.sourcePkg = sourcePkg;
+ this.targetPkg = targetPkg;
+ this.targetUid = targetUid;
+ this.uri = uri;
+ }
+
+ /**
+ * @return If mode changes should trigger persisting.
+ */
+ boolean grantModes(int modeFlagsToGrant, boolean persist, UriPermissionOwner owner) {
+ boolean persistChanged = false;
+
+ modeFlags |= modeFlagsToGrant;
+
+ if (persist) {
+ final int before = persistedModeFlags;
+ persistedModeFlags |= modeFlagsToGrant;
+ persistChanged = persistedModeFlags != before;
+
+ // Treat persisted grants as global (ownerless)
+ owner = null;
+ }
+
+ if (owner == null) {
+ globalModeFlags |= modeFlags;
+ } else {
+ if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ addReadOwner(owner);
+ owner.addReadPermission(this);
+ }
+ if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ addWriteOwner(owner);
+ owner.addWritePermission(this);
+ }
+ }
+
+ return persistChanged;
}
- void clearModes(int modeFlagsToClear) {
- if ((modeFlagsToClear&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ /**
+ * @return If mode changes should trigger persisting.
+ */
+ boolean clearModes(int modeFlagsToClear, boolean persist) {
+ final int before = persistedModeFlags;
+
+ if ((modeFlagsToClear & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (persist) {
+ persistedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ }
globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- if (readOwners.size() > 0) {
- for (UriPermissionOwner r : readOwners) {
+ if (mReadOwners != null) {
+ for (UriPermissionOwner r : mReadOwners) {
r.removeReadPermission(this);
}
- readOwners.clear();
+ mReadOwners = null;
}
}
- if ((modeFlagsToClear&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if ((modeFlagsToClear & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (persist) {
+ persistedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ }
globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- if (writeOwners.size() > 0) {
- for (UriPermissionOwner r : writeOwners) {
+ if (mWriteOwners != null) {
+ for (UriPermissionOwner r : mWriteOwners) {
r.removeWritePermission(this);
}
- writeOwners.clear();
+ mWriteOwners = null;
+ }
+ }
+
+ // Mode flags always bubble up
+ globalModeFlags |= persistedModeFlags;
+ modeFlags |= globalModeFlags;
+
+ return persistedModeFlags != before;
+ }
+
+ private void addReadOwner(UriPermissionOwner owner) {
+ if (mReadOwners == null) {
+ mReadOwners = Sets.newHashSet();
+ }
+ mReadOwners.add(owner);
+ }
+
+ /**
+ * Remove given read owner, updating {@Link #modeFlags} as needed.
+ */
+ void removeReadOwner(UriPermissionOwner owner) {
+ if (!mReadOwners.remove(owner)) {
+ Log.wtf(TAG, "Unknown read owner " + owner + " in " + this);
+ }
+ if (mReadOwners.size() == 0) {
+ mReadOwners = null;
+ if ((globalModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+ modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
}
}
}
-
+
+ private void addWriteOwner(UriPermissionOwner owner) {
+ if (mWriteOwners == null) {
+ mWriteOwners = Sets.newHashSet();
+ }
+ mWriteOwners.add(owner);
+ }
+
+ /**
+ * Remove given write owner, updating {@Link #modeFlags} as needed.
+ */
+ void removeWriteOwner(UriPermissionOwner owner) {
+ if (!mWriteOwners.remove(owner)) {
+ Log.wtf(TAG, "Unknown write owner " + owner + " in " + this);
+ }
+ if (mWriteOwners.size() == 0) {
+ mWriteOwners = null;
+ if ((globalModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+ modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ }
+ }
+ }
+
+ @Override
public String toString() {
if (stringName != null) {
return stringName;
@@ -82,22 +215,55 @@ class UriPermission {
}
void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("modeFlags=0x");
- pw.print(Integer.toHexString(modeFlags));
- pw.print(" uid="); pw.print(uid);
- pw.print(" globalModeFlags=0x");
- pw.println(Integer.toHexString(globalModeFlags));
- if (readOwners.size() != 0) {
- pw.print(prefix); pw.println("readOwners:");
- for (UriPermissionOwner owner : readOwners) {
- pw.print(prefix); pw.print(" * "); pw.println(owner);
+ pw.print(prefix);
+ pw.print("userHandle=" + userHandle);
+ pw.print("sourcePkg=" + sourcePkg);
+ pw.println("targetPkg=" + targetPkg);
+
+ pw.print(prefix);
+ pw.print("modeFlags=0x" + Integer.toHexString(modeFlags));
+ pw.print("globalModeFlags=0x" + Integer.toHexString(globalModeFlags));
+ pw.println("persistedModeFlags=0x" + Integer.toHexString(persistedModeFlags));
+
+ if (mReadOwners != null) {
+ pw.print(prefix);
+ pw.println("readOwners:");
+ for (UriPermissionOwner owner : mReadOwners) {
+ pw.print(prefix);
+ pw.println(" * " + owner);
}
}
- if (writeOwners.size() != 0) {
- pw.print(prefix); pw.println("writeOwners:");
- for (UriPermissionOwner owner : writeOwners) {
- pw.print(prefix); pw.print(" * "); pw.println(owner);
+ if (mWriteOwners != null) {
+ pw.print(prefix);
+ pw.println("writeOwners:");
+ for (UriPermissionOwner owner : mReadOwners) {
+ pw.print(prefix);
+ pw.println(" * " + owner);
}
}
}
+
+ /**
+ * Snapshot of {@link UriPermission} with frozen
+ * {@link UriPermission#persistedModeFlags} state.
+ */
+ public static class Snapshot {
+ final int userHandle;
+ final String sourcePkg;
+ final String targetPkg;
+ final Uri uri;
+ final int persistedModeFlags;
+
+ private Snapshot(UriPermission perm) {
+ this.userHandle = perm.userHandle;
+ this.sourcePkg = perm.sourcePkg;
+ this.targetPkg = perm.targetPkg;
+ this.uri = perm.uri;
+ this.persistedModeFlags = perm.persistedModeFlags;
+ }
+ }
+
+ public Snapshot snapshot() {
+ return new Snapshot(this);
+ }
}
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
index 68a2e0f..90ac88d 100644
--- a/services/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -67,24 +67,16 @@ class UriPermissionOwner {
if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
&& readUriPermissions != null) {
for (UriPermission perm : readUriPermissions) {
- perm.readOwners.remove(this);
- if (perm.readOwners.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- service.removeUriPermissionIfNeededLocked(perm);
- }
+ perm.removeReadOwner(this);
+ service.removeUriPermissionIfNeededLocked(perm);
}
readUriPermissions = null;
}
if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
&& writeUriPermissions != null) {
for (UriPermission perm : writeUriPermissions) {
- perm.writeOwners.remove(this);
- if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- service.removeUriPermissionIfNeededLocked(perm);
- }
+ perm.removeWriteOwner(this);
+ service.removeUriPermissionIfNeededLocked(perm);
}
writeUriPermissions = null;
}
@@ -97,12 +89,8 @@ class UriPermissionOwner {
while (it.hasNext()) {
UriPermission perm = it.next();
if (uri.equals(perm.uri)) {
- perm.readOwners.remove(this);
- if (perm.readOwners.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- service.removeUriPermissionIfNeededLocked(perm);
- }
+ perm.removeReadOwner(this);
+ service.removeUriPermissionIfNeededLocked(perm);
it.remove();
}
}
@@ -116,12 +104,8 @@ class UriPermissionOwner {
while (it.hasNext()) {
UriPermission perm = it.next();
if (uri.equals(perm.uri)) {
- perm.writeOwners.remove(this);
- if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- service.removeUriPermissionIfNeededLocked(perm);
- }
+ perm.removeWriteOwner(this);
+ service.removeUriPermissionIfNeededLocked(perm);
it.remove();
}
}
diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java
index f82cf01..4a5c0d5 100644
--- a/services/java/com/android/server/content/ContentService.java
+++ b/services/java/com/android/server/content/ContentService.java
@@ -36,6 +36,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -61,6 +62,10 @@ public final class ContentService extends IContentService.Stub {
private final Object mSyncManagerLock = new Object();
private SyncManager getSyncManager() {
+ if (SystemProperties.getBoolean("config.disable_network", false)) {
+ return null;
+ }
+
synchronized(mSyncManagerLock) {
try {
// Try to create the SyncManager, return null if it fails (e.g. the disk is full).
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..f154ab3
--- /dev/null
+++ b/services/java/com/android/server/pm/KeySetManager.java
@@ -0,0 +1,555 @@
+/*
+ * 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) {
+ synchronized (mLockObject) {
+ // Get the package's known keys and KeySets
+ Set<Long> deletableKeySets = getKnownKeySetsByPackageNameLocked(packageName);
+ Set<Long> deletableKeys = new HashSet<Long>();
+ Set<Long> knownKeys = null;
+ for (Long ks : deletableKeySets) {
+ knownKeys = mKeySetMapping.get(ks);
+ if (knownKeys != null) {
+ deletableKeys.addAll(knownKeys);
+ }
+ }
+
+ // Now remove the keys and KeySets known to any other package
+ for (String pkgName : mPackages.keySet()) {
+ if (pkgName.equals(packageName)) {
+ continue;
+ }
+ Set<Long> knownKeySets = getKnownKeySetsByPackageNameLocked(pkgName);
+ deletableKeySets.removeAll(knownKeySets);
+ knownKeys = new HashSet<Long>();
+ for (Long ks : knownKeySets) {
+ knownKeys = mKeySetMapping.get(ks);
+ if (knownKeys != null) {
+ deletableKeys.removeAll(knownKeys);
+ }
+ }
+ }
+
+ // 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);
+ }
+
+ // Now remove them from the KeySets known to each package
+ for (String pkgName : mPackages.keySet()) {
+ PackageSetting p = mPackages.get(packageName);
+ for (Long ks : deletableKeySets) {
+ p.keySetData.removeSigningKeySet(ks);
+ p.keySetData.removeDefinedKeySet(ks);
+ }
+ }
+ }
+ }
+
+ private Set<Long> getKnownKeySetsByPackageNameLocked(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..cb60621
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageKeySetData.java
@@ -0,0 +1,125 @@
+/*
+ * 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 removeSigningKeySet(long ks) {
+ if (packageIsSignedBy(ks)) {
+ long[] keysets = new long[mSigningKeySets.length - 1];
+ int index = 0;
+ for (long signingKeySet : mSigningKeySets) {
+ if (signingKeySet != ks) {
+ keysets[index] = signingKeySet;
+ index += 1;
+ }
+ }
+ mSigningKeySets = keysets;
+ }
+ }
+
+ 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 void removeDefinedKeySet(long ks) {
+ if (mKeySetAliases.containsValue(ks)) {
+ long[] keysets = new long[mDefinedKeySets.length - 1];
+ int index = 0;
+ for (long definedKeySet : mDefinedKeySets) {
+ if (definedKeySet != ks) {
+ keysets[index] = definedKeySet;
+ index += 1;
+ }
+ }
+ mDefinedKeySets = keysets;
+ for (String alias : mKeySetAliases.keySet()) {
+ if (mKeySetAliases.get(alias) == ks) {
+ mKeySetAliases.remove(alias);
+ break;
+ }
+ }
+ }
+ }
+
+ 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 1b8ee82..253ffe4 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;
@@ -115,10 +116,12 @@ import android.os.UserManager;
import android.provider.Settings.Secure;
import android.security.KeyStore;
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;
@@ -3330,10 +3333,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
@@ -3480,6 +3485,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
resPath = pkg.mScanPath;
}
+
codePath = pkg.mScanPath;
// Set application objects path explicitly.
setApplicationInfoPaths(pkg, codePath, resPath);
@@ -4516,6 +4522,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;
@@ -8865,7 +8889,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
@@ -8878,6 +8904,7 @@ public class PackageManagerService extends IPackageManager.Stub {
ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, outInfo,
writeSettings);
}
+
return ret;
}
@@ -9716,6 +9743,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;
@@ -9813,6 +9842,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);
@@ -9854,6 +9884,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);
}
}
@@ -9997,7 +10029,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 e64ec6d..b3fd60c 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 2e48074..a9c2ea1 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;
@@ -119,6 +122,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.
@@ -177,6 +182,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());
}
@@ -729,6 +737,7 @@ final class Settings {
} else {
mOtherUserIds.remove(uid);
}
+ setFirstAvailableUid(uid+1);
}
private void replaceUserIdLPw(int uid, Object obj) {
@@ -1331,6 +1340,8 @@ final class Settings {
}
}
+ mKeySetManager.writeKeySetManagerLPr(serializer);
+
serializer.endTag(null, "packages");
serializer.endDocument();
@@ -1521,9 +1532,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) {
@@ -1698,6 +1731,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());
@@ -2293,12 +2328,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);
}
@@ -2477,11 +2522,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 6293dc6..3cccf1d 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -6,7 +6,6 @@ import android.graphics.Matrix;
import android.util.Slog;
import android.util.TimeUtils;
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..5b2cf50 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -16,6 +16,12 @@
package com.android.server.wm;
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerService.TAG;
+
+import android.graphics.Rect;
+import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
@@ -67,6 +73,29 @@ 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();
+
+ /** Array containing the home StackBox and possibly one more which would contain apps */
+ private ArrayList<StackBox> mStackBoxes = new ArrayList<StackBox>();
+
+ /** True when the home StackBox is at the top of mStackBoxes, false otherwise */
+ private TaskStack mHomeStack = null;
+
+ /**
+ * 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 +121,135 @@ class DisplayContent {
return mDisplayInfo;
}
+ boolean homeOnTop() {
+ return mStackBoxes.get(0).mStack != mHomeStack;
+ }
+
+ /**
+ * Retrieve the tasks on this display in stack order from the topmost TaskStack down.
+ * Note that the order of TaskStacks in the same StackBox is defined within StackBox.
+ * @return All the Tasks, in order, on this display.
+ */
+ ArrayList<Task> getTasks() {
+ mTmpTasks.clear();
+ int numBoxes = mStackBoxes.size();
+ for (int boxNdx = 0; boxNdx < numBoxes; ++boxNdx) {
+ mTmpTasks.addAll(mStackBoxes.get(boxNdx).getTasks());
+ }
+ return mTmpTasks;
+ }
+
+ TaskStack getHomeStack() {
+ return mHomeStack;
+ }
+
public void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
}
+ /** @return The number of tokens in all of the Tasks on this display. */
+ int numTokens() {
+ getTasks();
+ int count = 0;
+ for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ count += mTmpTasks.get(taskNdx).mAppTokens.size();
+ }
+ return count;
+ }
+
+ /** Refer to {@link WindowManagerService#createStack(int, int, int, float)} */
+ TaskStack createStack(int stackId, int relativeStackId, int position, float weight) {
+ TaskStack newStack = null;
+ if (DEBUG_STACK) Slog.d(TAG, "createStack: stackId=" + stackId + " relativeStackId="
+ + relativeStackId + " position=" + position + " weight=" + weight);
+ if (mStackBoxes.isEmpty()) {
+ if (stackId != HOME_STACK_ID) {
+ throw new IllegalArgumentException("createStack: First stackId not "
+ + HOME_STACK_ID);
+ }
+ StackBox newBox = new StackBox(this, new Rect(0, 0, mDisplayInfo.logicalWidth,
+ mDisplayInfo.logicalHeight));
+ mStackBoxes.add(newBox);
+ newStack = new TaskStack(stackId, newBox);
+ newBox.mStack = newStack;
+ mHomeStack = 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.
+ if (box.contains(relativeStackId)) {
+ StackBox newBox = new StackBox(this, box.mBounds);
+ newStack = new TaskStack(stackId, newBox);
+ newBox.mStack = newStack;
+ final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0;
+ if (DEBUG_STACK) Slog.d(TAG, "createStack: inserting stack at " +
+ (stackBoxNdx + offset));
+ 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 " + relativeStackId
+ + " not found.");
+ }
+ }
+ return newStack;
+ }
+
+ /** Refer to {@link WindowManagerService#resizeStack(int, float)} */
+ 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;
+ }
+
+ void removeStackBox(StackBox box) {
+ if (DEBUG_STACK) Slog.d(TAG, "removeStackBox: box=" + box);
+ final TaskStack stack = box.mStack;
+ if (stack != null && stack.mStackId == HOME_STACK_ID) {
+ // Never delete the home stack, even if it is empty.
+ if (DEBUG_STACK) Slog.d(TAG, "removeStackBox: Not deleting home stack.");
+ return;
+ }
+ mStackBoxes.remove(box);
+ }
+
+ /**
+ * Move the home StackBox to the top or bottom of mStackBoxes. That is the only place
+ * it is allowed to be. This is a nop if the home StackBox is already in the correct position.
+ * @param toTop Move home to the top of mStackBoxes if true, to the bottom if false.
+ * @return true if a change was made, false otherwise.
+ */
+ boolean moveHomeStackBox(boolean toTop) {
+ if (DEBUG_STACK) Slog.d(TAG, "moveHomeStackBox: toTop=" + toTop);
+ switch (mStackBoxes.size()) {
+ case 0: throw new RuntimeException("moveHomeStackBox: No home StackBox!");
+ case 1: return false; // Only the home StackBox exists.
+ case 2:
+ if (homeOnTop() ^ toTop) {
+ mStackBoxes.add(mStackBoxes.remove(0));
+ return true;
+ }
+ return false;
+ default: throw new RuntimeException("moveHomeStackBox: Too many toplevel StackBoxes!");
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
final String subPrefix = " " + prefix;
@@ -119,7 +273,48 @@ 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);
+ for (int boxNdx = 0; boxNdx < mStackBoxes.size(); ++boxNdx) {
+ pw.print(prefix); pw.print("StackBox #"); pw.println(boxNdx);
+ mStackBoxes.get(boxNdx).dump(prefix + " ", pw);
+ }
+ 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..9525d7c
--- /dev/null
+++ b/services/java/com/android/server/wm/StackBox.java
@@ -0,0 +1,247 @@
+/*
+ * 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 static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+
+import java.io.PrintWriter;
+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;
+
+ /** The display this box sits in. */
+ final DisplayContent mDisplayContent;
+
+ /** Non-null indicates this is mFirst or mSecond of a parent StackBox. Null indicates this
+ * is this entire size of mDisplayContent. */
+ StackBox mParent;
+
+ /** First child, this is null exactly when mStack is non-null. */
+ StackBox mFirst;
+
+ /** Second child, this is null exactly when mStack is non-null. */
+ StackBox mSecond;
+
+ /** Stack of Tasks, this is null exactly when mFirst and mSecond are non-null. */
+ TaskStack mStack;
+
+ /** Content limits relative to the DisplayContent this sits in. */
+ Rect mBounds;
+
+ /** Relative orientation of mFirst and mSecond. */
+ boolean mVertical;
+
+ /** Dirty flag. Something inside this or some descendant of this has changed. */
+ boolean layoutNeeded;
+
+ /** Used to keep from reallocating a temporary array to hold the list of Tasks below */
+ 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();
+ }
+ }
+
+ /**
+ * Detremine if a particular TaskStack is in this StackBox or any of its descendants.
+ * @param stackId The TaskStack being considered.
+ * @return true if the specified TaskStack is in this box or its descendants. False otherwise.
+ */
+ boolean contains(int stackId) {
+ if (mStack != null) {
+ return mStack.mStackId == stackId;
+ }
+ return mFirst.contains(stackId) || mSecond.contains(stackId);
+ }
+
+ /**
+ * Create a new TaskStack relative to a specified one by splitting the StackBox containing
+ * the specified TaskStack into two children. The size and position each of the new StackBoxes
+ * is determined by the passed parameters.
+ * @param stackId The id of the new TaskStack to create.
+ * @param relativeStackId The id of the TaskStack to place the new one next to.
+ * @param position One of the static TASK_STACK_GOES_xxx positions defined in this class.
+ * @param weight The percentage size of the parent StackBox to devote to the new TaskStack.
+ * @return The new TaskStack.
+ */
+ 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;
+ }
+ // Not the intended TaskStack.
+ 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);
+ }
+
+ /**
+ * @return List of all Tasks underneath this StackBox. The order is currently mFirst followed
+ * by mSecond putting mSecond Tasks more recent than mFirst Tasks.
+ * TODO: Change to MRU ordering.
+ */
+ ArrayList<Task> getTasks() {
+ mTmpTasks.clear();
+ if (mStack != null) {
+ mTmpTasks.addAll(mStack.getTasks());
+ } else {
+ mTmpTasks.addAll(mFirst.getTasks());
+ mTmpTasks.addAll(mSecond.getTasks());
+ }
+ return mTmpTasks;
+ }
+
+ /** Combine a child StackBox into its parent.
+ * @param child The surviving child to be merge up into this StackBox. */
+ void absorb(StackBox child) {
+ mFirst = child.mFirst;
+ mSecond = child.mSecond;
+ mStack = child.mStack;
+ layoutNeeded = true;
+ }
+
+ /** Return the stackId of the first mFirst StackBox with a non-null mStack */
+ int getStackId() {
+ if (mStack != null) {
+ return mStack.mStackId;
+ }
+ return mFirst.getStackId();
+ }
+
+ /** Remove this box and propagate its sibling's content up to their parent.
+ * @return The first stackId of the resulting StackBox. */
+ int removeStack() {
+ if (mParent == null) {
+ mDisplayContent.removeStackBox(this);
+ return HOME_STACK_ID;
+ }
+ if (mParent.mFirst == this) {
+ mParent.absorb(mParent.mSecond);
+ } else {
+ mParent.absorb(mParent.mFirst);
+ }
+ mParent.makeDirty();
+ return mParent.getStackId();
+ }
+
+ /** TODO: */
+ boolean resize(int stackId, float weight) {
+ return false;
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mParent="); pw.println(mParent);
+ pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
+ pw.print(" mVertical="); pw.print(mVertical);
+ pw.print(" layoutNeeded="); pw.println(layoutNeeded);
+ if (mFirst != null) {
+ pw.print(prefix); pw.print("mFirst="); pw.println(mStack);
+ mFirst.dump(prefix + " ", pw);
+ pw.print(prefix); pw.print("mSecond="); pw.println(mStack);
+ mSecond.dump(prefix + " ", pw);
+ } else {
+ pw.print(prefix); pw.print("mStack="); pw.println(mStack);
+ mStack.dump(prefix + " ", pw);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (mStack != null) {
+ return "Box{" + hashCode() + " stack=" + mStack.mStackId + "}";
+ }
+ return "Box{" + hashCode() + " parent=" + mParent.hashCode()
+ + " first=" + mFirst.hashCode() + " second=" + mSecond.hashCode() + "}";
+ }
+}
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..2520f31
--- /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 "taskId=" + 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..3e5a933
--- /dev/null
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -0,0 +1,112 @@
+/*
+ * 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 static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class TaskStack {
+ /** Unique identifier */
+ final int mStackId;
+
+ /** The display this stack sits under. */
+ private final DisplayContent mDisplayContent;
+
+ /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
+ * mTaskHistory in the ActivityStack with the same mStackId */
+ private ArrayList<Task> mTasks = new ArrayList<Task>();
+
+ /** The StackBox this sits in. */
+ private final 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;
+ }
+
+ boolean isHomeStack() {
+ return mStackId == HOME_STACK_ID;
+ }
+
+ /**
+ * Put a Task in this stack. Used for adding and moving.
+ * @param task The task to add.
+ * @param toTop Whether to add it to the top or bottom.
+ */
+ boolean addTask(Task task, boolean toTop) {
+ mParent.makeDirty();
+ mTasks.add(toTop ? mTasks.size() : 0, task);
+ return mDisplayContent.moveHomeStackBox(mStackId == HOME_STACK_ID);
+ }
+
+ boolean moveTaskToTop(Task task) {
+ mTasks.remove(task);
+ return addTask(task, true);
+ }
+
+ boolean moveTaskToBottom(Task task) {
+ mTasks.remove(task);
+ return addTask(task, false);
+ }
+
+ /**
+ * Delete a Task from this stack. If it is the last Task in the stack, remove this stack from
+ * its parent StackBox and merge the parent.
+ * @param task The Task to delete.
+ */
+ void removeTask(Task task) {
+ mParent.makeDirty();
+ mTasks.remove(task);
+ }
+
+ int remove() {
+ return mParent.removeStack();
+ }
+
+ int numTokens() {
+ int count = 0;
+ for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ count += mTasks.get(taskNdx).mAppTokens.size();
+ }
+ return count;
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
+ for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
+ pw.print(prefix); pw.println(mTasks.get(taskNdx));
+ }
+ }
+}
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 054a075..0fb4c71 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;
@@ -173,28 +172,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)) {
@@ -455,39 +460,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();
+ }
}
}
}
@@ -531,11 +540,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 =
@@ -561,10 +569,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 0d67358..ad49d0a 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -16,29 +16,9 @@
package com.android.server.wm;
-import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.*;
+
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -200,6 +180,8 @@ 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 DEBUG_STACK = false;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -209,6 +191,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. */
@@ -337,34 +322,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,
@@ -449,9 +407,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;
@@ -631,6 +591,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);
@@ -860,10 +823,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;
}
@@ -920,218 +887,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;
+ }
+
+ // 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);
}
- 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;
}
+ 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);
@@ -1546,10 +1555,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) : "??")
@@ -2223,7 +2228,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);
@@ -2314,9 +2319,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG, "Remove " + win + " client="
- + Integer.toHexString(System.identityHashCode(
- win.mClient.asBinder()))
- + ", surface=" + win.mWinAnimator.mSurfaceControl);
+ + Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
+ + ", surface=" + win.mWinAnimator.mSurfaceControl,
+ new RuntimeException("here").fillInStackTrace());
final long origId = Binder.clearCallingIdentity();
@@ -2495,22 +2500,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();
- }
}
}
@@ -3157,35 +3155,71 @@ 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(int stackId, 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 = mStackIdToStack.get(stackId).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--;
}
}
+ public void validateStackOrder(Integer[] remoteStackIds) {
+ // TODO:
+ }
+
boolean checkCallingPermission(String permission, String func) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) {
@@ -3246,6 +3280,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;
@@ -3255,6 +3290,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;
@@ -3264,13 +3300,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;
}
}
@@ -3283,7 +3318,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);
}
@@ -3297,27 +3332,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");
@@ -3345,14 +3362,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.
@@ -3371,12 +3400,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);
}
}
@@ -3417,72 +3455,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;
@@ -4346,11 +4388,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
@@ -4360,8 +4404,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;
@@ -4413,78 +4459,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;
@@ -4533,198 +4592,162 @@ 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*/);
+ private void moveHomeTasksLocked(boolean toTop) {
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ if (toTop ^ displayContent.homeOnTop()) {
+ final ArrayList<Task> tasks = displayContent.getHomeStack().getTasks();
+ final int numTasks = tasks.size();
+ for (int i = 0; i < numTasks; ++i) {
+ if (toTop) {
+ // Keep pulling the bottom task off and moving it to the top.
+ moveTaskToTop(tasks.get(0).taskId);
+ } else {
+ // Keep pulling the top task off and moving it to the bottom.
+ moveTaskToBottom(tasks.get(numTasks - 1).taskId);
}
- 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;
+ }
+ final TaskStack stack = task.mStack;
+ final DisplayContent displayContent = task.getDisplayContent();
+ final boolean isHomeStackTask = stack.isHomeStack();
+ final boolean homeIsOnTop = displayContent.homeOnTop();
+ if (!isHomeStackTask && homeIsOnTop) {
+ // First move move the home tasks all to the bottom to rearrange the windows.
+ moveHomeTasksLocked(false);
+ // Now move the stack itself.
+ displayContent.moveHomeStackBox(false);
+ } else if (isHomeStackTask && !homeIsOnTop) {
+ // Move the stack to the top.
+ displayContent.moveHomeStackBox(true);
+ }
+ stack.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);
}
- 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 int removeStack(int stackId) {
+ final TaskStack stack = mStackIdToStack.get(stackId);
+ if (stack != null) {
+ mStackIdToStack.delete(stackId);
+ return stack.remove();
}
+ return HOME_STACK_ID;
+ }
- 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 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();
+ }
+ }
+
+ 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);
}
// -------------------------------------------------------------
@@ -4846,9 +4869,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);
@@ -5242,9 +5265,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;
@@ -6226,9 +6249,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;
}
@@ -6778,10 +6801,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
}
@@ -7067,8 +7090,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();
}
}
@@ -7113,13 +7134,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);
+ }
}
}
}
@@ -7653,8 +7677,7 @@ public class WindowManagerService extends IWindowManager.Stub
win.mRebuilding = true;
mRebuildTmp[numRemoved] = win;
mWindowsChanged = true;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
- "Rebuild removing window: " + win);
+ if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Rebuild removing window: " + win);
NW--;
numRemoved++;
continue;
@@ -7675,21 +7698,28 @@ 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;
if (i != numRemoved) {
- Slog.w(TAG, "Rebuild removed " + numRemoved
- + " windows but added " + i);
+ Slog.w(TAG, "Rebuild removed " + numRemoved + " windows but added " + i,
+ new RuntimeException("here").fillInStackTrace());
for (i=0; i<numRemoved; i++) {
WindowState ws = mRebuildTmp[i];
if (ws.mRebuilding) {
@@ -7703,7 +7733,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
Slog.w(TAG, "Current app token list:");
- dumpAnimatingAppTokensLocked();
+ dumpAppTokensLocked();
Slog.w(TAG, "Final window list:");
dumpWindowsLocked();
}
@@ -8354,11 +8384,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;
@@ -8517,21 +8553,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;
+ }
}
}
}
@@ -8555,13 +8595,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;
@@ -8590,10 +8634,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();
@@ -8837,10 +8881,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (!mInnerFields.mDimming && mAnimator.isDimmingLocked(displayId)) {
stopDimmingLocked(displayId);
}
- }
- if (updateAllDrawn) {
- updateAllDrawnLocked();
+ if (updateAllDrawn) {
+ updateAllDrawnLocked(displayContent);
+ }
}
if (focusDisplayed) {
@@ -9011,30 +9055,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);
+ }
}
}
@@ -9054,7 +9106,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) {
@@ -9306,10 +9358,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)) {
@@ -9342,9 +9394,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;
}
@@ -9470,8 +9522,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--) {
@@ -9487,8 +9551,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;
}
@@ -9497,18 +9561,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;
}
}
@@ -9516,8 +9587,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;
}
}
@@ -9898,15 +9971,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:");
@@ -9922,51 +9986,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) {
@@ -10011,9 +10030,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(":");
@@ -10206,9 +10225,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);
}
@@ -10223,9 +10242,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);
@@ -10478,6 +10497,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();
@@ -10497,7 +10520,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- final static boolean REVERSE_ITERATOR = true;
class AllWindowsIterator implements Iterator<WindowState> {
private DisplayContent mDisplayContent;
private DisplayContentsIterator mDisplayContentsIterator;
@@ -10506,19 +10528,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 788d514..7ea36ef 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -57,6 +57,12 @@ import java.io.PrintWriter;
import java.util.ArrayList;
class WindowList extends ArrayList<WindowState> {
+ WindowList() {
+ super();
+ }
+ WindowList(WindowList windows) {
+ super(windows);
+ }
}
/**
@@ -686,6 +692,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
@@ -1117,7 +1130,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) {
@@ -1127,13 +1140,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