diff options
Diffstat (limited to 'services')
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 |