Merge "Add implementations for saveLayerAlpha() and textured rects."
diff --git a/api/current.xml b/api/current.xml
index 3cf3df6..8d89bdc 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -23711,6 +23711,18 @@
<parameter name="context" type="android.content.Context">
</parameter>
</constructor>
+<constructor name="AlertDialog.Builder"
+ type="android.app.AlertDialog.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="theme" type="int">
+</parameter>
+</constructor>
<method name="create"
return="android.app.AlertDialog"
abstract="false"
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 2714de5..61a8fc3 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -265,12 +265,22 @@
public static class Builder {
private final AlertController.AlertParams P;
+ private int mTheme;
/**
* Constructor using a context for this builder and the {@link AlertDialog} it creates.
*/
public Builder(Context context) {
+ this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+ }
+
+ /**
+ * Constructor using a context and theme for this builder and
+ * the {@link AlertDialog} it creates.
+ */
+ public Builder(Context context, int theme) {
P = new AlertController.AlertParams(context);
+ mTheme = theme;
}
/**
@@ -783,7 +793,7 @@
* to do and want this to be created and displayed.
*/
public AlertDialog create() {
- final AlertDialog dialog = new AlertDialog(P.mContext);
+ final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
dialog.setOnCancelListener(P.mOnCancelListener);
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index b1f33ec..80eb18b 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -187,7 +187,7 @@
* @param window
*/
public void fillWindow(int position, CursorWindow window) {
- if (position < 0 || position > getCount()) {
+ if (position < 0 || position >= getCount()) {
return;
}
window.acquireReference();
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index dde0889..89b3cba 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -496,7 +496,7 @@
}
}
} else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
- count = duration / DAY_IN_MILLIS;
+ count = getNumberOfDaysPassed(time, now);
if (past) {
if (abbrevRelative) {
resId = com.android.internal.R.plurals.abbrev_num_days_ago;
@@ -521,6 +521,24 @@
}
/**
+ * Returns the number of days passed between two dates.
+ *
+ * @param date1 first date
+ * @param date2 second date
+ * @return number of days passed between to dates.
+ */
+ private synchronized static long getNumberOfDaysPassed(long date1, long date2) {
+ if (sThenTime == null) {
+ sThenTime = new Time();
+ }
+ sThenTime.set(date1);
+ int day1 = Time.getJulianDay(date1, sThenTime.gmtoff);
+ sThenTime.set(date2);
+ int day2 = Time.getJulianDay(date2, sThenTime.gmtoff);
+ return Math.abs(day2 - day1);
+ }
+
+ /**
* Return string describing the elapsed time since startTime formatted like
* "[relative time/date], [time]".
* <p>
@@ -1550,40 +1568,45 @@
public static CharSequence getRelativeTimeSpanString(Context c, long millis,
boolean withPreposition) {
+ String result;
long now = System.currentTimeMillis();
long span = now - millis;
- if (sNowTime == null) {
- sNowTime = new Time();
- sThenTime = new Time();
- }
+ synchronized (DateUtils.class) {
+ if (sNowTime == null) {
+ sNowTime = new Time();
+ }
- sNowTime.set(now);
- sThenTime.set(millis);
+ if (sThenTime == null) {
+ sThenTime = new Time();
+ }
- String result;
- int prepositionId;
- if (span < DAY_IN_MILLIS && sNowTime.weekDay == sThenTime.weekDay) {
- // Same day
- int flags = FORMAT_SHOW_TIME;
- result = formatDateRange(c, millis, millis, flags);
- prepositionId = R.string.preposition_for_time;
- } else if (sNowTime.year != sThenTime.year) {
- // Different years
- int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
- result = formatDateRange(c, millis, millis, flags);
+ sNowTime.set(now);
+ sThenTime.set(millis);
- // This is a date (like "10/31/2008" so use the date preposition)
- prepositionId = R.string.preposition_for_date;
- } else {
- // Default
- int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
- result = formatDateRange(c, millis, millis, flags);
- prepositionId = R.string.preposition_for_date;
- }
- if (withPreposition) {
- Resources res = c.getResources();
- result = res.getString(prepositionId, result);
+ int prepositionId;
+ if (span < DAY_IN_MILLIS && sNowTime.weekDay == sThenTime.weekDay) {
+ // Same day
+ int flags = FORMAT_SHOW_TIME;
+ result = formatDateRange(c, millis, millis, flags);
+ prepositionId = R.string.preposition_for_time;
+ } else if (sNowTime.year != sThenTime.year) {
+ // Different years
+ int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
+ result = formatDateRange(c, millis, millis, flags);
+
+ // This is a date (like "10/31/2008" so use the date preposition)
+ prepositionId = R.string.preposition_for_date;
+ } else {
+ // Default
+ int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
+ result = formatDateRange(c, millis, millis, flags);
+ prepositionId = R.string.preposition_for_date;
+ }
+ if (withPreposition) {
+ Resources res = c.getResources();
+ result = res.getString(prepositionId, result);
+ }
}
return result;
}
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png
new file mode 100644
index 0000000..950713b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png
new file mode 100644
index 0000000..c44d062
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png
Binary files differ
diff --git a/include/media/AudioCommon.h b/include/media/AudioCommon.h
deleted file mode 100644
index 245d760..0000000
--- a/include/media/AudioCommon.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef ANDROID_AUDIOCOMMON_H_
-#define ANDROID_AUDIOCOMMON_H_
-
-#if __cplusplus
-extern "C" {
-#endif
-
-/////////////////////////////////////////////////
-// Common definitions for PCM audio
-/////////////////////////////////////////////////
-
-
-// PCM Sample format
-enum audio_format_e {
- PCM_FORMAT_S15 = 1, // PCM signed 16 bits, must be 1 for backward compatibility
- PCM_FORMAT_U8 = 2, // PCM unsigned 8 bits, must be 2 for backward compatibility
- PCM_FORMAT_S7_24 // signed 7.24 fixed point representation
-};
-
-// Channel mask definitions
-enum audio_channels_e {
- CHANNEL_FRONT_LEFT = 0x4, // front left channel
- CHANNEL_FRONT_RIGHT = 0x8, // front right channel
- CHANNEL_FRONT_CENTER = 0x10, // front center channel
- CHANNEL_LOW_FREQUENCY = 0x20, // low frequency channel
- CHANNEL_BACK_LEFT = 0x40, // back left channel
- CHANNEL_BACK_RIGHT = 0x80, // back right channel
- CHANNEL_FRONT_LEFT_OF_CENTER = 0x100, // front left of center channel
- CHANNEL_FRONT_RIGHT_OF_CENTER = 0x200, // front right of center channel
- CHANNEL_BACK_CENTER = 0x400, // back center channel
- CHANNEL_MONO = CHANNEL_FRONT_LEFT,
- CHANNEL_STEREO = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT),
- CHANNEL_QUAD = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
- CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
- CHANNEL_SURROUND = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
- CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER),
- CHANNEL_5POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
- CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
- CHANNEL_7POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
- CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
- CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER),
-};
-
-// Render device definitions
-enum audio_device_e {
- DEVICE_EARPIECE = 0x1, // earpiece
- DEVICE_SPEAKER = 0x2, // speaker
- DEVICE_WIRED_HEADSET = 0x4, // wired headset, with microphone
- DEVICE_WIRED_HEADPHONE = 0x8, // wired headphone, without microphone
- DEVICE_BLUETOOTH_SCO = 0x10, // generic bluetooth SCO
- DEVICE_BLUETOOTH_SCO_HEADSET = 0x20, // bluetooth SCO headset
- DEVICE_BLUETOOTH_SCO_CARKIT = 0x40, // bluetooth SCO car kit
- DEVICE_BLUETOOTH_A2DP = 0x80, // generic bluetooth A2DP
- DEVICE_BLUETOOTH_A2DP_HEADPHONES = 0x100, // bluetooth A2DP headphones
- DEVICE_BLUETOOTH_A2DP_SPEAKER = 0x200, // bluetooth A2DP speakers
- DEVICE_AUX_DIGITAL = 0x400 // digital output
-};
-
-#if __cplusplus
-} // extern "C"
-#endif
-
-
-#endif /*ANDROID_AUDIOCOMMON_H_*/
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 2bdba2d..66670f3 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -93,14 +93,14 @@
/*
* Returns the number of effects available. This method together
- * with EffectQueryNext() is used to enumerate all effects:
+ * with queryEffect() is used to enumerate all effects:
* The enumeration sequence is:
- * QueryNumberEffects(&num_effects);
- * while (num_effects--)
- * QueryNextEffect();
+ * queryNumberEffects(&num_effects);
+ * for (i = 0; i < num_effects; i++)
+ * queryEffect(i,...);
*
* Parameters:
- * pNumEffects: address where the number of effects should be returned.
+ * numEffects: address where the number of effects should be returned.
*
* Returned status (from utils/Errors.h) can be:
* NO_ERROR successful operation.
@@ -114,24 +114,24 @@
static status_t queryNumberEffects(uint32_t *numEffects);
/*
- * Returns number effect descriptor during effect
+ * Returns an effect descriptor during effect
* enumeration.
*
* Parameters:
- * pDescriptor: address where the effect descriptor should be returned.
+ * index: index of the queried effect.
+ * descriptor: address where the effect descriptor should be returned.
*
* Returned status (from utils/Errors.h) can be:
* NO_ERROR successful operation.
- * NAME_NOT_FOUND no more effect available
* PERMISSION_DENIED could not get AudioFlinger interface
* NO_INIT effect library failed to initialize
- * BAD_VALUE invalid descriptor pointer
+ * BAD_VALUE invalid descriptor pointer or index
* INVALID_OPERATION effect list has changed since last execution of queryNumberEffects()
*
* Returned value
* *descriptor: updated with effect descriptor
*/
- static status_t queryNextEffect(effect_descriptor_t *descriptor);
+ static status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor);
/*
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index f21e83d..194f23a 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -20,7 +20,6 @@
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <media/IAudioFlinger.h>
-#include <media/AudioCommon.h>
namespace android {
@@ -51,8 +50,8 @@
// Audio sub formats (see AudioSystem::audio_format).
enum pcm_sub_format {
- PCM_SUB_16_BIT = PCM_FORMAT_S15, // must be 1 for backward compatibility
- PCM_SUB_8_BIT = PCM_FORMAT_U8, // must be 2 for backward compatibility
+ PCM_SUB_16_BIT = 0x1, // must be 1 for backward compatibility
+ PCM_SUB_8_BIT = 0x2, // must be 2 for backward compatibility
};
// MP3 sub format field definition : can use 11 LSBs in the same way as MP3 frame header to specify
@@ -104,21 +103,26 @@
// Channel mask definitions must be kept in sync with JAVA values in /media/java/android/media/AudioFormat.java
enum audio_channels {
// output channels
- CHANNEL_OUT_FRONT_LEFT = CHANNEL_FRONT_LEFT,
- CHANNEL_OUT_FRONT_RIGHT = CHANNEL_FRONT_RIGHT,
- CHANNEL_OUT_FRONT_CENTER = CHANNEL_FRONT_CENTER,
- CHANNEL_OUT_LOW_FREQUENCY = CHANNEL_LOW_FREQUENCY,
- CHANNEL_OUT_BACK_LEFT = CHANNEL_BACK_LEFT,
- CHANNEL_OUT_BACK_RIGHT = CHANNEL_BACK_RIGHT,
- CHANNEL_OUT_FRONT_LEFT_OF_CENTER = CHANNEL_FRONT_LEFT_OF_CENTER,
- CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = CHANNEL_FRONT_RIGHT_OF_CENTER,
- CHANNEL_OUT_BACK_CENTER = CHANNEL_BACK_CENTER,
- CHANNEL_OUT_MONO = CHANNEL_MONO,
- CHANNEL_OUT_STEREO = CHANNEL_STEREO,
- CHANNEL_OUT_QUAD = CHANNEL_QUAD,
- CHANNEL_OUT_SURROUND = CHANNEL_SURROUND,
- CHANNEL_OUT_5POINT1 = CHANNEL_5POINT1,
- CHANNEL_OUT_7POINT1 = CHANNEL_7POINT1,
+ CHANNEL_OUT_FRONT_LEFT = 0x4,
+ CHANNEL_OUT_FRONT_RIGHT = 0x8,
+ CHANNEL_OUT_FRONT_CENTER = 0x10,
+ CHANNEL_OUT_LOW_FREQUENCY = 0x20,
+ CHANNEL_OUT_BACK_LEFT = 0x40,
+ CHANNEL_OUT_BACK_RIGHT = 0x80,
+ CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100,
+ CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200,
+ CHANNEL_OUT_BACK_CENTER = 0x400,
+ CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
+ CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT),
+ CHANNEL_OUT_QUAD = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT),
+ CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER),
+ CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT),
+ CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
+ CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
CHANNEL_OUT_ALL = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | CHANNEL_OUT_BACK_CENTER),
@@ -238,17 +242,17 @@
enum audio_devices {
// output devices
- DEVICE_OUT_EARPIECE = DEVICE_EARPIECE,
- DEVICE_OUT_SPEAKER = DEVICE_SPEAKER,
- DEVICE_OUT_WIRED_HEADSET = DEVICE_WIRED_HEADSET,
- DEVICE_OUT_WIRED_HEADPHONE = DEVICE_WIRED_HEADPHONE,
- DEVICE_OUT_BLUETOOTH_SCO = DEVICE_BLUETOOTH_SCO,
- DEVICE_OUT_BLUETOOTH_SCO_HEADSET = DEVICE_BLUETOOTH_SCO_HEADSET,
- DEVICE_OUT_BLUETOOTH_SCO_CARKIT = DEVICE_BLUETOOTH_SCO_CARKIT,
- DEVICE_OUT_BLUETOOTH_A2DP = DEVICE_BLUETOOTH_A2DP,
- DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = DEVICE_BLUETOOTH_A2DP_HEADPHONES,
- DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = DEVICE_BLUETOOTH_A2DP_SPEAKER,
- DEVICE_OUT_AUX_DIGITAL = DEVICE_AUX_DIGITAL,
+ DEVICE_OUT_EARPIECE = 0x1,
+ DEVICE_OUT_SPEAKER = 0x2,
+ DEVICE_OUT_WIRED_HEADSET = 0x4,
+ DEVICE_OUT_WIRED_HEADPHONE = 0x8,
+ DEVICE_OUT_BLUETOOTH_SCO = 0x10,
+ DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
+ DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
+ DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
+ DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
+ DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
+ DEVICE_OUT_AUX_DIGITAL = 0x400,
DEVICE_OUT_DEFAULT = 0x8000,
DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |
DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
diff --git a/include/media/EffectApi.h b/include/media/EffectApi.h
index 97874f7..b4d738c 100644
--- a/include/media/EffectApi.h
+++ b/include/media/EffectApi.h
@@ -20,7 +20,6 @@
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
-#include <media/AudioCommon.h>
#if __cplusplus
extern "C" {
@@ -65,10 +64,11 @@
//--- Effect descriptor structure effect_descriptor_t
//
-// Unique effect ID (can be generated from the following site: http://www.itu.int/ITU-T/asn1/uuid.html)
+// Unique effect ID (can be generated from the following site:
+// http://www.itu.int/ITU-T/asn1/uuid.html)
// This format is used for both "type" and "uuid" fields of the effect descriptor structure.
-// - When used for effect type and the engine is implementing and effect corresponding to a standard OpenSL ES
-// interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
+// - When used for effect type and the engine is implementing and effect corresponding to a standard
+// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
// - When used as uuid, it should be a unique UUID for this particular implementation.
typedef struct effect_uuid_s {
uint32_t timeLow;
@@ -79,23 +79,32 @@
} effect_uuid_t;
// NULL UUID definition (matches SL_IID_NULL_)
-#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
+#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \
+ { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER;
const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_;
const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210";
-// the effect descriptor contains necessary information to facilitate the enumeration of the effect
+// The effect descriptor contains necessary information to facilitate the enumeration of the effect
// engines present in a library.
typedef struct effect_descriptor_s {
- effect_uuid_t type; // UUID corresponding to the OpenSL ES interface implemented by this effect
+ effect_uuid_t type; // UUID of to the OpenSL ES interface implemented by this effect
effect_uuid_t uuid; // UUID for this particular implementation
- uint16_t apiVersion; // Version of the effect API implemented: must match current EFFECT_API_VERSION
+ uint16_t apiVersion; // Version of the effect API implemented: matches EFFECT_API_VERSION
uint32_t flags; // effect engine capabilities/requirements flags (see below)
- char name[EFFECT_STRING_LEN_MAX] ; // human readable effect name
- char implementor[EFFECT_STRING_LEN_MAX] ; // human readable effect implementor name
+ uint16_t cpuLoad; // CPU load indication (see below)
+ uint16_t memoryUsage; // Data Memory usage (see below)
+ char name[EFFECT_STRING_LEN_MAX]; // human readable effect name
+ char implementor[EFFECT_STRING_LEN_MAX]; // human readable effect implementor name
} effect_descriptor_t;
-// definitions for flags field of effect descriptor.
+// CPU load and memory usage indication: each effect implementation must provide an indication of
+// its CPU and memory usage for the audio effect framework to limit the number of effects
+// instantiated at a given time on a given platform.
+// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS.
+// The memory usage is expressed in KB and includes only dynamically allocated memory
+
+// Definitions for flags field of effect descriptor.
// +---------------------------+-----------+-----------------------------------
// | description | bits | values
// +---------------------------+-----------+-----------------------------------
@@ -117,7 +126,7 @@
// | | | 2 requires volume indication
// | | | 3 reserved
// +---------------------------+-----------+-----------------------------------
-// | Device management | 7..8 | 0 none
+// | Device indication | 7..8 | 0 none
// | | | 1 requires device updates
// | | | 2..3 reserved
// +---------------------------+-----------+-----------------------------------
@@ -125,7 +134,8 @@
// | | | command must specify a buffer descriptor
// | | | 1 provider: process() function uses the
// | | | bufferProvider indicated by the
-// | | | EFFECT_CMD_CONFIGURE command to request input buffers.
+// | | | EFFECT_CMD_CONFIGURE command to request input.
+// | | | buffers.
// | | | 2 both: both input modes are supported
// | | | 3 reserved
// +---------------------------+-----------+-----------------------------------
@@ -133,18 +143,34 @@
// | | | command must specify a buffer descriptor
// | | | 1 provider: process() function uses the
// | | | bufferProvider indicated by the
-// | | | EFFECT_CMD_CONFIGURE command to request output buffers.
+// | | | EFFECT_CMD_CONFIGURE command to request output
+// | | | buffers.
// | | | 2 both: both output modes are supported
// | | | 3 reserved
// +---------------------------+-----------+-----------------------------------
+// | Hardware acceleration | 13..15 | 0 No hardware acceleration
+// | | | 1 non tunneled hw acceleration: the process() function
+// | | | reads the samples, send them to HW accelerated
+// | | | effect processor, reads back the processed samples
+// | | | and returns them to the output buffer.
+// | | | 2 tunneled hw acceleration: the process() function is
+// | | | transparent. The effect interface is only used to
+// | | | control the effect engine. This mode is relevant for
+// | | | global effects actually applied by the audio
+// | | | hardware on the output stream.
+// +---------------------------+-----------+-----------------------------------
+// | Audio Mode indication | 16..17 | 0 none
+// | | | 1 requires audio mode updates
+// | | | 2..3 reserved
+// +---------------------------+-----------+-----------------------------------
-// insert mode
+// Insert mode
#define EFFECT_FLAG_TYPE_MASK 0x00000003
#define EFFECT_FLAG_TYPE_INSERT 0x00000000
#define EFFECT_FLAG_TYPE_AUXILIARY 0x00000001
#define EFFECT_FLAG_TYPE_REPLACE 0x00000002
-// insert preference
+// Insert preference
#define EFFECT_FLAG_INSERT_MASK 0x0000001C
#define EFFECT_FLAG_INSERT_ANY 0x00000000
#define EFFECT_FLAG_INSERT_FIRST 0x00000004
@@ -152,30 +178,40 @@
#define EFFECT_FLAG_INSERT_EXCLUSIVE 0x0000000C
-// volume control
+// Volume control
#define EFFECT_FLAG_VOLUME_MASK 0x00000060
#define EFFECT_FLAG_VOLUME_CTRL 0x00000020
#define EFFECT_FLAG_VOLUME_IND 0x00000040
#define EFFECT_FLAG_VOLUME_NONE 0x00000000
-// device control
+// Device indication
#define EFFECT_FLAG_DEVICE_MASK 0x00000180
#define EFFECT_FLAG_DEVICE_IND 0x00000080
#define EFFECT_FLAG_DEVICE_NONE 0x00000000
-// sample input modes
+// Sample input modes
#define EFFECT_FLAG_INPUT_MASK 0x00000600
#define EFFECT_FLAG_INPUT_DIRECT 0x00000000
#define EFFECT_FLAG_INPUT_PROVIDER 0x00000200
#define EFFECT_FLAG_INPUT_BOTH 0x00000400
-// sample output modes
+// Sample output modes
#define EFFECT_FLAG_OUTPUT_MASK 0x00001800
#define EFFECT_FLAG_OUTPUT_DIRECT 0x00000000
#define EFFECT_FLAG_OUTPUT_PROVIDER 0x00000800
#define EFFECT_FLAG_OUTPUT_BOTH 0x00001000
-// forward definition of type audio_buffer_t
+// Hardware acceleration mode
+#define EFFECT_FLAG_HW_ACC_MASK 0x00006000
+#define EFFECT_FLAG_HW_ACC_SIMPLE 0x00002000
+#define EFFECT_FLAG_HW_ACC_TUNNEL 0x00004000
+
+// Audio mode indication
+#define EFFECT_FLAG_AUDIO_MODE_MASK 0x00018000
+#define EFFECT_FLAG_AUDIO_MODE_IND 0x00008000
+#define EFFECT_FLAG_AUDIO_MODE_NONE 0x00000000
+
+// Forward definition of type audio_buffer_t
typedef struct audio_buffer_s audio_buffer_t;
////////////////////////////////////////////////////////////////////////////////
@@ -206,7 +242,9 @@
// -EINVAL invalid interface handle or
// invalid input/output buffer description
////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_process_t)(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
+typedef int32_t (*effect_process_t)(effect_interface_t self,
+ audio_buffer_t *inBuffer,
+ audio_buffer_t *outBuffer);
////////////////////////////////////////////////////////////////////////////////
//
@@ -238,7 +276,12 @@
// *pReplyData updated with command response
//
////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_command_t)(effect_interface_t self, int32_t cmdCode, int32_t cmdSize, void *pCmdData, int32_t *replySize, void *pReplyData);
+typedef int32_t (*effect_command_t)(effect_interface_t self,
+ int32_t cmdCode,
+ int32_t cmdSize,
+ void *pCmdData,
+ int32_t *replySize,
+ void *pReplyData);
// Effect control interface definition
@@ -248,75 +291,9 @@
};
-//--- Standardized command codes for command function
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | description | command code | command format | reply format
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Initialize effect engine: | EFFECT_CMD_INIT | size: 0 | size: sizeof(int)
-// | All configurations return to | | data: N/A | data: status
-// | default | | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Apply new audio parameters | EFFECT_CMD_CONFIGURE | size: sizeof(effect_config_t) | size: sizeof(int)
-// | configurations for input and | | data: effect_config_t | data: status
-// | output buffers | | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Reset effect engine: keep | EFFECT_CMD_RESET | size: 0 | size: 0
-// | configuration but reset state | | data: N/A | data: N/A
-// | and buffer content. | | |
-// | Called by the framework before | | |
-// | enabling the effect | | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Enable the process | EFFECT_CMD_ENABLE | size: 0 | size: sizeof(int)
-// | Called by the framework before | | data: N/A | data: status
-// | the first call to process() | | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Disable the process | EFFECT_CMD_DISABLE | size: 0 | size: sizeof(int)
-// | Called by the framework after | | data: N/A | data: status
-// | the last call to process() | | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Set a parameter and apply it | EFFECT_CMD_SET_PARAM | size: sizeof(effect_param_t) | size: sizeof(int)
-// | immediately | | + size of param + value | data: status
-// | | | data: effect_param_t |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Set a parameter but apply it | EFFECT_CMD_SET_PARAM_DEFERRED | size: sizeof(effect_param_t) | size: 0
-// | only when receiving command | | + size of param + value | data: N/A
-// | EFFECT_CMD_SET_PARAM_COMMIT | | data: effect_param_t |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Apply all previously received | EFFECT_CMD_SET_PARAM_COMMIT | size: 0 | size: sizeof(int)
-// | EFFECT_CMD_SET_PARAM_DEFERRED | | data: N/A | data: status
-// | commands | | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Get a parameter value | EFFECT_CMD_GET_PARAM | size: sizeof(effect_param_t) | size: sizeof(effect_param_t)
-// | | | + size of param | + size of param + value
-// | | | data: effect_param_t | data: effect_param_t
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Set the rendering device the | EFFECT_CMD_SET_DEVICE | size: sizeof(uint32_t) | size: 0
-// | audio output path is connected | | data: audio_device_e | data: N/A
-// | to. See audio_device_e in | | |
-// | AudioCommon.h for device values| | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | Set and get volume. Used by | EFFECT_CMD_SET_VOLUME | size: n * sizeof(uint32_t) | size: n * sizeof(uint32_t)
-// | audio framework to delegate | | data: volume for each channel | data: volume for each channel
-// | volume control to effect engine| | defined in effect_config_t in | defined in effect_config_t in
-// | If volume control flag is set | | 8.24 fixed point format | 8.24 fixed point format
-// | in the effect descriptor, the | | | It is legal to receive a null
-// | effect engine must return the | | | pointer as pReplyData in which
-// | volume that should be applied | | | case the effect framework has
-// | before the effect is processed | | | delegated volume control to
-// | The overall volume (the volume | | | another effect.
-// | actually applied by the effect | | |
-// | multiplied by the returned | | |
-// | value) should match the | | |
-// | requested value | | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-// | All proprietary effect commands| EFFECT_CMD_FIRST_PROPRIETARY | |
-// | must use command codes above | | |
-// | this value. The size and format| | |
-// | of command and response fields | | |
-// | is free in this case. | | |
-// +--------------------------------+-------------------------------+-------------------------------+--------------------------
-
-
+//
+//--- Standardized command codes for command() function
+//
enum effect_command_e {
EFFECT_CMD_INIT, // initialize effect engine
EFFECT_CMD_CONFIGURE, // configure effect engine (see effect_config_t)
@@ -327,18 +304,202 @@
EFFECT_CMD_SET_PARAM_DEFERRED, // set parameter deferred
EFFECT_CMD_SET_PARAM_COMMIT, // commit previous set parameter deferred
EFFECT_CMD_GET_PARAM, // get parameter
- EFFECT_CMD_SET_DEVICE, // set audio device (see audio_device_e in AudioCommon.h)
+ EFFECT_CMD_SET_DEVICE, // set audio device (see audio_device_e)
EFFECT_CMD_SET_VOLUME, // set volume
+ EFFECT_CMD_SET_AUDIO_MODE, // set the audio mode (normal, ring, ...)
EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
};
-// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t structure
-// Multi-channel audio is always interleaved. The channel order is from LSB to MSB with regard to the
-// channel mask definition in audio_channels_e (AudioCommon.h) e.g :
+//==================================================================================================
+// command: EFFECT_CMD_INIT
+//--------------------------------------------------------------------------------------------------
+// description:
+// Initialize effect engine: All configurations return to default
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_CONFIGURE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Apply new audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_config_t)
+// data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_RESET
+//--------------------------------------------------------------------------------------------------
+// description:
+// Reset the effect engine. Keep configuration but resets state and buffer content
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_ENABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Enable the process. Called by the framework before the first call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_DISABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Disable the process. Called by the framework after the last call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set a parameter and apply it immediately
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_param_t) + size of param and value
+// data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_DEFERRED
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_param_t) + size of param and value
+// data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_COMMIT
+//--------------------------------------------------------------------------------------------------
+// description:
+// Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: 0
+// data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(int)
+// data: status
+//==================================================================================================
+// command: EFFECT_CMD_GET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+// Get a parameter value
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(effect_param_t) + size of param
+// data: effect_param_t + param
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: sizeof(effect_param_t) + size of param and value
+// data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//==================================================================================================
+// command: EFFECT_CMD_SET_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set the rendering device the audio output path is connected to. See audio_device_e for device
+// values.
+// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+// command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(uint32_t)
+// data: audio_device_e
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_VOLUME
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set and get volume. Used by audio framework to delegate volume control to effect engine.
+// The effect implementation must set EFFECT_FLAG_VOLUME_IND and/or EFFECT_FLAG_VOLUME_CTRL flag in
+// its descriptor to receive this command before every call to process() function
+// If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return
+// the volume that should be applied before the effect is processed. The overall volume (the volume
+// actually applied by the effect engine multiplied by the returned value) should match the value
+// indicated in the command.
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: n * sizeof(uint32_t)
+// data: volume for each channel defined in effect_config_t for output buffer expressed in
+// 8.24 fixed point format
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: n * sizeof(uint32_t) / 0
+// data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor:
+// volume for each channel defined in effect_config_t for output buffer expressed in
+// 8.24 fixed point format
+// - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor:
+// N/A
+// It is legal to receive a null pointer as pReplyData in which case the effect framework has
+// delegated volume control to another effect
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_MODE
+//--------------------------------------------------------------------------------------------------
+// description:
+// Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its
+// descriptor to receive this command when the audio mode changes.
+//--------------------------------------------------------------------------------------------------
+// command format:
+// size: sizeof(uint32_t)
+// data: audio_mode_e
+//--------------------------------------------------------------------------------------------------
+// reply format:
+// size: 0
+// data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_FIRST_PROPRIETARY
+//--------------------------------------------------------------------------------------------------
+// description:
+// All proprietary effect commands must use command codes above this value. The size and format of
+// command and response fields is free in this case
+//==================================================================================================
+
+
+// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t
+// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with
+// regard to the channel mask definition in audio_channels_e e.g :
// Stereo: left, right
// 5 point 1: front left, front right, front center, low frequency, back left, back right
// The buffer size is expressed in frame count, a frame being composed of samples for all
-// channels at a given time
+// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by
+// definition
struct audio_buffer_s {
size_t frameCount; // number of frames in buffer
union {
@@ -349,7 +510,7 @@
};
};
-// the buffer_provider_s structure contains functions that can be used
+// The buffer_provider_s structure contains functions that can be used
// by the effect engine process() function to query and release input
// or output audio buffer.
// The getBuffer() function is called to retrieve a buffer where data
@@ -369,6 +530,7 @@
void *cookie; // for use by client of buffer provider functions
} buffer_provider_t;
+
// The buffer_config_s structure specifies the input or output audio format
// to be used by the effect engine. It is part of the effect_config_t
// structure that defines both input and output buffer configurations and is
@@ -376,14 +538,69 @@
typedef struct buffer_config_s {
audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly
uint32_t samplingRate; // sampling rate
- uint32_t channels; // channel mask (see audio_channels_e in AudioCommon.h)
+ uint32_t channels; // channel mask (see audio_channels_e)
buffer_provider_t bufferProvider; // buffer provider
- uint8_t format; // PCM format (see audio_format_e in AudioCommon.h)
+ uint8_t format; // Audio format (see audio_format_e)
uint8_t accessMode; // read/write or accumulate in buffer (effect_buffer_access_e)
uint16_t mask; // indicates which of the above fields is valid
} buffer_config_t;
-// values for "accessMode" field of buffer_config_t:
+// Sample format
+enum audio_format_e {
+ SAMPLE_FORMAT_PCM_S15, // PCM signed 16 bits
+ SAMPLE_FORMAT_PCM_U8, // PCM unsigned 8 bits
+ SAMPLE_FORMAT_PCM_S7_24, // PCM signed 7.24 fixed point representation
+ SAMPLE_FORMAT_OTHER // other format (e.g. compressed)
+};
+
+// Channel mask
+enum audio_channels_e {
+ CHANNEL_FRONT_LEFT = 0x1, // front left channel
+ CHANNEL_FRONT_RIGHT = 0x2, // front right channel
+ CHANNEL_FRONT_CENTER = 0x4, // front center channel
+ CHANNEL_LOW_FREQUENCY = 0x8, // low frequency channel
+ CHANNEL_BACK_LEFT = 0x10, // back left channel
+ CHANNEL_BACK_RIGHT = 0x20, // back right channel
+ CHANNEL_FRONT_LEFT_OF_CENTER = 0x40, // front left of center channel
+ CHANNEL_FRONT_RIGHT_OF_CENTER = 0x80, // front right of center channel
+ CHANNEL_BACK_CENTER = 0x100, // back center channel
+ CHANNEL_MONO = CHANNEL_FRONT_LEFT,
+ CHANNEL_STEREO = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT),
+ CHANNEL_QUAD = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+ CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
+ CHANNEL_SURROUND = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+ CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER),
+ CHANNEL_5POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+ CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT),
+ CHANNEL_7POINT1 = (CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
+ CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY | CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
+ CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER),
+};
+
+// Render device
+enum audio_device_e {
+ DEVICE_EARPIECE = 0x1, // earpiece
+ DEVICE_SPEAKER = 0x2, // speaker
+ DEVICE_WIRED_HEADSET = 0x4, // wired headset, with microphone
+ DEVICE_WIRED_HEADPHONE = 0x8, // wired headphone, without microphone
+ DEVICE_BLUETOOTH_SCO = 0x10, // generic bluetooth SCO
+ DEVICE_BLUETOOTH_SCO_HEADSET = 0x20, // bluetooth SCO headset
+ DEVICE_BLUETOOTH_SCO_CARKIT = 0x40, // bluetooth SCO car kit
+ DEVICE_BLUETOOTH_A2DP = 0x80, // generic bluetooth A2DP
+ DEVICE_BLUETOOTH_A2DP_HEADPHONES = 0x100, // bluetooth A2DP headphones
+ DEVICE_BLUETOOTH_A2DP_SPEAKER = 0x200, // bluetooth A2DP speakers
+ DEVICE_AUX_DIGITAL = 0x400, // digital output
+ DEVICE_EXTERNAL_SPEAKER = 0x800 // external speaker (stereo and High quality)
+};
+
+// Audio mode
+enum audio_mode_e {
+ AUDIO_MODE_NORMAL, // phone idle
+ AUDIO_MODE_RINGTONE, // phone ringing
+ AUDIO_MODE_IN_CALL // phone call connected
+};
+
+// Values for "accessMode" field of buffer_config_t:
// overwrite, read only, accumulate (read/modify/write)
enum effect_buffer_access_e {
EFFECT_BUFFER_ACCESS_WRITE,
@@ -392,7 +609,7 @@
};
-// values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
+// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
// in buffer_config_t must be taken into account when executing the EFFECT_CMD_CONFIGURE command
#define EFFECT_CONFIG_BUFFER 0x0001 // buffer field must be taken into account
#define EFFECT_CONFIG_SMP_RATE 0x0002 // samplingRate field must be taken into account
@@ -404,13 +621,15 @@
EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \
EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER)
-// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_CONFIGURE command
-// to configure audio parameters and buffers for effect engine input and output.
+
+// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_CONFIGURE
+// command to configure audio parameters and buffers for effect engine input and output.
typedef struct effect_config_s {
buffer_config_t inputCfg;
buffer_config_t outputCfg;;
} effect_config_t;
+
// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
// psize and vsize represent the actual size of parameter and value.
@@ -452,7 +671,7 @@
// specified here as the effect framework will get the function address with dlsym():
//
// - effect_QueryNumberEffects_t EffectQueryNumberEffects;
-// - effect_QueryNextEffect_t EffectQueryNext;
+// - effect_QueryEffect_t EffectQueryEffect;
// - effect_CreateEffect_t EffectCreate;
// - effect_ReleaseEffect_t EffectRelease;
@@ -463,11 +682,8 @@
//
// Description: Returns the number of different effects exposed by the
// library. Each effect must have a unique effect uuid (see
-// effect_descriptor_t). This function together with EffectQueryNext()
+// effect_descriptor_t). This function together with EffectQueryEffect()
// is used to enumerate all effects present in the library.
-// Each time EffectQueryNumberEffects() is called, the library must
-// reset the index of the effect descriptor returned by next call to
-// EffectQueryNext() to restart enumeration from the beginning.
//
// Input/Output:
// pNumEffects: address where the number of effects should be returned.
@@ -483,28 +699,33 @@
////////////////////////////////////////////////////////////////////////////////
//
-// Function: EffectQueryNext
+// Function: EffectQueryEffect
//
-// Description: Returns a descriptor of the next available effect.
+// Description: Returns the descriptor of the effect engine which index is
+// given as first argument.
// See effect_descriptor_t for details on effect descriptors.
-// This function together with EffectQueryNext() is used to enumerate all
+// This function together with EffectQueryNumberEffects() is used to enumerate all
// effects present in the library. The enumeration sequence is:
// EffectQueryNumberEffects(&num_effects);
-// while (num_effects--)
-// EffectQueryNext();
+// for (i = 0; i < num_effects; i++)
+// EffectQueryEffect(i,...);
//
// Input/Output:
+// index: index of the effect
// pDescriptor: address where to return the effect descriptor.
//
// Output:
// returned value: 0 successful operation.
// -ENODEV library failed to initialize
-// -EINVAL invalid pDescriptor
+// -EINVAL invalid pDescriptor or index
+// -ENOSYS effect list has changed since last execution of
+// EffectQueryNumberEffects()
// -ENOENT no more effect available
// *pDescriptor: updated with the effect descriptor.
//
////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_QueryNextEffect_t)(effect_descriptor_t *pDescriptor);
+typedef int32_t (*effect_QueryEffect_t)(uint32_t index,
+ effect_descriptor_t *pDescriptor);
////////////////////////////////////////////////////////////////////////////////
//
@@ -516,7 +737,13 @@
// a handle on the effect control interface.
//
// Input:
-// pEffectUuid: pointer to the effect uuid.
+// uuid: pointer to the effect uuid.
+// sessionId: audio session to which this effect instance will be attached. All effects
+// created with the same session ID are connected in series and process the same signal
+// stream. Knowing that two effects are part of the same effect chain can help the
+// library implement some kind of optimizations.
+// ioId: identifies the output or input stream this effect is directed to at audio HAL.
+// For future use especially with tunneled HW accelerated effects
//
// Input/Output:
// pInterface: address where to return the effect interface.
@@ -529,7 +756,10 @@
// *pInterface: updated with the effect interface handle.
//
////////////////////////////////////////////////////////////////////////////////
-typedef int32_t (*effect_CreateEffect_t)(effect_uuid_t *uuid, effect_interface_t *pInterface);
+typedef int32_t (*effect_CreateEffect_t)(effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_interface_t *pInterface);
////////////////////////////////////////////////////////////////////////////////
//
diff --git a/include/media/EffectFactoryApi.h b/include/media/EffectsFactoryApi.h
similarity index 86%
rename from include/media/EffectFactoryApi.h
rename to include/media/EffectsFactoryApi.h
index 6cc9932..0ed1a14 100644
--- a/include/media/EffectFactoryApi.h
+++ b/include/media/EffectsFactoryApi.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_EFFECTFACTORYAPI_H_
-#define ANDROID_EFFECTFACTORYAPI_H_
+#ifndef ANDROID_EFFECTSFACTORYAPI_H_
+#define ANDROID_EFFECTSFACTORYAPI_H_
#include <errno.h>
#include <stdint.h>
@@ -36,11 +36,11 @@
//
// Description: Returns the number of different effects in all loaded libraries.
// Each effect must have a different effect uuid (see
-// effect_descriptor_t). This function together with EffectQueryNext()
+// effect_descriptor_t). This function together with EffectQueryEffect()
// is used to enumerate all effects present in all loaded libraries.
// Each time EffectQueryNumberEffects() is called, the factory must
// reset the index of the effect descriptor returned by next call to
-// EffectQueryNext() to restart enumeration from the beginning.
+// EffectQueryEffect() to restart enumeration from the beginning.
//
// Input/Output:
// pNumEffects: address where the number of effects should be returned.
@@ -56,28 +56,29 @@
////////////////////////////////////////////////////////////////////////////////
//
-// Function: EffectQueryNext
+// Function: EffectQueryEffect
//
// Description: Returns a descriptor of the next available effect.
// See effect_descriptor_t for a details on effect descriptor.
-// This function together with EffectQueryNext() is used to enumerate all
+// This function together with EffectQueryNumberEffects() is used to enumerate all
// effects present in all loaded libraries. The enumeration sequence is:
// EffectQueryNumberEffects(&num_effects);
-// while (num_effects--)
-// EffectQueryNext();
+// for (i = 0; i < num_effects; i++)
+// EffectQueryEffect(i,...);
//
// Input/Output:
// pDescriptor: address where to return the effect descriptor.
//
// Output:
// returned value: 0 successful operation.
+// -ENOENT no more effect available
// -ENODEV factory failed to initialize
// -EINVAL invalid pDescriptor
-// -ENOENT no more effect available
+// -ENOSYS effect list has changed since last execution of EffectQueryNumberEffects()
// *pDescriptor: updated with the effect descriptor.
//
////////////////////////////////////////////////////////////////////////////////
-int EffectQueryNext(effect_descriptor_t *pDescriptor);
+int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor);
////////////////////////////////////////////////////////////////////////////////
//
@@ -90,6 +91,12 @@
//
// Input:
// pEffectUuid: pointer to the effect uuid.
+// sessionId: audio session to which this effect instance will be attached. All effects created
+// with the same session ID are connected in series and process the same signal stream.
+// Knowing that two effects are part of the same effect chain can help the library implement
+// some kind of optimizations.
+// ioId: identifies the output or input stream this effect is directed to at audio HAL. For future
+// use especially with tunneled HW accelerated effects
//
// Input/Output:
// pInterface: address where to return the effect interface.
@@ -102,7 +109,7 @@
// *pInterface: updated with the effect interface.
//
////////////////////////////////////////////////////////////////////////////////
-int EffectCreate(effect_uuid_t *pEffectUuid, effect_interface_t *pInterface);
+int EffectCreate(effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface);
////////////////////////////////////////////////////////////////////////////////
//
@@ -211,4 +218,4 @@
#endif
-#endif /*ANDROID_EFFECTFACTORYAPI_H_*/
+#endif /*ANDROID_EFFECTSFACTORYAPI_H_*/
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index ccfa530..5814fd6 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -148,7 +148,7 @@
virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
- virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor) = 0;
+ virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) = 0;
virtual status_t getEffectDescriptor(effect_uuid_t *pEffectUUID, effect_descriptor_t *pDescriptor) = 0;
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index eead166..4f4ec43 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -136,7 +136,9 @@
MEDIA_RECORDER_INFO_UNKNOWN = 1,
MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800,
MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801,
- MEDIA_RECORDER_INFO_STOP_PREMATURELY = 802
+ MEDIA_RECORDER_INFO_COMPLETION_STATUS = 802,
+ MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS = 803,
+ MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS = 804,
};
// ----------------------------------------------------------------------------
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index b0eaba4..813dd43 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -26,6 +26,7 @@
namespace android {
struct MediaSource;
+struct MetaData;
struct AMRWriter : public MediaWriter {
AMRWriter(const char *filename);
@@ -35,7 +36,7 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
- virtual status_t start();
+ virtual status_t start(MetaData *params = NULL);
virtual void stop();
virtual void pause();
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index f2001e1..628200d 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -39,6 +39,9 @@
virtual status_t stop();
virtual sp<MetaData> getFormat();
+ // Returns the maximum amplitude since last call.
+ int16_t getMaxAmplitude();
+
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
@@ -53,13 +56,17 @@
bool mStarted;
bool mCollectStats;
+ bool mTrackMaxAmplitude;
int64_t mTotalReadTimeUs;
int64_t mTotalReadBytes;
int64_t mTotalReads;
int64_t mStartTimeUs;
+ int16_t mMaxAmplitude;
MediaBufferGroup *mGroup;
+ void trackMaxAmplitude(int16_t *data, int nSamples);
+
AudioSource(const AudioSource &);
AudioSource &operator=(const AudioSource &);
};
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 39d0ea1..962b38b 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -36,7 +36,7 @@
MPEG4Writer(int fd);
virtual status_t addSource(const sp<MediaSource> &source);
- virtual status_t start();
+ virtual status_t start(MetaData *param = NULL);
virtual bool reachedEOS();
virtual void stop();
virtual void pause();
@@ -81,8 +81,9 @@
void setStartTimestampUs(int64_t timeUs);
int64_t getStartTimestampUs(); // Not const
- status_t startTracks();
+ status_t startTracks(MetaData *params);
size_t numTracks();
+ int64_t estimateMoovBoxSize(int32_t bitRate);
void lock();
void unlock();
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 8528203..46aaf7c 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -24,13 +24,14 @@
namespace android {
struct MediaSource;
+struct MetaData;
struct MediaWriter : public RefBase {
MediaWriter() {}
virtual status_t addSource(const sp<MediaSource> &source) = 0;
virtual bool reachedEOS() = 0;
- virtual status_t start() = 0;
+ virtual status_t start(MetaData *params = NULL) = 0;
virtual void stop() = 0;
virtual void pause() = 0;
virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 6a20602..95fe6f6 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -36,7 +36,7 @@
kKeyStride = 'strd', // int32_t
kKeySliceHeight = 'slht', // int32_t
kKeyChannelCount = '#chn', // int32_t
- kKeySampleRate = 'srte', // int32_t
+ kKeySampleRate = 'srte', // int32_t (also video frame rate)
kKeyBitRate = 'brte', // int32_t (bps)
kKeyESDS = 'esds', // raw data
kKeyAVCC = 'avcc', // raw data
@@ -68,6 +68,21 @@
kKeyDiscNumber = 'dnum', // cstring
kKeyDate = 'date', // cstring
kKeyWriter = 'writ', // cstring
+
+ // Set this key to enable authoring files in 64-bit offset
+ kKey64BitFileOffset = 'fobt', // int32_t (bool)
+
+ // Identify the file output format for authoring
+ // Please see <media/mediarecorder.h> for the supported
+ // file output formats.
+ kKeyFileType = 'ftyp', // int32_t
+
+ // Track authoring progress status
+ // kKeyTrackTimeStatus is used to track progress in elapsed time
+ // kKeyTrackFrameStatus is used to track progress in authored frames
+ kKeyTrackFrameStatus = 'tkfm', // int32_t
+ kKeyTrackTimeStatus = 'tktm', // int64_t
+
};
enum {
diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
index bd7f28c..9c64ac0 100644
--- a/include/utils/RefBase.h
+++ b/include/utils/RefBase.h
@@ -333,9 +333,10 @@
template<typename T>
sp<T>& sp<T>::operator = (const sp<T>& other) {
- if (other.m_ptr) other.m_ptr->incStrong(this);
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherPtr->incStrong(this);
if (m_ptr) m_ptr->decStrong(this);
- m_ptr = other.m_ptr;
+ m_ptr = otherPtr;
return *this;
}
@@ -351,9 +352,10 @@
template<typename T> template<typename U>
sp<T>& sp<T>::operator = (const sp<U>& other)
{
- if (other.m_ptr) other.m_ptr->incStrong(this);
+ U* otherPtr(other.m_ptr);
+ if (otherPtr) otherPtr->incStrong(this);
if (m_ptr) m_ptr->decStrong(this);
- m_ptr = other.m_ptr;
+ m_ptr = otherPtr;
return *this;
}
@@ -466,10 +468,12 @@
template<typename T>
wp<T>& wp<T>::operator = (const wp<T>& other)
{
- if (other.m_ptr) other.m_refs->incWeak(this);
+ weakref_type* otherRefs(other.m_refs);
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
if (m_ptr) m_refs->decWeak(this);
- m_ptr = other.m_ptr;
- m_refs = other.m_refs;
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
return *this;
}
@@ -478,8 +482,9 @@
{
weakref_type* newRefs =
other != NULL ? other->createWeak(this) : 0;
+ T* otherPtr(other.m_ptr);
if (m_ptr) m_refs->decWeak(this);
- m_ptr = other.get();
+ m_ptr = otherPtr;
m_refs = newRefs;
return *this;
}
@@ -498,10 +503,12 @@
template<typename T> template<typename U>
wp<T>& wp<T>::operator = (const wp<U>& other)
{
- if (other.m_ptr) other.m_refs->incWeak(this);
+ weakref_type* otherRefs(other.m_refs);
+ U* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
if (m_ptr) m_refs->decWeak(this);
- m_ptr = other.m_ptr;
- m_refs = other.m_refs;
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
return *this;
}
@@ -510,8 +517,9 @@
{
weakref_type* newRefs =
other != NULL ? other->createWeak(this) : 0;
+ U* otherPtr(other.m_ptr);
if (m_ptr) m_refs->decWeak(this);
- m_ptr = other.get();
+ m_ptr = otherPtr;
m_refs = newRefs;
return *this;
}
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 1860793..48c04a6 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -51,7 +51,7 @@
#include "lifevibes.h"
#endif
-#include <media/EffectFactoryApi.h>
+#include <media/EffectsFactoryApi.h>
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
@@ -126,7 +126,8 @@
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
+ mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+ mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
{
mHardwareStatus = AUDIO_HW_IDLE;
@@ -135,8 +136,8 @@
mHardwareStatus = AUDIO_HW_INIT;
if (mAudioHardware->initCheck() == NO_ERROR) {
// open 16-bit output stream for s/w mixer
-
- setMode(AudioSystem::MODE_NORMAL);
+ mMode = AudioSystem::MODE_NORMAL;
+ setMode(mMode);
setMasterVolume(1.0f);
setMasterMute(false);
@@ -431,6 +432,8 @@
status_t AudioFlinger::setMode(int mode)
{
+ status_t ret;
+
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
@@ -440,15 +443,23 @@
return BAD_VALUE;
}
- AutoMutex lock(mHardwareLock);
- mHardwareStatus = AUDIO_HW_SET_MODE;
- status_t ret = mAudioHardware->setMode(mode);
-#ifdef LVMX
- if (NO_ERROR == ret) {
- LifeVibes::setMode(mode);
+ { // scope for the lock
+ AutoMutex lock(mHardwareLock);
+ mHardwareStatus = AUDIO_HW_SET_MODE;
+ ret = mAudioHardware->setMode(mode);
+ mHardwareStatus = AUDIO_HW_IDLE;
}
+
+ if (NO_ERROR == ret) {
+ Mutex::Autolock _l(mLock);
+ mMode = mode;
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setMode(mode);
+#ifdef LVMX
+ LifeVibes::setMode(mode);
#endif
- mHardwareStatus = AUDIO_HW_IDLE;
+ }
+
return ret;
}
@@ -1385,6 +1396,15 @@
return chain;
}
+void AudioFlinger::PlaybackThread::setMode(uint32_t mode)
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mEffectChains.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffectChains[i]->setMode(mode);
+ }
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
@@ -1803,9 +1823,9 @@
t->mCblk->flags |= CBLK_INVALID_ON;
t->mCblk->cv.signal();
t->mCblk->lock.unlock();
- }
- }
}
+ }
+}
// getTrackName_l() must be called with ThreadBase::mLock held
@@ -4466,10 +4486,10 @@
return EffectQueryNumberEffects(numEffects);
}
-status_t AudioFlinger::queryNextEffect(effect_descriptor_t *descriptor)
+status_t AudioFlinger::queryEffect(uint32_t index, effect_descriptor_t *descriptor)
{
Mutex::Autolock _l(mLock);
- return EffectQueryNext(descriptor);
+ return EffectQueryEffect(index, descriptor);
}
status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor)
@@ -4529,10 +4549,10 @@
LOGW("createEffect() error %d from EffectQueryNumberEffects", lStatus);
goto Exit;
}
- for (; numEffects > 0; numEffects--) {
- lStatus = EffectQueryNext(&desc);
+ for (uint32_t i = 0; i < numEffects; i++) {
+ lStatus = EffectQueryEffect(i, &desc);
if (lStatus < 0) {
- LOGW("createEffect() error %d from EffectQueryNext", lStatus);
+ LOGW("createEffect() error %d from EffectQueryEffect", lStatus);
continue;
}
if (memcmp(&desc.type, &pDesc->type, sizeof(effect_uuid_t)) == 0) {
@@ -4567,6 +4587,13 @@
goto Exit;
}
+ // Session -1 is reserved for output stage effects that can only be created
+ // by audio policy manager (running in same process)
+ if (sessionId == -1 && getpid() != IPCThreadState::self()->getCallingPid()) {
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+
// return effect descriptor
memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
@@ -4574,7 +4601,7 @@
// output threads.
// TODO: allow attachment of effect to inputs
if (output == 0) {
- if (sessionId == 0) {
+ if (sessionId <= 0) {
// default to first output
// TODO: define criteria to choose output when not specified. Or
// receive output from audio policy manager
@@ -4621,6 +4648,33 @@
return handle;
}
+status_t AudioFlinger::registerEffectResource_l(effect_descriptor_t *desc) {
+ if (mTotalEffectsCpuLoad + desc->cpuLoad > MAX_EFFECTS_CPU_LOAD) {
+ LOGW("registerEffectResource() CPU Load limit exceeded for Fx %s, CPU %f MIPS",
+ desc->name, (float)desc->cpuLoad/10);
+ return INVALID_OPERATION;
+ }
+ if (mTotalEffectsMemory + desc->memoryUsage > MAX_EFFECTS_MEMORY) {
+ LOGW("registerEffectResource() memory limit exceeded for Fx %s, Memory %d KB",
+ desc->name, desc->memoryUsage);
+ return INVALID_OPERATION;
+ }
+ mTotalEffectsCpuLoad += desc->cpuLoad;
+ mTotalEffectsMemory += desc->memoryUsage;
+ LOGV("registerEffectResource_l() effect %s, CPU %d, memory %d",
+ desc->name, desc->cpuLoad, desc->memoryUsage);
+ LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+ return NO_ERROR;
+}
+
+void AudioFlinger::unregisterEffectResource_l(effect_descriptor_t *desc) {
+ mTotalEffectsCpuLoad -= desc->cpuLoad;
+ mTotalEffectsMemory -= desc->memoryUsage;
+ LOGV("unregisterEffectResource_l() effect %s, CPU %d, memory %d",
+ desc->name, desc->cpuLoad, desc->memoryUsage);
+ LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+}
+
// PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l(
const sp<AudioFlinger::Client>& client,
@@ -4638,6 +4692,7 @@
sp<Track> track;
sp<EffectChain> chain;
bool effectCreated = false;
+ bool effectRegistered = false;
if (mOutput == 0) {
LOGW("createEffect_l() Audio driver not initialized.");
@@ -4680,20 +4735,26 @@
LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
if (effect == 0) {
+ // Check CPU and memory usage
+ lStatus = mAudioFlinger->registerEffectResource_l(desc);
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+ effectRegistered = true;
// create a new effect module if none present in the chain
- effectCreated = true;
effect = new EffectModule(this, chain, desc, mAudioFlinger->nextUniqueId(), sessionId);
lStatus = effect->status();
if (lStatus != NO_ERROR) {
goto Exit;
}
- //TODO: handle CPU load and memory usage here
lStatus = chain->addEffect(effect);
if (lStatus != NO_ERROR) {
goto Exit;
}
+ effectCreated = true;
effect->setDevice(mDevice);
+ effect->setMode(mAudioFlinger->getMode());
}
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
@@ -4705,11 +4766,14 @@
Exit:
if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
- if (chain != 0 && effectCreated) {
+ if (effectCreated) {
if (chain->removeEffect(effect) == 0) {
removeEffectChain_l(chain);
}
}
+ if (effectRegistered) {
+ mAudioFlinger->unregisterEffectResource_l(desc);
+ }
handle.clear();
}
@@ -4719,21 +4783,37 @@
return handle;
}
+void AudioFlinger::PlaybackThread::disconnectEffect(const sp< EffectModule>& effect,
+ const wp<EffectHandle>& handle) {
+ effect_descriptor_t desc = effect->desc();
+ Mutex::Autolock _l(mLock);
+ // delete the effect module if removing last handle on it
+ if (effect->removeHandle(handle) == 0) {
+ if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ detachAuxEffect_l(effect->id());
+ }
+ sp<EffectChain> chain = effect->chain().promote();
+ if (chain != 0) {
+ // remove effect chain if remove last effect
+ if (chain->removeEffect(effect) == 0) {
+ removeEffectChain_l(chain);
+ }
+ }
+ mLock.unlock();
+ mAudioFlinger->mLock.lock();
+ mAudioFlinger->unregisterEffectResource_l(&desc);
+ mAudioFlinger->mLock.unlock();
+ }
+}
+
status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
{
int session = chain->sessionId();
int16_t *buffer = mMixBuffer;
+ bool ownsBuffer = false;
LOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
- if (session == 0) {
- chain->setInBuffer(buffer, false);
- chain->setOutBuffer(buffer);
- // Effect chain for session 0 is inserted at end of effect chains list
- // to be processed last as it contains output mix effects to apply after
- // all track specific effects
- mEffectChains.add(chain);
- } else {
- bool ownsBuffer = false;
+ if (session > 0) {
// Only one effect chain can be present in direct output thread and it uses
// the mix buffer as input
if (mType != DIRECT) {
@@ -4743,32 +4823,42 @@
LOGV("addEffectChain_l() creating new input buffer %p session %d", buffer, session);
ownsBuffer = true;
}
- chain->setInBuffer(buffer, ownsBuffer);
- chain->setOutBuffer(mMixBuffer);
- // Effect chain for session other than 0 is inserted at beginning of effect
- // chains list to be processed before output mix effects. Relative order between
- // sessions other than 0 is not important
- mEffectChains.insertAt(chain, 0);
- }
- // Attach all tracks with same session ID to this chain.
- for (size_t i = 0; i < mTracks.size(); ++i) {
- sp<Track> track = mTracks[i];
- if (session == track->sessionId()) {
- LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
- track->setMainBuffer(buffer);
+ // Attach all tracks with same session ID to this chain.
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (session == track->sessionId()) {
+ LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
+ track->setMainBuffer(buffer);
+ }
+ }
+
+ // indicate all active tracks in the chain
+ for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
+ sp<Track> track = mActiveTracks[i].promote();
+ if (track == 0) continue;
+ if (session == track->sessionId()) {
+ LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
+ chain->startTrack();
+ }
}
}
- // indicate all active tracks in the chain
- for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
- sp<Track> track = mActiveTracks[i].promote();
- if (track == 0) continue;
- if (session == track->sessionId()) {
- LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
- chain->startTrack();
- }
+ chain->setInBuffer(buffer, ownsBuffer);
+ chain->setOutBuffer(mMixBuffer);
+ // Effect chain for session -1 is inserted at end of effect chains list
+ // in order to be processed last as it contains output stage effects
+ // Effect chain for session 0 is inserted before session -1 to be processed
+ // after track specific effects and before output stage
+ // Effect chain for session other than 0 is inserted at beginning of effect
+ // chains list to be processed before output mix effects. Relative order between
+ // sessions other than 0 is not important
+ size_t size = mEffectChains.size();
+ size_t i = 0;
+ for (i = 0; i < size; i++) {
+ if (mEffectChains[i]->sessionId() < session) break;
}
+ mEffectChains.insertAt(chain, i);
return NO_ERROR;
}
@@ -4884,7 +4974,8 @@
memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t));
// create effect engine from effect factory
- mStatus = EffectCreate(&desc->uuid, &mEffectInterface);
+ mStatus = EffectCreate(&desc->uuid, sessionId, p->id(), &mEffectInterface);
+
if (mStatus != NO_ERROR) {
return;
}
@@ -4969,22 +5060,11 @@
// keep a strong reference on this EffectModule to avoid calling the
// destructor before we exit
sp<EffectModule> keep(this);
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
- // delete the effect module if removing last handle on it
- if (removeHandle(handle) == 0) {
+ {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- playbackThread->detachAuxEffect_l(mId);
- }
- sp<EffectChain> chain = mChain.promote();
- if (chain != 0) {
- // remove effect chain if remove last effect
- if (chain->removeEffect(keep) == 0) {
- playbackThread->removeEffectChain_l(chain);
- }
- }
+ playbackThread->disconnectEffect(keep, handle);
}
}
}
@@ -5007,88 +5087,25 @@
// TODO: handle effects with buffer provider
if (mState != ACTIVE) {
- uint32_t count = mConfig.inputCfg.buffer.frameCount;
- int32_t amp = 32767L << 16;
- int32_t step = amp / count;
- int16_t *pIn = mConfig.inputCfg.buffer.s16;
- int16_t *pOut = mConfig.outputCfg.buffer.s16;
- int inChannels;
- int outChannels;
-
- if (mConfig.inputCfg.channels == CHANNEL_MONO) {
- inChannels = 1;
- } else {
- inChannels = 2;
- }
- if (mConfig.outputCfg.channels == CHANNEL_MONO) {
- outChannels = 1;
- } else {
- outChannels = 2;
- }
-
switch (mState) {
case RESET:
reset();
+ mState = STARTING;
// clear auxiliary effect input buffer for next accumulation
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t));
}
- step = -step;
- mState = STARTING;
- break;
+ return;
case STARTING:
start();
- amp = 0;
- pOut = mConfig.inputCfg.buffer.s16;
- outChannels = inChannels;
mState = ACTIVE;
break;
case STOPPING:
- step = -step;
- pOut = mConfig.inputCfg.buffer.s16;
- outChannels = inChannels;
mState = STOPPED;
break;
case STOPPED:
stop();
- amp = 0;
mState = IDLE;
- break;
- }
-
- // ramp volume down or up before activating or deactivating the effect
- if (inChannels == 1) {
- if (outChannels == 1) {
- while (count--) {
- *pOut++ = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15);
- amp += step;
- }
- } else {
- while (count--) {
- int32_t smp = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15);
- *pOut++ = smp;
- *pOut++ = smp;
- amp += step;
- }
- }
- } else {
- if (outChannels == 1) {
- while (count--) {
- int32_t smp = (((int32_t)*pIn * (amp >> 16)) >> 16) +
- (((int32_t)*(pIn + 1) * (amp >> 16)) >> 16);
- pIn += 2;
- *pOut++ = (int16_t)smp;
- amp += step;
- }
- } else {
- while (count--) {
- *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15;
- *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15;
- amp += step;
- }
- }
- }
- if (mState == STARTING || mState == IDLE) {
return;
}
}
@@ -5148,8 +5165,8 @@
mConfig.inputCfg.channels = channels;
}
mConfig.outputCfg.channels = channels;
- mConfig.inputCfg.format = PCM_FORMAT_S15;
- mConfig.outputCfg.format = PCM_FORMAT_S15;
+ mConfig.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
+ mConfig.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
mConfig.inputCfg.samplingRate = thread->sampleRate();
mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
mConfig.inputCfg.bufferProvider.cookie = NULL;
@@ -5160,7 +5177,7 @@
mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
// Insert effect:
- // - in session 0, always overwrites output buffer: input buffer == output buffer
+ // - in session 0 or -1, always overwrites output buffer: input buffer == output buffer
// - in other sessions:
// last effect in the chain accumulates in output buffer: input buffer != output buffer
// other effect: overwrites output buffer: input buffer == output buffer
@@ -5331,7 +5348,12 @@
status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
{
status_t status = NO_ERROR;
- if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_MASK) {
+ if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
+ // convert device bit field from AudioSystem to EffectApi format.
+ device = deviceAudioSystemToEffectApi(device);
+ if (device == 0) {
+ return BAD_VALUE;
+ }
status_t cmdStatus;
int size = sizeof(status_t);
status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_DEVICE, sizeof(uint32_t), &device, &size, &cmdStatus);
@@ -5342,6 +5364,70 @@
return status;
}
+status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
+{
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
+ // convert audio mode from AudioSystem to EffectApi format.
+ int effectMode = modeAudioSystemToEffectApi(mode);
+ if (effectMode < 0) {
+ return BAD_VALUE;
+ }
+ status_t cmdStatus;
+ int size = sizeof(status_t);
+ status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_AUDIO_MODE, sizeof(int), &effectMode, &size, &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ }
+ return status;
+}
+
+// update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
+const uint32_t AudioFlinger::EffectModule::sDeviceConvTable[] = {
+ DEVICE_EARPIECE, // AudioSystem::DEVICE_OUT_EARPIECE
+ DEVICE_SPEAKER, // AudioSystem::DEVICE_OUT_SPEAKER
+ DEVICE_WIRED_HEADSET, // case AudioSystem::DEVICE_OUT_WIRED_HEADSET
+ DEVICE_WIRED_HEADPHONE, // AudioSystem::DEVICE_OUT_WIRED_HEADPHONE
+ DEVICE_BLUETOOTH_SCO, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO
+ DEVICE_BLUETOOTH_SCO_HEADSET, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET
+ DEVICE_BLUETOOTH_SCO_CARKIT, // AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+ DEVICE_BLUETOOTH_A2DP, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP
+ DEVICE_BLUETOOTH_A2DP_HEADPHONES, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+ DEVICE_BLUETOOTH_A2DP_SPEAKER, // AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
+ DEVICE_AUX_DIGITAL // AudioSystem::DEVICE_OUT_AUX_DIGITAL
+};
+
+uint32_t AudioFlinger::EffectModule::deviceAudioSystemToEffectApi(uint32_t device)
+{
+ uint32_t deviceOut = 0;
+ while (device) {
+ const uint32_t i = 31 - __builtin_clz(device);
+ device &= ~(1 << i);
+ if (i >= sizeof(sDeviceConvTable)/sizeof(uint32_t)) {
+ LOGE("device convertion error for AudioSystem device 0x%08x", device);
+ return 0;
+ }
+ deviceOut |= (uint32_t)sDeviceConvTable[i];
+ }
+ return deviceOut;
+}
+
+// update this table when AudioSystem::audio_mode or audio_mode_e (in EffectApi.h) are modified
+const uint32_t AudioFlinger::EffectModule::sModeConvTable[] = {
+ AUDIO_MODE_NORMAL, // AudioSystem::MODE_NORMAL
+ AUDIO_MODE_RINGTONE, // AudioSystem::MODE_RINGTONE
+ AUDIO_MODE_IN_CALL // AudioSystem::MODE_IN_CALL
+};
+
+int AudioFlinger::EffectModule::modeAudioSystemToEffectApi(uint32_t mode)
+{
+ int modeOut = -1;
+ if (mode < sizeof(sModeConvTable) / sizeof(uint32_t)) {
+ modeOut = (int)sModeConvTable[mode];
+ }
+ return modeOut;
+}
status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
{
@@ -5525,7 +5611,16 @@
int rsize = sizeof(int);
int *p = (int *)(mBuffer + mCblk->serverIndex);
int size = *p++;
+ if (((uint8_t *)p + size) > mBuffer + mCblk->clientIndex) {
+ LOGW("command(): invalid parameter block size");
+ break;
+ }
effect_param_t *param = (effect_param_t *)p;
+ if (param->psize == 0 || param->vsize == 0) {
+ LOGW("command(): null parameter or value size");
+ mCblk->serverIndex += size;
+ continue;
+ }
int psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM, psize, p, &rsize, &reply);
if (ret == NO_ERROR) {
@@ -5659,7 +5754,7 @@
}
// if no track is active, input buffer must be cleared here as the mixer process
// will not do it
- if (mSessionId != 0 && activeTracks() == 0) {
+ if (mSessionId > 0 && activeTracks() == 0) {
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
size_t numSamples = thread->frameCount() * thread->channelCount();
@@ -5697,15 +5792,16 @@
} else {
// Insert effects are inserted at the end of mEffects vector as they are processed
// after track and auxiliary effects.
- // Insert effect order:
- // if EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_EXCLUSIVE insert as first insert effect
+ // Insert effect order as a function of indicated preference:
+ // if EFFECT_FLAG_INSERT_EXCLUSIVE, insert in first position or reject if
+ // another effect is present
+ // else if EFFECT_FLAG_INSERT_FIRST, insert in first position or after the
+ // last effect claiming first position
+ // else if EFFECT_FLAG_INSERT_LAST, insert in last position or before the
+ // first effect claiming last position
// else if EFFECT_FLAG_INSERT_ANY insert after first or before last
- // else insert as last insert effect
- // Reject insertion if:
- // - EFFECT_FLAG_INSERT_EXCLUSIVE and another effect is present
- // - an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is present
- // - EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_LAST and an effect with same
- // preference is present
+ // Reject insertion if an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is
+ // already present
int size = (int)mEffects.size();
int idx_insert = size;
@@ -5719,35 +5815,40 @@
if (iMode == EFFECT_FLAG_TYPE_INSERT) {
// check invalid effect chaining combinations
if (insertPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
- iPref == EFFECT_FLAG_INSERT_EXCLUSIVE ||
- (insertPref != EFFECT_FLAG_INSERT_ANY
- && insertPref == iPref)) {
+ iPref == EFFECT_FLAG_INSERT_EXCLUSIVE) {
+ LOGW("addEffect() could not insert effect %s: exclusive conflict with %s", desc.name, d.name);
return INVALID_OPERATION;
}
- // remember position of first insert effect
+ // remember position of first insert effect and by default
+ // select this as insert position for new effect
if (idx_insert == size) {
idx_insert = i;
}
- // remember position of insert effect claiming
- // first place
+ // remember position of last insert effect claiming
+ // first position
if (iPref == EFFECT_FLAG_INSERT_FIRST) {
idx_insert_first = i;
}
- // remember position of insert effect claiming
- // last place
- if (iPref == EFFECT_FLAG_INSERT_LAST) {
+ // remember position of first insert effect claiming
+ // last position
+ if (iPref == EFFECT_FLAG_INSERT_LAST &&
+ idx_insert_last == -1) {
idx_insert_last = i;
}
}
}
- // modify idx_insert from first place if needed
- if (idx_insert_first != -1) {
- idx_insert = idx_insert_first + 1;
- } else if (idx_insert_last != -1) {
- idx_insert = idx_insert_last;
- } else if (insertPref == EFFECT_FLAG_INSERT_LAST) {
- idx_insert = size;
+ // modify idx_insert from first position if needed
+ if (insertPref == EFFECT_FLAG_INSERT_LAST) {
+ if (idx_insert_last != -1) {
+ idx_insert = idx_insert_last;
+ } else {
+ idx_insert = size;
+ }
+ } else {
+ if (idx_insert_first != -1) {
+ idx_insert = idx_insert_first + 1;
+ }
}
// always read samples from chain input buffer
@@ -5764,14 +5865,14 @@
} else {
effect->setOutBuffer(mInBuffer);
}
- status_t status = mEffects.insertAt(effect, idx_insert);
+ mEffects.insertAt(effect, idx_insert);
// Always give volume control to last effect in chain with volume control capability
if (((desc.flags & EFFECT_FLAG_VOLUME_MASK) & EFFECT_FLAG_VOLUME_CTRL) &&
mVolumeCtrlIdx < idx_insert) {
mVolumeCtrlIdx = idx_insert;
}
- LOGV("addEffect() effect %p, added in chain %p at rank %d status %d", effect.get(), this, idx_insert, status);
+ LOGV("addEffect() effect %p, added in chain %p at rank %d", effect.get(), this, idx_insert);
}
effect->configure();
return NO_ERROR;
@@ -5823,6 +5924,14 @@
}
}
+void AudioFlinger::EffectChain::setMode(uint32_t mode)
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->setMode(mode);
+ }
+}
+
bool AudioFlinger::EffectChain::setVolume(uint32_t *left, uint32_t *right)
{
uint32_t newLeft = *left;
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index e543334..42dca4c 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -149,7 +149,7 @@
virtual status_t queryNumberEffects(uint32_t *numEffects);
- virtual status_t queryNextEffect(effect_descriptor_t *descriptor);
+ virtual status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor);
virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor);
@@ -163,6 +163,9 @@
int *id,
int *enabled);
+ status_t registerEffectResource_l(effect_descriptor_t *desc);
+ void unregisterEffectResource_l(effect_descriptor_t *desc);
+
enum hardware_call_state {
AUDIO_HW_IDLE = 0,
AUDIO_HW_INIT,
@@ -200,6 +203,8 @@
Parcel* reply,
uint32_t flags);
+ uint32_t getMode() { return mMode; }
+
private:
AudioFlinger();
virtual ~AudioFlinger();
@@ -601,6 +606,8 @@
effect_descriptor_t *desc,
int *enabled,
status_t *status);
+ void disconnectEffect(const sp< EffectModule>& effect,
+ const wp<EffectHandle>& handle);
bool hasAudioSession(int sessionId);
sp<EffectChain> getEffectChain(int sessionId);
@@ -614,6 +621,7 @@
void detachAuxEffect_l(int effectId);
status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
status_t attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
+ void setMode(uint32_t mode);
struct stream_type_t {
stream_type_t()
@@ -930,9 +938,11 @@
size_t removeHandle (const wp<EffectHandle>& handle);
effect_descriptor_t& desc() { return mDescriptor; }
+ wp<EffectChain>& chain() { return mChain; }
status_t setDevice(uint32_t device);
status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
+ status_t setMode(uint32_t mode);
status_t dump(int fd, const Vector<String16>& args);
@@ -944,6 +954,14 @@
status_t start();
status_t stop();
+ // update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
+ static const uint32_t sDeviceConvTable[];
+ static uint32_t deviceAudioSystemToEffectApi(uint32_t device);
+
+ // update this table when AudioSystem::audio_mode or audio_mode_e (in EffectApi.h) are modified
+ static const uint32_t sModeConvTable[];
+ static int modeAudioSystemToEffectApi(uint32_t mode);
+
Mutex mLock; // mutex for process, commands and handles list protection
wp<ThreadBase> mThread; // parent thread
wp<EffectChain> mChain; // parent effect chain
@@ -1042,6 +1060,8 @@
sp<EffectModule> getVolumeController();
bool setVolume(uint32_t *left, uint32_t *right);
void setDevice(uint32_t device);
+ void setMode(uint32_t mode);
+
void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
mInBuffer = buffer;
@@ -1104,6 +1124,14 @@
#ifdef LVMX
int mLifeVibesClientPid;
#endif
+ uint32_t mMode;
+
+ // Maximum CPU load allocated to audio effects in 0.1 MIPS (ARMv5TE, 0 WS memory) units
+ static const uint32_t MAX_EFFECTS_CPU_LOAD = 1000;
+ // Maximum memory allocated to audio effects in KB
+ static const uint32_t MAX_EFFECTS_MEMORY = 512;
+ uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects
+ uint32_t mTotalEffectsMemory; // current memory used by effects
};
// ----------------------------------------------------------------------------
diff --git a/libs/rs/java/Fountain/res/raw/fountain.rs b/libs/rs/java/Fountain/res/raw/fountain.rs
index 6ff4c5bb..ef12d9a 100644
--- a/libs/rs/java/Fountain/res/raw/fountain.rs
+++ b/libs/rs/java/Fountain/res/raw/fountain.rs
@@ -40,7 +40,7 @@
return 1;
}
-void addParticles(int rate, float x, float y, int newColor)
+void addParticles(int rate, float x, float y, bool newColor)
{
if (newColor) {
partColor.x = rsRand(0.5f, 1.0f);
diff --git a/libs/rs/java/Fountain/res/raw/fountain_bc.bc b/libs/rs/java/Fountain/res/raw/fountain_bc.bc
index b200687..ac6b7d4 100644
--- a/libs/rs/java/Fountain/res/raw/fountain_bc.bc
+++ b/libs/rs/java/Fountain/res/raw/fountain_bc.bc
Binary files differ
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
index 6f4737f..ffe2fad 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
@@ -51,7 +51,7 @@
boolean holdingColor = false;
public void newTouchPosition(int x, int y, int rate) {
if (rate > 0) {
- mScript.invoke_addParticles(rate, x, y, !holdingColor ? 1 : 0);
+ mScript.invoke_addParticles(rate, x, y, !holdingColor);
holdingColor = true;
} else {
holdingColor = false;
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java b/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java
index 2653f6b..a4c598b 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/ScriptC_Fountain.java
@@ -50,12 +50,13 @@
}
private final static int mExportFuncIdx_addParticles = 0;
- public void invoke_addParticles(int rate, float x, float y, int newColor) {
+ public void invoke_addParticles(int rate, float x, float y, boolean newColor) {
FieldPacker addParticles_fp = new FieldPacker(16);
addParticles_fp.addI32(rate);
addParticles_fp.addF32(x);
addParticles_fp.addF32(y);
- addParticles_fp.addI32(newColor);
+ addParticles_fp.addBoolean(newColor);
+ addParticles_fp.skip(3);
invoke(mExportFuncIdx_addParticles, addParticles_fp);
}
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/ScriptField_Point.java b/libs/rs/java/Fountain/src/com/android/fountain/ScriptField_Point.java
index dee4e96..d88f47b 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/ScriptField_Point.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/ScriptField_Point.java
@@ -38,17 +38,18 @@
private Item mItemArray[];
private FieldPacker mIOBuffer;
+ public static Element createElement(RenderScript rs) {
+ Element.Builder eb = new Element.Builder(rs);
+ eb.add(Element.createVector(rs, Element.DataType.FLOAT_32, 2), "delta");
+ eb.add(Element.createVector(rs, Element.DataType.FLOAT_32, 2), "position");
+ eb.add(Element.createVector(rs, Element.DataType.UNSIGNED_8, 4), "color");
+ return eb.create();
+ }
+
public ScriptField_Point(RenderScript rs, int count) {
mItemArray = null;
mIOBuffer = null;
- {
- Element.Builder eb = new Element.Builder(rs);
- eb.add(Element.createVector(rs, Element.DataType.FLOAT_32, 2), "delta");
- eb.add(Element.createVector(rs, Element.DataType.FLOAT_32, 2), "position");
- eb.add(Element.createVector(rs, Element.DataType.UNSIGNED_8, 4), "color");
- mElement = eb.create();
- }
-
+ mElement = createElement(rs);
init(rs, count);
}
@@ -65,7 +66,7 @@
mItemArray[index] = i;
if (copyNow) {
copyToArray(i, index);
- mAllocation.subData1D(index * Item.sizeof, Item.sizeof, mIOBuffer.getData());
+ mAllocation.subData1D(index, 1, mIOBuffer.getData());
}
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 013f8fc..3333268 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1215,8 +1215,12 @@
prescan(path);
File file = new File(path);
+
+ // lastModified is in milliseconds on Files.
+ long lastModifiedSeconds = file.lastModified() / 1000;
+
// always scan the file, so we can return the content://media Uri for existing files
- return mClient.doScanFile(path, mimeType, file.lastModified(), file.length(), true);
+ return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), true);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
return null;
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index de01dd3..17f2d8f 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -758,7 +758,7 @@
LOGV("queryEffects() numEffects: %d", numEffects);
for (i = 0; i < numEffects; i++) {
- if (AudioEffect::queryNextEffect(&desc) != NO_ERROR) {
+ if (AudioEffect::queryEffect(i, &desc) != NO_ERROR) {
goto queryEffects_failure;
}
diff --git a/media/libeffects/Android.mk b/media/libeffects/Android.mk
index ff21454..b5f1d42 100644
--- a/media/libeffects/Android.mk
+++ b/media/libeffects/Android.mk
@@ -1,5 +1,8 @@
LOCAL_PATH:= $(call my-dir)
+#
+TEST_EFFECT_LIBRARIES := true
+
# Effect factory library
include $(CLEAR_VARS)
@@ -25,7 +28,8 @@
include $(BUILD_SHARED_LIBRARY)
-# Default Reverb library
+ifeq ($(TEST_EFFECT_LIBRARIES),true)
+# Test Reverb library
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -54,7 +58,7 @@
include $(BUILD_SHARED_LIBRARY)
-# Default Equalizer library
+# Test Equalizer library
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -87,4 +91,6 @@
LOCAL_PRELINK_MODULE := false
-include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libeffects/AudioCoefInterpolator.cpp b/media/libeffects/AudioCoefInterpolator.cpp
index 05898c9..039ab9f 100644
--- a/media/libeffects/AudioCoefInterpolator.cpp
+++ b/media/libeffects/AudioCoefInterpolator.cpp
@@ -46,7 +46,7 @@
while (dim-- > 0) {
if (UNLIKELY(intCoord[dim] < 0)) {
fracCoord[dim] = 0;
- } else if (UNLIKELY(intCoord[dim] >= mInDims[dim] - 1)) {
+ } else if (UNLIKELY(intCoord[dim] >= (int)mInDims[dim] - 1)) {
fracCoord[dim] = 0;
index += mInDimOffsets[dim] * (mInDims[dim] - 1);
} else {
diff --git a/media/libeffects/AudioCommon.h b/media/libeffects/AudioCommon.h
index 12d2193e..444f93a 100644
--- a/media/libeffects/AudioCommon.h
+++ b/media/libeffects/AudioCommon.h
@@ -1,4 +1,4 @@
-/* //device/include/server/AudioFlinger/AudioCommon.h
+/*
**
** Copyright 2009, The Android Open Source Project
**
diff --git a/media/libeffects/AudioFormatAdapter.h b/media/libeffects/AudioFormatAdapter.h
index 8aa5e65..d93ebe9 100644
--- a/media/libeffects/AudioFormatAdapter.h
+++ b/media/libeffects/AudioFormatAdapter.h
@@ -75,7 +75,7 @@
while (numSamples > 0) {
uint32_t numSamplesIter = min(numSamples, mMaxSamplesPerCall);
uint32_t nSamplesChannels = numSamplesIter * mNumChannels;
- if (mPcmFormat == PCM_FORMAT_S7_24) {
+ if (mPcmFormat == SAMPLE_FORMAT_PCM_S7_24) {
if (mBehavior == EFFECT_BUFFER_ACCESS_WRITE) {
mpProcessor->process(
reinterpret_cast<const audio_sample_t *> (pIn),
@@ -125,7 +125,7 @@
// sample.
// numSamples The number of single-channel samples to process.
void ConvertInput(const void *& pIn, uint32_t numSamples) {
- if (mPcmFormat == PCM_FORMAT_S15) {
+ if (mPcmFormat == SAMPLE_FORMAT_PCM_S15) {
const int16_t * pIn16 = reinterpret_cast<const int16_t *>(pIn);
audio_sample_t * pOut = mBuffer;
while (numSamples-- > 0) {
@@ -143,7 +143,7 @@
// When function exist will point to the next output sample.
// numSamples The number of single-channel samples to process.
void ConvertOutput(void *& pOut, uint32_t numSamples) {
- if (mPcmFormat == PCM_FORMAT_S15) {
+ if (mPcmFormat == SAMPLE_FORMAT_PCM_S15) {
const audio_sample_t * pIn = mBuffer;
int16_t * pOut16 = reinterpret_cast<int16_t *>(pOut);
if (mBehavior == EFFECT_BUFFER_ACCESS_WRITE) {
diff --git a/media/libeffects/AudioShelvingFilter.cpp b/media/libeffects/AudioShelvingFilter.cpp
index d8abbd2..b8650ba 100644
--- a/media/libeffects/AudioShelvingFilter.cpp
+++ b/media/libeffects/AudioShelvingFilter.cpp
@@ -50,8 +50,8 @@
AudioShelvingFilter::AudioShelvingFilter(ShelfType type, int nChannels,
int sampleRate)
- : mBiquad(nChannels, sampleRate)
- , mType(type) {
+ : mType(type),
+ mBiquad(nChannels, sampleRate) {
configure(nChannels, sampleRate);
}
diff --git a/media/libeffects/EffectEqualizer.cpp b/media/libeffects/EffectEqualizer.cpp
index e39e595..d19c6b9 100644
--- a/media/libeffects/EffectEqualizer.cpp
+++ b/media/libeffects/EffectEqualizer.cpp
@@ -39,10 +39,11 @@
{0xe25aa840, 0x543b, 0x11df, 0x98a5, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
EFFECT_API_VERSION,
(EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST),
+ 0, // TODO
+ 1,
"Graphic Equalizer",
"Google Inc.",
};
-static int gEffectIndex;
/////////////////// BEGIN EQ PRESETS ///////////////////////////////////////////
const int kNumBands = 5;
@@ -101,7 +102,6 @@
AudioEqualizer * pEqualizer;
};
-
//--- local function prototypes
int Equalizer_init(EqualizerContext *pContext);
@@ -116,22 +116,23 @@
extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) {
*pNumEffects = 1;
- gEffectIndex = 0;
return 0;
} /* end EffectQueryNumberEffects */
-extern "C" int EffectQueryNext(effect_descriptor_t *pDescriptor) {
+extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
if (pDescriptor == NULL) {
return -EINVAL;
}
- if (gEffectIndex++ > 0) {
- return -ENOENT;
+ if (index > 0) {
+ return -EINVAL;
}
memcpy(pDescriptor, &gEqualizerDescriptor, sizeof(effect_descriptor_t));
return 0;
} /* end EffectQueryNext */
extern "C" int EffectCreate(effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
effect_interface_t *pInterface) {
int ret;
int i;
@@ -160,7 +161,7 @@
*pInterface = (effect_interface_t)pContext;
- LOGV("EffectLibCreateEffect %p", pContext);
+ LOGV("EffectLibCreateEffect %p, size %d", pContext, AudioEqualizer::GetInstanceSize(kNumBands)+sizeof(EqualizerContext));
return 0;
@@ -219,8 +220,8 @@
CHECK_ARG((pConfig->inputCfg.channels == CHANNEL_MONO) || (pConfig->inputCfg.channels == CHANNEL_STEREO));
CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE
|| pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
- CHECK_ARG(pConfig->inputCfg.format == PCM_FORMAT_S7_24
- || pConfig->inputCfg.format == PCM_FORMAT_S15);
+ CHECK_ARG(pConfig->inputCfg.format == SAMPLE_FORMAT_PCM_S7_24
+ || pConfig->inputCfg.format == SAMPLE_FORMAT_PCM_S15);
int channelCount;
if (pConfig->inputCfg.channels == CHANNEL_MONO) {
@@ -230,6 +231,8 @@
}
CHECK_ARG(channelCount <= AudioBiquadFilter::MAX_CHANNELS);
+ memcpy(&pContext->config, pConfig, sizeof(effect_config_t));
+
pContext->pEqualizer->configure(channelCount,
pConfig->inputCfg.samplingRate);
@@ -268,7 +271,7 @@
pContext->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
pContext->config.inputCfg.channels = CHANNEL_STEREO;
- pContext->config.inputCfg.format = PCM_FORMAT_S15;
+ pContext->config.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
pContext->config.inputCfg.samplingRate = 44100;
pContext->config.inputCfg.bufferProvider.getBuffer = NULL;
pContext->config.inputCfg.bufferProvider.releaseBuffer = NULL;
@@ -276,7 +279,7 @@
pContext->config.inputCfg.mask = EFFECT_CONFIG_ALL;
pContext->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
pContext->config.outputCfg.channels = CHANNEL_STEREO;
- pContext->config.outputCfg.format = PCM_FORMAT_S15;
+ pContext->config.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
pContext->config.outputCfg.samplingRate = 44100;
pContext->config.outputCfg.bufferProvider.getBuffer = NULL;
pContext->config.outputCfg.bufferProvider.releaseBuffer = NULL;
@@ -526,6 +529,7 @@
}
pContext->adapter.process(inBuffer->raw, outBuffer->raw, outBuffer->frameCount);
+
return 0;
} // end Equalizer_process
@@ -589,6 +593,17 @@
*(int *)pReplyData = android::Equalizer_setParameter(pEqualizer, (int32_t *)p->data,
p->data + p->psize);
} break;
+ case EFFECT_CMD_ENABLE:
+ case EFFECT_CMD_DISABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *)pReplyData = 0;
+ break;
+ case EFFECT_CMD_SET_DEVICE:
+ case EFFECT_CMD_SET_VOLUME:
+ case EFFECT_CMD_SET_AUDIO_MODE:
+ break;
default:
LOGW("Equalizer_command invalid command %d",cmdCode);
return -EINVAL;
diff --git a/media/libeffects/EffectReverb.c b/media/libeffects/EffectReverb.c
index 202f50b..ada252c 100644
--- a/media/libeffects/EffectReverb.c
+++ b/media/libeffects/EffectReverb.c
@@ -24,8 +24,6 @@
#include "EffectReverb.h"
#include "EffectsMath.h"
-static int gEffectIndex;
-
// effect_interface_t interface implementation for reverb effect
const struct effect_interface_s gReverbInterface = {
Reverb_Process,
@@ -37,7 +35,10 @@
{0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
{0x1f0ae2e0, 0x4ef7, 0x11df, 0xbc09, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_API_VERSION,
- EFFECT_FLAG_TYPE_AUXILIARY,
+ // flags other than EFFECT_FLAG_TYPE_AUXILIARY set for test purpose
+ EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_AUDIO_MODE_IND,
+ 0, // TODO
+ 33,
"Aux Environmental Reverb",
"Google Inc."
};
@@ -48,6 +49,8 @@
{0xaa476040, 0x6342, 0x11df, 0x91a4, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_API_VERSION,
EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+ 0, // TODO
+ 33,
"Insert Environmental reverb",
"Google Inc."
};
@@ -58,6 +61,8 @@
{0x63909320, 0x53a6, 0x11df, 0xbdbd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_API_VERSION,
EFFECT_FLAG_TYPE_AUXILIARY,
+ 0, // TODO
+ 33,
"Aux Preset Reverb",
"Google Inc."
};
@@ -68,6 +73,8 @@
{0xd93dc6a0, 0x6342, 0x11df, 0xb128, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_API_VERSION,
EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+ 0, // TODO
+ 33,
"Insert Preset Reverb",
"Google Inc."
};
@@ -77,8 +84,7 @@
&gAuxEnvReverbDescriptor,
&gInsertEnvReverbDescriptor,
&gAuxPresetReverbDescriptor,
- &gInsertPresetReverbDescriptor,
- NULL
+ &gInsertPresetReverbDescriptor
};
/*----------------------------------------------------------------------------
@@ -88,25 +94,25 @@
/*--- Effect Library Interface Implementation ---*/
int EffectQueryNumberEffects(uint32_t *pNumEffects) {
- *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)
- - 1;
- gEffectIndex = 0;
+ *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
return 0;
}
-int EffectQueryNext(effect_descriptor_t *pDescriptor) {
+int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
if (pDescriptor == NULL) {
return -EINVAL;
}
- if (gDescriptors[gEffectIndex] == NULL) {
- return -ENOENT;
+ if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) {
+ return -EINVAL;
}
- memcpy(pDescriptor, gDescriptors[gEffectIndex++],
+ memcpy(pDescriptor, gDescriptors[index],
sizeof(effect_descriptor_t));
return 0;
}
int EffectCreate(effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
effect_interface_t *pInterface) {
int ret;
int i;
@@ -152,7 +158,7 @@
*pInterface = (effect_interface_t) module;
- LOGV("EffectLibCreateEffect %p", module);
+ LOGV("EffectLibCreateEffect %p ,size %d", module, sizeof(reverb_module_t));
return 0;
}
@@ -191,8 +197,23 @@
//if bypassed or the preset forces the signal to be completely dry
if (pReverb->m_bBypass) {
- if (inBuffer->raw != outBuffer->raw && !pReverb->m_Aux) {
- memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * NUM_OUTPUT_CHANNELS * sizeof(int16_t));
+ if (inBuffer->raw != outBuffer->raw) {
+ int16_t smp;
+ pSrc = inBuffer->s16;
+ pDst = outBuffer->s16;
+ size_t count = inBuffer->frameCount;
+ if (pRvbModule->config.inputCfg.channels == pRvbModule->config.outputCfg.channels) {
+ count *= 2;
+ while (count--) {
+ *pDst++ = *pSrc++;
+ }
+ } else {
+ while (count--) {
+ smp = *pSrc++;
+ *pDst++ = smp;
+ *pDst++ = smp;
+ }
+ }
}
return 0;
}
@@ -226,10 +247,11 @@
numSamples -= processedSamples;
if (pReverb->m_Aux) {
- pDst += processedSamples;
+ pSrc += processedSamples;
} else {
pSrc += processedSamples * NUM_OUTPUT_CHANNELS;
}
+ pDst += processedSamples * NUM_OUTPUT_CHANNELS;
}
return 0;
@@ -292,6 +314,35 @@
*(int *)pReplyData = Reverb_setParameter(pReverb, *(int32_t *)cmd->data,
cmd->vsize, cmd->data + sizeof(int32_t));
break;
+ case EFFECT_CMD_ENABLE:
+ case EFFECT_CMD_DISABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *)pReplyData = 0;
+ break;
+ case EFFECT_CMD_SET_DEVICE:
+ if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
+ return -EINVAL;
+ }
+ LOGV("Reverb_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData);
+ break;
+ case EFFECT_CMD_SET_VOLUME: {
+ // audio output is always stereo => 2 channel volumes
+ if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
+ return -EINVAL;
+ }
+ float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
+ float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
+ LOGV("Reverb_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
+ break;
+ }
+ case EFFECT_CMD_SET_AUDIO_MODE:
+ if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
+ return -EINVAL;
+ }
+ LOGV("Reverb_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData);
+ break;
default:
LOGW("Reverb_Command invalid command %d",cmdCode);
return -EINVAL;
@@ -339,7 +390,7 @@
} else {
pRvbModule->config.inputCfg.channels = CHANNEL_STEREO;
}
- pRvbModule->config.inputCfg.format = PCM_FORMAT_S15;
+ pRvbModule->config.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
pRvbModule->config.inputCfg.bufferProvider.getBuffer = NULL;
pRvbModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
pRvbModule->config.inputCfg.bufferProvider.cookie = NULL;
@@ -347,7 +398,7 @@
pRvbModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
pRvbModule->config.outputCfg.samplingRate = 44100;
pRvbModule->config.outputCfg.channels = CHANNEL_STEREO;
- pRvbModule->config.outputCfg.format = PCM_FORMAT_S15;
+ pRvbModule->config.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
pRvbModule->config.outputCfg.bufferProvider.getBuffer = NULL;
pRvbModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
pRvbModule->config.outputCfg.bufferProvider.cookie = NULL;
@@ -391,8 +442,8 @@
if (pConfig->inputCfg.samplingRate
!= pConfig->outputCfg.samplingRate
|| pConfig->outputCfg.channels != OUTPUT_CHANNELS
- || pConfig->inputCfg.format != PCM_FORMAT_S15
- || pConfig->outputCfg.format != PCM_FORMAT_S15) {
+ || pConfig->inputCfg.format != SAMPLE_FORMAT_PCM_S15
+ || pConfig->outputCfg.format != SAMPLE_FORMAT_PCM_S15) {
LOGV("Reverb_Configure invalid config");
return -EINVAL;
}
@@ -1033,6 +1084,7 @@
// Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk)
// convert ms to samples
value32 = (value32 * pReverb->m_nSamplingRate) / 1000;
+
// calculate valid decay time range as a function of current reverb delay and
// max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB
// Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels.
@@ -1834,7 +1886,6 @@
//gsReverbObject.m_nXfadeInterval = pPreset->m_nXfadeInterval;
pReverb->m_nXfadeCounter = pReverb->m_nXfadeInterval + 1; // force update on first iteration
-
pReverb->m_nCurrentRoom = pReverb->m_nNextRoom;
return 0;
diff --git a/media/libeffects/EffectReverb.h b/media/libeffects/EffectReverb.h
index 578e09e..f5aadfa 100644
--- a/media/libeffects/EffectReverb.h
+++ b/media/libeffects/EffectReverb.h
@@ -293,8 +293,8 @@
*------------------------------------
*/
int EffectQueryNumberEffects(uint32_t *pNumEffects);
-int EffectQueryNext(effect_descriptor_t *pDescriptor);
-int EffectCreate(effect_uuid_t *effectUID, effect_interface_t *pInterface);
+int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor);
+int EffectCreate(effect_uuid_t *effectUID, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface);
int EffectRelease(effect_interface_t interface);
static int Reverb_Process(effect_interface_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
diff --git a/media/libeffects/EffectsFactory.c b/media/libeffects/EffectsFactory.c
index 6800765..edd6184 100644
--- a/media/libeffects/EffectsFactory.c
+++ b/media/libeffects/EffectsFactory.c
@@ -26,11 +26,16 @@
static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
+static uint32_t gNumEffects; // total number number of effects
static list_elem_t *gCurLib; // current library in enumeration process
static list_elem_t *gCurEffect; // current effect in enumeration process
+static uint32_t gCurEffectIdx; // current effect index in enumeration process
static const char * const gEffectLibPath = "/system/lib/soundfx"; // path to built-in effect libraries
static int gInitDone; // true is global initialization has been preformed
+static int gNextLibId; // used by loadLibrary() to allocate unique library handles
+static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
+ // was not modified since last call to EffectQueryNumberEffects()
/////////////////////////////////////////////////
// Local functions prototypes
@@ -39,7 +44,8 @@
static int init();
static int loadLibrary(const char *libPath, int *handle);
static int unloadLibrary(int handle);
-static uint32_t numEffectModules();
+static void resetEffectEnumeration();
+static uint32_t updateNumEffects();
static int findEffect(effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc);
static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
@@ -107,38 +113,53 @@
}
pthread_mutex_lock(&gLibLock);
- *pNumEffects = numEffectModules();
+ *pNumEffects = gNumEffects;
+ gCanQueryEffect = 1;
pthread_mutex_unlock(&gLibLock);
LOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
return ret;
}
-int EffectQueryNext(effect_descriptor_t *pDescriptor)
+int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
{
int ret = init();
if (ret < 0) {
return ret;
}
- if (pDescriptor == NULL) {
+ if (pDescriptor == NULL ||
+ index >= gNumEffects) {
return -EINVAL;
}
+ if (gCanQueryEffect == 0) {
+ return -ENOSYS;
+ }
pthread_mutex_lock(&gLibLock);
ret = -ENOENT;
+ if (index < gCurEffectIdx) {
+ resetEffectEnumeration();
+ }
while (gCurLib) {
if (gCurEffect) {
- memcpy(pDescriptor, gCurEffect->object, sizeof(effect_descriptor_t));
- gCurEffect = gCurEffect->next;
- ret = 0;
- break;
+ if (index == gCurEffectIdx) {
+ memcpy(pDescriptor, gCurEffect->object, sizeof(effect_descriptor_t));
+ ret = 0;
+ break;
+ } else {
+ gCurEffect = gCurEffect->next;
+ gCurEffectIdx++;
+ }
} else {
gCurLib = gCurLib->next;
gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
}
}
+
+#if (LOG_NDEBUG == 0)
char str[256];
dumpEffectDescriptor(pDescriptor, str, 256);
- LOGV("EffectQueryNext() desc:%s", str);
+ LOGV("EffectQueryEffect() desc:%s", str);
+#endif
pthread_mutex_unlock(&gLibLock);
return ret;
}
@@ -164,7 +185,7 @@
return ret;
}
-int EffectCreate(effect_uuid_t *uuid, effect_interface_t *pInterface)
+int EffectCreate(effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_interface_t *pInterface)
{
list_elem_t *e = gLibraryList;
lib_entry_t *l = NULL;
@@ -198,9 +219,9 @@
}
// create effect in library
- ret = l->createFx(uuid, &itfe);
- if (ret < 0) {
- LOGW("EffectCreate() library %s: could not create fx %s", l->path, d->name);
+ ret = l->createFx(uuid, sessionId, ioId, &itfe);
+ if (ret != 0) {
+ LOGW("EffectCreate() library %s: could not create fx %s, error %d", l->path, d->name, ret);
goto exit;
}
@@ -282,7 +303,10 @@
if (libPath == NULL) {
return -EINVAL;
}
- return loadLibrary(libPath, handle);
+
+ ret = loadLibrary(libPath, handle);
+ updateNumEffects();
+ return ret;
}
int EffectUnloadLibrary(int handle)
@@ -292,7 +316,9 @@
return ret;
}
- return unloadLibrary(handle);
+ ret = unloadLibrary(handle);
+ updateNumEffects();
+ return ret;
}
int EffectIsNullUuid(effect_uuid_t *uuid)
@@ -339,7 +365,7 @@
}
}
closedir(dir);
-
+ updateNumEffects();
gInitDone = 1;
LOGV("init() done");
return 0;
@@ -350,7 +376,7 @@
{
void *hdl;
effect_QueryNumberEffects_t queryNumFx;
- effect_QueryNextEffect_t queryFx;
+ effect_QueryEffect_t queryFx;
effect_CreateEffect_t createFx;
effect_ReleaseEffect_t releaseFx;
uint32_t numFx;
@@ -378,9 +404,9 @@
ret = -ENODEV;
goto error;
}
- queryFx = (effect_QueryNextEffect_t)dlsym(hdl, "EffectQueryNext");
+ queryFx = (effect_QueryEffect_t)dlsym(hdl, "EffectQueryEffect");
if (queryFx == NULL) {
- LOGW("could not get EffectQueryNext from lib %s", libPath);
+ LOGW("could not get EffectQueryEffect from lib %s", libPath);
ret = -ENODEV;
goto error;
}
@@ -409,7 +435,7 @@
ret = -ENOMEM;
goto error;
}
- ret = queryFx(d);
+ ret = queryFx(fx, d);
if (ret == 0) {
#if (LOG_NDEBUG==0)
char s[256];
@@ -434,8 +460,12 @@
LOGW("Error querying effect # %d on lib %s", fx, libPath);
}
}
+
+ pthread_mutex_lock(&gLibLock);
+
// add entry for library in gLibraryList
l = malloc(sizeof(lib_entry_t));
+ l->id = ++gNextLibId;
l->handle = hdl;
strncpy(l->path, libPath, PATH_MAX);
l->createFx = createFx;
@@ -444,14 +474,13 @@
pthread_mutex_init(&l->lock, NULL);
e = malloc(sizeof(list_elem_t));
- pthread_mutex_lock(&gLibLock);
e->next = gLibraryList;
e->object = l;
gLibraryList = e;
pthread_mutex_unlock(&gLibLock);
LOGV("loadLibrary() linked library %p", l);
- *handle = (int)hdl;
+ *handle = l->id;
return 0;
@@ -480,7 +509,7 @@
el2 = NULL;
while (el1) {
l = (lib_entry_t *)el1->object;
- if (handle == (int)l->handle) {
+ if (handle == l->id) {
if (el2) {
el2->next = el1->next;
} else {
@@ -508,6 +537,7 @@
// disable all effects from this library
pthread_mutex_lock(&l->lock);
+
el1 = gEffectList;
while (el1) {
fx = (effect_entry_t *)el1->object;
@@ -523,17 +553,23 @@
return 0;
}
+void resetEffectEnumeration()
+{
+ gCurLib = gLibraryList;
+ gCurEffect = NULL;
+ if (gCurLib) {
+ gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
+ }
+ gCurEffectIdx = 0;
+}
-
-uint32_t numEffectModules() {
- list_elem_t *e = gLibraryList;
+uint32_t updateNumEffects() {
+ list_elem_t *e;
uint32_t cnt = 0;
- // Reset pointers for EffectQueryNext()
- gCurLib = e;
- if (e) {
- gCurEffect = ((lib_entry_t *)e->object)->effects;
- }
+ resetEffectEnumeration();
+
+ e = gLibraryList;
while (e) {
lib_entry_t *l = (lib_entry_t *)e->object;
list_elem_t *efx = l->effects;
@@ -543,6 +579,8 @@
}
e = e->next;
}
+ gNumEffects = cnt;
+ gCanQueryEffect = 0;
return cnt;
}
diff --git a/media/libeffects/EffectsFactory.h b/media/libeffects/EffectsFactory.h
index 17ad3f0..8f543ca 100644
--- a/media/libeffects/EffectsFactory.h
+++ b/media/libeffects/EffectsFactory.h
@@ -20,7 +20,7 @@
#include <cutils/log.h>
#include <pthread.h>
#include <dirent.h>
-#include <media/EffectFactoryApi.h>
+#include <media/EffectsFactoryApi.h>
#if __cplusplus
@@ -35,6 +35,7 @@
typedef struct lib_entry_s {
char path[PATH_MAX];
void *handle;
+ int id;
effect_CreateEffect_t createFx;
effect_ReleaseEffect_t releaseFx;
list_elem_t *effects; //list of effect_descriptor_t
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 8648211..4afa2dc 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -394,11 +394,11 @@
return af->queryNumberEffects(numEffects);
}
-status_t AudioEffect::queryNextEffect(effect_descriptor_t *descriptor)
+status_t AudioEffect::queryEffect(uint32_t index, effect_descriptor_t *descriptor)
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
- return af->queryNextEffect(descriptor);
+ return af->queryEffect(index, descriptor);
}
status_t AudioEffect::getEffectDescriptor(effect_uuid_t *uuid, effect_descriptor_t *descriptor)
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index f2a8db3..7d6a5d3 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -67,7 +67,7 @@
LOAD_EFFECT_LIBRARY,
UNLOAD_EFFECT_LIBRARY,
QUERY_NUM_EFFECTS,
- QUERY_NEXT_EFFECT,
+ QUERY_EFFECT,
GET_EFFECT_DESCRIPTOR,
CREATE_EFFECT
};
@@ -586,14 +586,15 @@
return NO_ERROR;
}
- virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor)
+ virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
{
if (pDescriptor == NULL) {
return BAD_VALUE;
}
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- status_t status = remote()->transact(QUERY_NEXT_EFFECT, data, &reply);
+ data.writeInt32(index);
+ status_t status = remote()->transact(QUERY_EFFECT, data, &reply);
if (status != NO_ERROR) {
return status;
}
@@ -980,10 +981,10 @@
}
return NO_ERROR;
}
- case QUERY_NEXT_EFFECT: {
+ case QUERY_EFFECT: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
effect_descriptor_t desc;
- status_t status = queryNextEffect(&desc);
+ status_t status = queryEffect(data.readInt32(), &desc);
reply->writeInt32(status);
if (status == NO_ERROR) {
reply->write(&desc, sizeof(effect_descriptor_t));
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index d49c4e0..1e20f7e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -310,12 +310,46 @@
// If interval < 0, only the first frame is I frame, and rest are all P frames
// If interval == 0, all frames are encoded as I frames. No P frames
// If interval > 0, it is the time spacing between 2 neighboring I frames
-status_t StagefrightRecorder::setParamIFramesInterval(int32_t interval) {
- LOGV("setParamIFramesInterval: %d seconds", interval);
+status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) {
+ LOGV("setParamVideoIFramesInterval: %d seconds", interval);
mIFramesInterval = interval;
return OK;
}
+status_t StagefrightRecorder::setParam64BitFileOffset(bool use64Bit) {
+ LOGV("setParam64BitFileOffset: %s",
+ use64Bit? "use 64 bit file offset": "use 32 bit file offset");
+ mUse64BitFileOffset = use64Bit;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) {
+ LOGV("setParamVideoCameraId: %d", cameraId);
+ if (cameraId < 0) {
+ return BAD_VALUE;
+ }
+ mCameraId = cameraId;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) {
+ LOGV("setParamTrackFrameStatus: %d", nFrames);
+ if (nFrames <= 0) {
+ return BAD_VALUE;
+ }
+ mTrackEveryNumberOfFrames = nFrames;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) {
+ LOGV("setParamTrackTimeStatus: %lld", timeDurationUs);
+ if (timeDurationUs < 20000) { // Infeasible if shorter than 20 ms?
+ return BAD_VALUE;
+ }
+ mTrackEveryTimeDurationUs = timeDurationUs;
+ return OK;
+}
+
status_t StagefrightRecorder::setParameter(
const String8 &key, const String8 &value) {
LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -331,6 +365,26 @@
return setParamMaxDurationOrFileSize(
max_filesize_bytes, false /* limit is filesize */);
}
+ } else if (key == "interleave-duration-us") {
+ int32_t durationUs;
+ if (safe_strtoi32(value.string(), &durationUs)) {
+ return setParamInterleaveDuration(durationUs);
+ }
+ } else if (key == "param-use-64bit-offset") {
+ int32_t use64BitOffset;
+ if (safe_strtoi32(value.string(), &use64BitOffset)) {
+ return setParam64BitFileOffset(use64BitOffset != 0);
+ }
+ } else if (key == "param-track-frame-status") {
+ int32_t nFrames;
+ if (safe_strtoi32(value.string(), &nFrames)) {
+ return setParamTrackFrameStatus(nFrames);
+ }
+ } else if (key == "param-track-time-status") {
+ int64_t timeDurationUs;
+ if (safe_strtoi64(value.string(), &timeDurationUs)) {
+ return setParamTrackTimeStatus(timeDurationUs);
+ }
} else if (key == "audio-param-sampling-rate") {
int32_t sampling_rate;
if (safe_strtoi32(value.string(), &sampling_rate)) {
@@ -351,15 +405,15 @@
if (safe_strtoi32(value.string(), &video_bitrate)) {
return setParamVideoEncodingBitRate(video_bitrate);
}
- } else if (key == "param-interleave-duration-us") {
- int32_t durationUs;
- if (safe_strtoi32(value.string(), &durationUs)) {
- return setParamInterleaveDuration(durationUs);
- }
- } else if (key == "param-i-frames-interval") {
+ } else if (key == "video-param-i-frames-interval") {
int32_t interval;
if (safe_strtoi32(value.string(), &interval)) {
- return setParamIFramesInterval(interval);
+ return setParamVideoIFramesInterval(interval);
+ }
+ } else if (key == "video-param-camera-id") {
+ int32_t cameraId;
+ if (safe_strtoi32(value.string(), &cameraId)) {
+ return setParamVideoCameraId(cameraId);
}
} else {
LOGE("setParameter: failed to find key %s", key.string());
@@ -484,6 +538,7 @@
sp<MediaSource> audioEncoder =
OMXCodec::Create(client.interface(), encMeta,
true /* createEncoder */, audioSource);
+ mAudioSourceNode = audioSource;
return audioEncoder;
}
@@ -632,6 +687,7 @@
status_t StagefrightRecorder::startMPEG4Recording() {
mWriter = new MPEG4Writer(dup(mOutputFd));
+ int32_t totalBitRate = 0;
// Add audio source first if it exists
if (mAudioSource != AUDIO_SOURCE_LIST_END) {
@@ -650,7 +706,7 @@
if (audioEncoder == NULL) {
return UNKNOWN_ERROR;
}
-
+ totalBitRate += mAudioBitRate;
mWriter->addSource(audioEncoder);
}
if (mVideoSource == VIDEO_SOURCE_DEFAULT
@@ -663,7 +719,7 @@
int64_t token = IPCThreadState::self()->clearCallingIdentity();
if (mCamera == 0) {
- mCamera = Camera::connect(0);
+ mCamera = Camera::connect(mCameraId);
mCamera->lock();
}
@@ -703,7 +759,7 @@
sp<MetaData> enc_meta = new MetaData;
enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
- enc_meta->setInt32(kKeySampleRate, mFrameRate); // XXX: kKeySampleRate?
+ enc_meta->setInt32(kKeySampleRate, mFrameRate);
switch (mVideoEncoder) {
case VIDEO_ENCODER_H263:
@@ -746,12 +802,13 @@
true /* createEncoder */, cameraSource);
CHECK(mOutputFd >= 0);
+ totalBitRate += mVideoBitRate;
mWriter->addSource(encoder);
}
{
// MPEGWriter specific handling
- MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get()); // mWriter is an MPEGWriter
+ MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get());
writer->setInterleaveDuration(mInterleaveDurationUs);
}
@@ -762,7 +819,18 @@
mWriter->setMaxFileSize(mMaxFileSizeBytes);
}
mWriter->setListener(mListener);
- mWriter->start();
+ sp<MetaData> meta = new MetaData;
+ meta->setInt64(kKeyTime, systemTime() / 1000);
+ meta->setInt32(kKeyFileType, mOutputFormat);
+ meta->setInt32(kKeyBitRate, totalBitRate);
+ meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
+ if (mTrackEveryNumberOfFrames > 0) {
+ meta->setInt32(kKeyTrackFrameStatus, mTrackEveryNumberOfFrames);
+ }
+ if (mTrackEveryTimeDurationUs > 0) {
+ meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
+ }
+ mWriter->start(meta.get());
return OK;
}
@@ -822,6 +890,11 @@
mAudioBitRate = 12200;
mInterleaveDurationUs = 0;
mIFramesInterval = 1;
+ mAudioSourceNode = 0;
+ mUse64BitFileOffset = false;
+ mCameraId = 0;
+ mTrackEveryNumberOfFrames = 0;
+ mTrackEveryTimeDurationUs = 0;
mEncoderProfiles = MediaProfiles::getInstance();
mOutputFd = -1;
@@ -831,7 +904,11 @@
}
status_t StagefrightRecorder::getMaxAmplitude(int *max) {
- *max = 0;
+ if (mAudioSourceNode != 0) {
+ *max = mAudioSourceNode->getMaxAmplitude();
+ } else {
+ *max = 0;
+ }
return OK;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 7de96f6..9fb7e8f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -26,6 +26,7 @@
class Camera;
struct MediaSource;
struct MediaWriter;
+struct AudioSource;
class MediaProfiles;
struct StagefrightRecorder : public MediaRecorderBase {
@@ -64,12 +65,14 @@
sp<ISurface> mPreviewSurface;
sp<IMediaPlayerClient> mListener;
sp<MediaWriter> mWriter;
+ sp<AudioSource> mAudioSourceNode;
audio_source mAudioSource;
video_source mVideoSource;
output_format mOutputFormat;
audio_encoder mAudioEncoder;
video_encoder mVideoEncoder;
+ bool mUse64BitFileOffset;
int32_t mVideoWidth, mVideoHeight;
int32_t mFrameRate;
int32_t mVideoBitRate;
@@ -78,8 +81,11 @@
int32_t mSampleRate;
int32_t mInterleaveDurationUs;
int32_t mIFramesInterval;
+ int32_t mCameraId;
int64_t mMaxFileSizeBytes;
int64_t mMaxFileDurationUs;
+ int32_t mTrackEveryNumberOfFrames;
+ int64_t mTrackEveryTimeDurationUs;
String8 mParams;
int mOutputFd;
@@ -92,12 +98,16 @@
status_t startAACRecording();
sp<MediaSource> createAudioSource();
status_t setParameter(const String8 &key, const String8 &value);
- status_t setParamVideoEncodingBitRate(int32_t bitRate);
status_t setParamAudioEncodingBitRate(int32_t bitRate);
status_t setParamAudioNumberOfChannels(int32_t channles);
status_t setParamAudioSamplingRate(int32_t sampleRate);
+ status_t setParamVideoEncodingBitRate(int32_t bitRate);
+ status_t setParamVideoIFramesInterval(int32_t interval);
+ status_t setParamVideoCameraId(int32_t cameraId);
+ status_t setParamTrackTimeStatus(int64_t timeDurationUs);
+ status_t setParamTrackFrameStatus(int32_t nFrames);
status_t setParamInterleaveDuration(int32_t durationUs);
- status_t setParamIFramesInterval(int32_t interval);
+ status_t setParam64BitFileOffset(bool use64BitFileOffset);
status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
void clipVideoBitRate();
void clipVideoFrameRate();
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 8951f5b..c71743e 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -97,7 +97,7 @@
return OK;
}
-status_t AMRWriter::start() {
+status_t AMRWriter::start(MetaData *params) {
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -253,7 +253,7 @@
}
if (stoppedPrematurely) {
- notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
}
fflush(mFile);
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index d203dbf..6031797 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -78,6 +78,8 @@
mCollectStats = true;
}
+ mTrackMaxAmplitude = false;
+ mMaxAmplitude = 0;
mStartTimeUs = 0;
int64_t startTimeUs;
if (params && params->findInt64(kKeyTime, &startTimeUs)) {
@@ -168,6 +170,10 @@
return (status_t)n;
}
+ if (mTrackMaxAmplitude) {
+ trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
+ }
+
uint32_t sampleRate = mRecord->getSampleRate();
int64_t timestampUs = (1000000LL * numFramesRecorded) / sampleRate + mStartTimeUs;
buffer->meta_data()->setInt64(kKeyTime, timestampUs);
@@ -181,4 +187,27 @@
return OK;
}
+void AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) {
+ for (int i = nSamples; i > 0; --i) {
+ int16_t value = *data++;
+ if (value < 0) {
+ value = -value;
+ }
+ if (mMaxAmplitude < value) {
+ mMaxAmplitude = value;
+ }
+ }
+}
+
+int16_t AudioSource::getMaxAmplitude() {
+ // First call activates the tracking.
+ if (!mTrackMaxAmplitude) {
+ mTrackMaxAmplitude = true;
+ }
+ int16_t value = mMaxAmplitude;
+ mMaxAmplitude = 0;
+ LOGV("max amplitude since last call: %d", value);
+ return value;
+}
+
} // namespace android
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f16b225..b3e1a01 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -41,7 +41,7 @@
Track(MPEG4Writer *owner, const sp<MediaSource> &source);
~Track();
- status_t start(int64_t startTimeUs);
+ status_t start(MetaData *params);
void stop();
void pause();
bool reachedEOS();
@@ -101,9 +101,13 @@
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
bool mGotAllCodecSpecificData;
+ bool mTrackingProgressStatus;
bool mReachedEOS;
int64_t mStartTimestampUs;
+ int64_t mPreviousTrackTimeUs;
+ int64_t mTrackEveryTimeDurationUs;
+ int32_t mTrackEveryNumberOfFrames;
static void *ThreadWrapper(void *me);
void threadEntry();
@@ -114,6 +118,8 @@
void logStatisticalData(bool isAudio);
void findMinMaxFrameRates(float *minFps, float *maxFps);
void findMinMaxChunkDurations(int64_t *min, int64_t *max);
+ void trackProgressStatus(int32_t nFrames, int64_t timeUs);
+ void initTrackingProgressStatus(MetaData *params);
Track(const Track &);
Track &operator=(const Track &);
@@ -162,11 +168,10 @@
return OK;
}
-status_t MPEG4Writer::startTracks() {
- int64_t startTimeUs = systemTime() / 1000;
+status_t MPEG4Writer::startTracks(MetaData *params) {
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- status_t err = (*it)->start(startTimeUs);
+ status_t err = (*it)->start(params);
if (err != OK) {
for (List<Track *>::iterator it2 = mTracks.begin();
@@ -180,16 +185,78 @@
return OK;
}
-status_t MPEG4Writer::start() {
+int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
+ // This implementation is highly experimental/heurisitic.
+ //
+ // Statistical analysis shows that metadata usually accounts
+ // for a small portion of the total file size, usually < 0.6%.
+ // Currently, lets set to 0.4% for now.
+
+ // The default MIN_MOOV_BOX_SIZE is set to 0.4% x 1MB,
+ // where 1MB is the common file size limit for MMS application.
+ // The default MAX _MOOV_BOX_SIZE value is based on about 4
+ // minute video recording with a bit rate about 3 Mbps, because
+ // statistics also show that most of the video captured are going
+ // to be less than 3 minutes.
+
+ // If the estimation is wrong, we will pay the price of wasting
+ // some reserved space. This should not happen so often statistically.
+ static const int32_t factor = mUse32BitOffset? 1: 2;
+ static const int64_t MIN_MOOV_BOX_SIZE = 4 * 1024; // 4 KB
+ static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
+ int64_t size = MIN_MOOV_BOX_SIZE;
+
+ if (mMaxFileSizeLimitBytes != 0) {
+ size = mMaxFileSizeLimitBytes * 4 / 1000;
+ } else if (mMaxFileDurationLimitUs != 0) {
+ if (bitRate <= 0) {
+ // We could not estimate the file size since bitRate is not set.
+ size = MIN_MOOV_BOX_SIZE;
+ } else {
+ size = ((mMaxFileDurationLimitUs * bitRate * 4) / 1000 / 8000000);
+ }
+ }
+ if (size < MIN_MOOV_BOX_SIZE) {
+ size = MIN_MOOV_BOX_SIZE;
+ }
+
+ // Any long duration recording will be probably end up with
+ // non-streamable mp4 file.
+ if (size > MAX_MOOV_BOX_SIZE) {
+ size = MAX_MOOV_BOX_SIZE;
+ }
+
+ LOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
+ " moov size %lld bytes",
+ mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
+ return factor * size;
+}
+
+status_t MPEG4Writer::start(MetaData *param) {
if (mFile == NULL) {
return UNKNOWN_ERROR;
}
+ int32_t use64BitOffset;
+ if (param &&
+ param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
+ use64BitOffset) {
+ mUse32BitOffset = false;
+ }
+
+ // System property can overwrite the file offset bits parameter
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.record-64bits", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ mUse32BitOffset = false;
+ }
+
mStartTimestampUs = -1;
+
if (mStarted) {
if (mPaused) {
mPaused = false;
- return startTracks();
+ return startTracks(param);
}
return OK;
}
@@ -200,17 +267,28 @@
mMoovBoxBufferOffset = 0;
beginBox("ftyp");
- writeFourcc("isom");
+ {
+ int32_t fileType;
+ if (param && param->findInt32(kKeyFileType, &fileType) &&
+ fileType != OUTPUT_FORMAT_MPEG_4) {
+ writeFourcc("3gp4");
+ } else {
+ writeFourcc("isom");
+ }
+ }
writeInt32(0);
writeFourcc("isom");
+ writeFourcc("3gp4");
endBox();
mFreeBoxOffset = mOffset;
if (mEstimatedMoovBoxSize == 0) {
- // XXX: Estimate the moov box size
- // based on max file size or duration limit
- mEstimatedMoovBoxSize = 0x0F00;
+ int32_t bitRate = -1;
+ if (param) {
+ param->findInt32(kKeyBitRate, &bitRate);
+ }
+ mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
}
CHECK(mEstimatedMoovBoxSize >= 8);
fseeko(mFile, mFreeBoxOffset, SEEK_SET);
@@ -225,8 +303,7 @@
} else {
write("\x00\x00\x00\x01mdat????????", 16);
}
-
- status_t err = startTracks();
+ status_t err = startTracks(param);
if (err != OK) {
return err;
}
@@ -332,8 +409,7 @@
write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
// Free box
- mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset;
- fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+ fseeko(mFile, mOffset, SEEK_SET);
writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
write("free", 4);
@@ -341,6 +417,8 @@
free(mMoovBoxBuffer);
mMoovBoxBuffer = NULL;
mMoovBoxBufferOffset = 0;
+ } else {
+ LOGI("The mp4 file will not be streamable.");
}
CHECK(mBoxes.empty());
@@ -606,13 +684,41 @@
}
}
-status_t MPEG4Writer::Track::start(int64_t startTimeUs) {
+void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
+ LOGV("initTrackingProgressStatus");
+ mPreviousTrackTimeUs = -1;
+ mTrackingProgressStatus = false;
+ mTrackEveryTimeDurationUs = 0;
+ mTrackEveryNumberOfFrames = 0;
+ {
+ int64_t timeUs;
+ if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
+ LOGV("Receive request to track progress status for every %lld us", timeUs);
+ mTrackEveryTimeDurationUs = timeUs;
+ mTrackingProgressStatus = true;
+ }
+ }
+ {
+ int32_t nFrames;
+ if (params && params->findInt32(kKeyTrackFrameStatus, &nFrames)) {
+ LOGV("Receive request to track progress status for every %d frames", nFrames);
+ mTrackEveryNumberOfFrames = nFrames;
+ mTrackingProgressStatus = true;
+ }
+ }
+}
+
+status_t MPEG4Writer::Track::start(MetaData *params) {
if (!mDone && mPaused) {
mPaused = false;
mResumed = true;
return OK;
}
+ int64_t startTimeUs;
+ CHECK(params && params->findInt64(kKeyTime, &startTimeUs));
+ initTrackingProgressStatus(params);
+
sp<MetaData> meta = new MetaData;
meta->setInt64(kKeyTime, startTimeUs);
status_t err = mSource->start(meta.get());
@@ -784,8 +890,9 @@
int64_t previousPausedDurationUs = 0;
sp<MetaData> meta_data;
+ status_t err = OK;
MediaBuffer *buffer;
- while (!mDone && mSource->read(&buffer) == OK) {
+ while (!mDone && (err = mSource->read(&buffer)) == OK) {
if (buffer->range_length() == 0) {
buffer->release();
buffer = NULL;
@@ -998,6 +1105,12 @@
mStssTableEntries.push_back(mSampleInfos.size());
}
+ if (mTrackingProgressStatus) {
+ if (mPreviousTrackTimeUs <= 0) {
+ mPreviousTrackTimeUs = mStartTimestampUs;
+ }
+ trackProgressStatus(mSampleInfos.size(), timestampUs);
+ }
if (mOwner->numTracks() == 1) {
off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy)
: mOwner->addSample_l(copy);
@@ -1037,8 +1150,9 @@
}
if (mSampleInfos.empty()) {
- mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
+ err = UNKNOWN_ERROR;
}
+ mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, err);
// Last chunk
if (mOwner->numTracks() == 1) {
@@ -1068,6 +1182,24 @@
logStatisticalData(is_audio);
}
+void MPEG4Writer::Track::trackProgressStatus(int32_t nFrames, int64_t timeUs) {
+ LOGV("trackProgressStatus: %d frames and %lld us", nFrames, timeUs);
+ if (nFrames % mTrackEveryNumberOfFrames == 0) {
+ LOGV("Fire frame tracking progress status at frame %d", nFrames);
+ mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
+ MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS,
+ nFrames);
+ }
+
+ if (timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
+ LOGV("Fire time tracking progress status at %lld us", timeUs);
+ mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
+ MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
+ timeUs / 1000);
+ mPreviousTrackTimeUs = timeUs;
+ }
+}
+
void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) {
int32_t minSampleDuration = 0x7FFFFFFF;
int32_t maxSampleDuration = 0;
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index f73e837..3a20c16 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -49,8 +49,8 @@
friend struct AHandlerReflector<NuCachedSource2>;
enum {
- kPageSize = 16384,
- kHighWaterThreshold = 3 * 1024 * 1024,
+ kPageSize = 65536,
+ kHighWaterThreshold = 5 * 1024 * 1024,
kLowWaterThreshold = 512 * 1024,
// Read data after a 15 sec timeout whether we're actively
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 6d9888b..35baaa7 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -619,6 +619,7 @@
}
public WifiConfiguration getWifiApConfiguration() {
+ enforceAccessPermission();
final ContentResolver cr = mContext.getContentResolver();
WifiConfiguration wifiConfig = new WifiConfiguration();
int authType;
@@ -636,7 +637,8 @@
}
}
- private void persistApConfiguration(WifiConfiguration wifiConfig) {
+ public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
+ enforceChangePermission();
final ContentResolver cr = mContext.getContentResolver();
boolean isWpa;
if (wifiConfig == null)
@@ -669,7 +671,7 @@
try {
nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
SOFTAP_IFACE);
- persistApConfiguration(wifiConfig);
+ setWifiApConfiguration(wifiConfig);
return true;
} catch(Exception e) {
Slog.e(TAG, "Exception in nwService during AP restart");
@@ -721,7 +723,7 @@
return false;
}
- persistApConfiguration(wifiConfig);
+ setWifiApConfiguration(wifiConfig);
} else {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 252392b..2c6806b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -26,6 +26,7 @@
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.WindowManagerService;
+import com.android.server.am.ActivityStack.ActivityState;
import dalvik.system.Zygote;
@@ -183,47 +184,21 @@
// 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;
-
- // How long until we reset a task when the user returns to it. Currently
- // 30 minutes.
- static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
-
- // Set to true to disable the icon that is shown while a new activity
- // is being started.
- static final boolean SHOW_APP_STARTING_ICON = true;
-
- // How long we wait until giving up on the last activity to pause. This
- // is short because it directly impacts the responsiveness of starting the
- // next activity.
- static final int PAUSE_TIMEOUT = 500;
-
- /**
- * How long we can hold the launch wake lock before giving up.
- */
- static final int LAUNCH_TIMEOUT = 10*1000;
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
- // How long we wait until giving up on the last activity telling us it
- // is idle.
- static final int IDLE_TIMEOUT = 10*1000;
-
// How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000;
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
- // 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 we allow a receiver to run before giving up on it.
static final int BROADCAST_TIMEOUT = 10*1000;
@@ -388,29 +363,13 @@
static final String[] EMPTY_STRING_ARRAY = new String[0];
- enum ActivityState {
- INITIALIZING,
- RESUMED,
- PAUSING,
- PAUSED,
- STOPPING,
- STOPPED,
- FINISHING,
- DESTROYING,
- DESTROYED
- }
-
- /**
- * The back history of all previous (and possibly still
- * running) activities. It contains HistoryRecord objects.
- */
- final ArrayList mHistory = new ArrayList();
-
+ public ActivityStack mMainStack;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
*/
- class PendingActivityLaunch {
+ static class PendingActivityLaunch {
ActivityRecord r;
ActivityRecord sourceRecord;
Uri[] grantedUriPermissions;
@@ -422,18 +381,6 @@
= new ArrayList<PendingActivityLaunch>();
/**
- * 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.
- */
- final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
- = new ArrayList<IActivityManager.WaitResult>();
-
- /**
* List of all active broadcasts that are to be executed immediately
* (without waiting for another broadcast to finish). Currently this only
* contains broadcasts to registered receivers, to avoid spinning up
@@ -463,57 +410,9 @@
boolean mBroadcastsScheduled = false;
/**
- * Set to indicate whether to issue an onUserLeaving callback when a
- * newly launched activity is being brought in front of us.
- */
- boolean mUserLeaving = false;
-
- /**
- * When we are in the process of pausing an activity, before starting the
- * next one, this variable holds the activity that is currently being paused.
- */
- ActivityRecord mPausingActivity = null;
-
- /**
- * Current activity that is resumed, or null if there is none.
- */
- ActivityRecord mResumedActivity = null;
-
- /**
* Activity we have told the window manager to have key focus.
*/
ActivityRecord mFocusedActivity = null;
-
- /**
- * This is the last activity that we put into the paused state. This is
- * used to determine if we need to do an activity transition while sleeping,
- * when we normally hold the top activity paused.
- */
- ActivityRecord mLastPausedActivity = null;
-
- /**
- * 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>();
-
- /**
- * Animations that for the current transition have requested not to
- * be considered for the transition animation.
- */
- final ArrayList<ActivityRecord> mNoAnimActivities
- = new ArrayList<ActivityRecord>();
-
/**
* List of intents that were used to start the most recent tasks.
*/
@@ -521,14 +420,6 @@
= new ArrayList<TaskRecord>();
/**
- * List of activities that are ready to be finished, but waiting
- * for the previous activity to settle down before doing so. It contains
- * HistoryRecord objects.
- */
- final ArrayList<ActivityRecord> mFinishingActivities
- = new ArrayList<ActivityRecord>();
-
- /**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
* returned by the package manager), and the keys are ApplicationRecord
@@ -628,16 +519,9 @@
* This is the process holding what we currently consider to be
* the "home" activity.
*/
- private ProcessRecord mHomeProcess;
+ ProcessRecord mHomeProcess;
/**
- * List of running activities, sorted by recent usage.
- * The first entry in the list is the least recently used.
- * It contains HistoryRecord objects.
- */
- private final ArrayList mLRUActivities = new ArrayList();
-
- /**
* Set of PendingResultRecord objects that are currently active.
*/
final HashSet mPendingResultRecords = new HashSet();
@@ -831,12 +715,6 @@
int mConfigurationSeq = 0;
/**
- * Set when we know we are going to be calling updateConfiguration()
- * soon, so want to skip intermediate config checks.
- */
- boolean mConfigWillChange;
-
- /**
* Hardware-reported OpenGLES version.
*/
final int GL_ES_VERSION;
@@ -892,21 +770,6 @@
* Set if we are shutting down the system, similar to sleeping.
*/
boolean mShuttingDown = false;
-
- /**
- * Set when the system is going to sleep, until we have
- * successfully paused the current activity and released our wake lock.
- * At that point the system is allowed to actually sleep.
- */
- PowerManager.WakeLock mGoingToSleep;
-
- /**
- * We don't want to allow the device to go to sleep while in the process
- * of launching an activity. This is primarily to allow alarm intent
- * receivers to launch an activity and get that to run before the device
- * goes back to sleep.
- */
- PowerManager.WakeLock mLaunchingActivity;
/**
* Task identifier that activities are currently being started
@@ -986,8 +849,6 @@
long mLastWriteTime = 0;
- long mInitialStartTime = 0;
-
/**
* Set to true after the system has finished booting.
*/
@@ -1034,16 +895,10 @@
static final int WAIT_FOR_DEBUGGER_MSG = 6;
static final int BROADCAST_INTENT_MSG = 7;
static final int BROADCAST_TIMEOUT_MSG = 8;
- static final int PAUSE_TIMEOUT_MSG = 9;
- static final int IDLE_TIMEOUT_MSG = 10;
- static final int IDLE_NOW_MSG = 11;
static final int SERVICE_TIMEOUT_MSG = 12;
static final int UPDATE_TIME_ZONE = 13;
static final int SHOW_UID_ERROR_MSG = 14;
static final int IM_FEELING_LUCKY_MSG = 15;
- static final int LAUNCH_TIMEOUT_MSG = 16;
- static final int DESTROY_TIMEOUT_MSG = 17;
- static final int RESUME_TOP_ACTIVITY_MSG = 19;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
static final int KILL_APPLICATION_MSG = 22;
@@ -1183,38 +1038,6 @@
broadcastTimeout();
}
} break;
- case PAUSE_TIMEOUT_MSG: {
- IBinder token = (IBinder)msg.obj;
- // 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 pause timeout for " + token);
- activityPaused(token, null, true);
- } break;
- case IDLE_TIMEOUT_MSG: {
- if (mDidDexOpt) {
- mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
- nmsg.obj = msg.obj;
- mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
- return;
- }
- // We don't at this point know if the activity is fullscreen,
- // so we need to be conservative and assume it isn't.
- IBinder token = (IBinder)msg.obj;
- Slog.w(TAG, "Activity idle timeout for " + token);
- activityIdleInternal(token, true, null);
- } break;
- case DESTROY_TIMEOUT_MSG: {
- IBinder token = (IBinder)msg.obj;
- // 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 " + token);
- activityDestroyed(token);
- } break;
- case IDLE_NOW_MSG: {
- IBinder token = (IBinder)msg.obj;
- activityIdle(token, null);
- } break;
case SERVICE_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
@@ -1257,25 +1080,6 @@
mUidAlert = null;
}
} break;
- case LAUNCH_TIMEOUT_MSG: {
- if (mDidDexOpt) {
- mDidDexOpt = false;
- Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
- return;
- }
- synchronized (ActivityManagerService.this) {
- if (mLaunchingActivity.isHeld()) {
- Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
- mLaunchingActivity.release();
- }
- }
- } break;
- case RESUME_TOP_ACTIVITY_MSG: {
- synchronized (ActivityManagerService.this) {
- resumeTopActivityLocked(null);
- }
- } break;
case PROC_START_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
@@ -1424,11 +1228,7 @@
Context context = at.getSystemContext();
m.mContext = context;
m.mFactoryTest = factoryTest;
- PowerManager pm =
- (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- m.mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
- m.mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
- m.mLaunchingActivity.setReferenceCounted(false);
+ m.mMainStack = new ActivityStack(m, context, true);
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
@@ -1717,7 +1517,7 @@
return mAppBindArgs;
}
- private final void setFocusedActivityLocked(ActivityRecord r) {
+ final void setFocusedActivityLocked(ActivityRecord r) {
if (mFocusedActivity != r) {
mFocusedActivity = r;
mWindowManager.setFocusedApp(r, true);
@@ -1802,65 +1602,13 @@
}
}
- private final void updateLruProcessLocked(ProcessRecord app,
+ final void updateLruProcessLocked(ProcessRecord app,
boolean oomAdj, boolean updateActivityTime) {
mLruSeq++;
updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0);
}
- private final boolean updateLRUListLocked(ActivityRecord r) {
- final boolean hadit = mLRUActivities.remove(r);
- mLRUActivities.add(r);
- return hadit;
- }
-
- private final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (!r.finishing && r != notTop) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- private final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (!r.finishing && !r.delayedResume && r != notTop) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- /**
- * 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.
- */
- private final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
- int i = mHistory.size()-1;
- while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- // Note: the taskId check depends on real taskId fields being non-zero
- if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
- return r;
- }
- i--;
- }
- return null;
- }
-
- private final ProcessRecord getProcessRecordLocked(
+ final ProcessRecord getProcessRecordLocked(
String processName, int uid) {
if (uid == Process.SYSTEM_UID) {
// The system gets to run in any process. If there are multiple
@@ -1874,7 +1622,7 @@
return proc;
}
- private void ensurePackageDexOpt(String packageName) {
+ void ensurePackageDexOpt(String packageName) {
IPackageManager pm = AppGlobals.getPackageManager();
try {
if (pm.performDexOpt(packageName)) {
@@ -1884,175 +1632,14 @@
}
}
- private boolean isNextTransitionForward() {
+ boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
|| transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
|| transit == WindowManagerPolicy.TRANSIT_TASK_TO_FRONT;
}
- private final boolean realStartActivityLocked(ActivityRecord r,
- ProcessRecord app, boolean andResume, boolean checkConfig)
- throws RemoteException {
-
- r.startFreezingScreenLocked(app, 0);
- mWindowManager.setAppVisibility(r, true);
-
- // 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 = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- r.mayFreezeScreenLocked(app) ? r : null);
- updateConfigurationLocked(config, r);
- }
-
- r.app = app;
-
- if (localLOGV) Slog.v(TAG, "Launching: " + r);
-
- int idx = app.activities.indexOf(r);
- if (idx < 0) {
- app.activities.add(r);
- }
- updateLruProcessLocked(app, true, 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,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
- }
- if (r.isHomeActivity) {
- mHomeProcess = app;
- }
- ensurePackageDexOpt(r.intent.getComponent().getPackageName());
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
- System.identityHashCode(r),
- r.info, r.icicle, results, newIntents, !andResume,
- isNextTransitionForward());
-
- if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 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 (mHeavyWeightProcess != null && mHeavyWeightProcess != app) {
- Log.w(TAG, "Starting new heavy weight process " + app
- + " when already running " + mHeavyWeightProcess);
- }
- mHeavyWeightProcess = app;
- Message msg = mHandler.obtainMessage(POST_HEAVY_NOTIFICATION_MSG);
- msg.obj = r;
- 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);
- appDiedLocked(app, app.pid, app.thread);
- requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
- "2nd-crash");
- 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;
- r.icicle = null;
- r.haveState = false;
- r.stopped = false;
- mResumedActivity = r;
- r.task.touchActiveTime();
- completeResumeLocked(r);
- pauseIfSleepingLocked();
- } 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.
- 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.
- startSetupActivityLocked();
-
- return true;
- }
-
- private final void startSpecificActivityLocked(ActivityRecord r,
- boolean andResume, boolean checkConfig) {
- // Is this activity's application already running?
- ProcessRecord app = getProcessRecordLocked(r.processName,
- r.info.applicationInfo.uid);
-
- if (r.startTime == 0) {
- r.startTime = SystemClock.uptimeMillis();
- if (mInitialStartTime == 0) {
- mInitialStartTime = r.startTime;
- }
- } else if (mInitialStartTime == 0) {
- mInitialStartTime = SystemClock.uptimeMillis();
- }
-
- if (app != null && app.thread != null) {
- try {
- 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.
- }
-
- startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent(), false);
- }
-
- private final ProcessRecord startProcessLocked(String processName,
+ final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
ProcessRecord app = getProcessRecordLocked(processName, info.uid);
@@ -2253,365 +1840,7 @@
}
}
- 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);
- }
- ActivityRecord prev = mResumedActivity;
- if (prev == null) {
- RuntimeException e = new RuntimeException();
- Slog.e(TAG, "Trying to pause when nothing is resumed", e);
- resumeTopActivityLocked(null);
- return;
- }
- if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
- mResumedActivity = null;
- mPausingActivity = prev;
- mLastPausedActivity = prev;
- prev.state = ActivityState.PAUSING;
- prev.task.touchActiveTime();
-
- updateCpuStats();
-
- if (prev.app != null && prev.app.thread != null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
- try {
- EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
- System.identityHashCode(prev),
- prev.shortComponentName);
- prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
- prev.configChangeFlags);
- updateUsageStats(prev, false);
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- }
- } else {
- mPausingActivity = null;
- mLastPausedActivity = null;
- }
-
- // If we are not going to sleep, we want to ensure the device is
- // awake until the next activity is started.
- if (!mSleeping && !mShuttingDown) {
- mLaunchingActivity.acquire();
- if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
- // To be safe, don't allow the wake lock to be held for too long.
- Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
- mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
- }
- }
-
-
- 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
- // the screen is being turned off and the UI is sleeping, don't interrupt
- // key dispatch; the same activity will pick it up again on wakeup.
- if (!uiSleeping) {
- prev.pauseKeyDispatchingLocked();
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
- }
-
- // Schedule a pause timeout in case the app doesn't respond.
- // We don't give it much time because this directly impacts the
- // responsiveness seen by the user.
- Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
- msg.obj = prev;
- mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
- if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
- } else {
- // 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);
- }
- }
-
- 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);
- prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
- } else if (prev.app != null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
- if (prev.waitingVisible) {
- prev.waitingVisible = false;
- mWaitingVisibleActivities.remove(prev);
- if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
- TAG, "Complete pause, no longer waiting: " + prev);
- }
- if (prev.configDestroy) {
- // The previous is being paused because the configuration
- // is changing, which means it is actually stopping...
- // To juggle the fact that we are also starting a new
- // instance right now, we need to first completely stop
- // the current instance before starting the new one.
- if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
- destroyActivityLocked(prev, true);
- } else {
- mStoppingActivities.add(prev);
- if (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.
- if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
- }
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
- prev = null;
- }
- mPausingActivity = null;
- }
-
- if (!mSleeping && !mShuttingDown) {
- resumeTopActivityLocked(prev);
- } else {
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
- }
- if (mShuttingDown) {
- notifyAll();
- }
- }
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
- }
-
- if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) {
- long diff = 0;
- synchronized (mProcessStatsThread) {
- diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume;
- }
- if (diff > 0) {
- BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
- synchronized (bsi) {
- BatteryStatsImpl.Uid.Proc ps =
- bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
- prev.info.packageName);
- if (ps != null) {
- ps.addForegroundTimeLocked(diff);
- }
- }
- }
- }
- prev.cpuTimeAtResume = 0; // reset it
- }
-
- /**
- * 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),
- * this function updates the rest of our state to match that fact.
- */
- private final void completeResumeLocked(ActivityRecord next) {
- next.idle = false;
- next.results = null;
- next.newIntents = null;
-
- // schedule an idle timeout in case the app doesn't do it for us.
- Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
- msg.obj = next;
- mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
-
- if (false) {
- // The activity was never told to pause, so just keep
- // things going as-is. To maintain our own state,
- // we need to emulate it coming back and saying it is
- // idle.
- msg = mHandler.obtainMessage(IDLE_NOW_MSG);
- msg.obj = next;
- mHandler.sendMessage(msg);
- }
-
- reportResumedActivityLocked(next);
-
- next.thumbnail = null;
- setFocusedActivityLocked(next);
- next.resumeKeyDispatchingLocked();
- ensureActivitiesVisibleLocked(null, 0);
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
-
- // Mark the point when the activity is resuming
- // TODO: To be more accurate, the mark should be before the onCreate,
- // not after the onResume. But for subsequent starts, onResume is fine.
- if (next.app != null) {
- synchronized (mProcessStatsThread) {
- next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid);
- }
- } else {
- next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
- }
- }
-
- /**
- * Make sure that all activities that need to be visible (that is, they
- * currently can be seen by the user) actually are.
- */
- private final void ensureActivitiesVisibleLocked(ActivityRecord top,
- ActivityRecord starting, String onlyThisProcess, int configChanges) {
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "ensureActivitiesVisible behind " + top
- + " configChanges=0x" + Integer.toHexString(configChanges));
-
- // 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 = (ActivityRecord)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.
- 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);
- mWindowManager.setAppVisibility(r, true);
- }
- if (r != starting) {
- startSpecificActivityLocked(r, false, false);
- }
- }
-
- } else if (r.visible) {
- // If this activity is already visible, then there is nothing
- // else to do here.
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Skipping: already visible at " + r);
- r.stopFreezingScreenLocked(false);
-
- } 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 {
- mWindowManager.setAppVisibility(r, true);
- r.app.thread.scheduleWindowVisibility(r, 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);
- }
- }
- }
-
- // Aggregate current change flags.
- configChanges |= r.configChangeFlags;
-
- 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;
- }
- }
-
- // 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 = (ActivityRecord)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 (DEBUG_VISBILITY) Slog.v(
- TAG, "Making invisible: " + r);
- r.visible = false;
- try {
- mWindowManager.setAppVisibility(r, 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);
- r.app.thread.scheduleWindowVisibility(r, false);
- }
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making hidden: "
- + r.intent.getComponent(), e);
- }
- } else {
- 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--;
- }
- }
-
- /**
- * Version of ensureActivitiesVisible that can easily be called anywhere.
- */
- private final void ensureActivitiesVisibleLocked(ActivityRecord starting,
- int configChanges) {
- ActivityRecord r = topRunningActivityLocked(null);
- if (r != null) {
- ensureActivitiesVisibleLocked(r, starting, null, configChanges);
- }
- }
-
- private void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) {
+ void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) {
if (resumed) {
mUsageStatsService.noteResumeComponent(resumedComponent.realActivity);
} else {
@@ -2619,7 +1848,7 @@
}
}
- private boolean startHomeActivityLocked() {
+ boolean startHomeActivityLocked() {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -2646,7 +1875,7 @@
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivityLocked(null, intent, null, null, 0, aInfo,
+ mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
null, null, 0, 0, 0, false, false);
}
}
@@ -2658,7 +1887,7 @@
/**
* Starts the "new version setup screen" if appropriate.
*/
- private void startSetupActivityLocked() {
+ void startSetupActivityLocked() {
// Only do this once per boot.
if (mCheckedForSetup) {
return;
@@ -2702,14 +1931,14 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
- startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
+ mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
null, null, 0, 0, 0, false, false);
}
}
}
}
- private void reportResumedActivityLocked(ActivityRecord r) {
+ void reportResumedActivityLocked(ActivityRecord r) {
//Slog.i(TAG, "**** REPORT RESUME: " + r);
final int identHash = System.identityHashCode(r);
@@ -2728,1278 +1957,27 @@
}
mWatchers.finishBroadcast();
}
-
- /**
- * Ensure that the top activity in the stack is resumed.
- *
- * @param prev The previously resumed activity, for when in the process
- * of pausing; can be null to call from elsewhere.
- *
- * @return Returns true if something is being resumed, or false if
- * nothing happened.
- */
- private final boolean resumeTopActivityLocked(ActivityRecord prev) {
- // 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;
-
- if (next == null) {
- // There are no more activities! Let's just start up the
- // Launcher...
- return startHomeActivityLocked();
- }
-
- next.delayedResume = false;
-
- // If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return false;
- }
-
- // If we are sleeping, and there is no resumed activity, and the top
- // activity is paused, well that is the state we want.
- if ((mSleeping || mShuttingDown)
- && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return false;
- }
-
- // The activity may be waiting for stop, but that is no longer
- // appropriate for it.
- mStoppingActivities.remove(next);
- mWaitingVisibleActivities.remove(next);
-
- if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
-
- // If we are currently pausing an activity, then don't do anything
- // until that is done.
- if (mPausingActivity != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
- return false;
- }
-
- // We need to start pausing the current activity so the top one
- // can be resumed...
- if (mResumedActivity != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
- startPausingLocked(userLeaving, false);
- return true;
- }
-
- if (prev != null && prev != next) {
- if (!prev.waitingVisible && next != null && !next.nowVisible) {
- prev.waitingVisible = true;
- mWaitingVisibleActivities.add(prev);
- if (DEBUG_SWITCH) Slog.v(
- TAG, "Resuming top, waiting visible to hide: " + prev);
- } else {
- // The next activity is already visible, so hide the previous
- // activity's windows right now so we can show the new one ASAP.
- // We only do this if the previous is finishing, which should mean
- // it is on top of the one being resumed so hiding it quickly
- // is good. Otherwise, we want to do the normal route of allowing
- // the resumed activity to be shown so we can decide if the
- // previous should actually be hidden depending on whether the
- // new one is found to be full-screen or not.
- if (prev.finishing) {
- mWindowManager.setAppVisibility(prev, false);
- if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
- + prev + ", waitingVisible="
- + (prev != null ? prev.waitingVisible : null)
- + ", nowVisible=" + next.nowVisible);
- } else {
- if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
- + prev + ", waitingVisible="
- + (prev != null ? prev.waitingVisible : null)
- + ", nowVisible=" + next.nowVisible);
- }
- }
- }
-
- // We are starting up the next activity, so tell the window manager
- // that the previous one will be hidden soon. This way it can know
- // to ignore it when computing the desired screen orientation.
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare close transition: prev=" + prev);
- if (mNoAnimActivities.contains(prev)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
- : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
- }
- mWindowManager.setAppWillBeHidden(prev);
- mWindowManager.setAppVisibility(prev, false);
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: prev=" + prev);
- if (mNoAnimActivities.contains(next)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
- : WindowManagerPolicy.TRANSIT_TASK_OPEN);
- }
- }
- if (false) {
- mWindowManager.setAppWillBeHidden(prev);
- mWindowManager.setAppVisibility(prev, false);
- }
- } else if (mHistory.size() > 1) {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: no previous");
- if (mNoAnimActivities.contains(next)) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
- }
- }
-
- if (next.app != null && next.app.thread != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
-
- // This activity is now becoming visible.
- mWindowManager.setAppVisibility(next, true);
-
- ActivityRecord lastResumedActivity = mResumedActivity;
- ActivityState lastState = next.state;
-
- updateCpuStats();
-
- next.state = ActivityState.RESUMED;
- mResumedActivity = next;
- next.task.touchActiveTime();
- updateLruProcessLocked(next.app, true, true);
- updateLRUListLocked(next);
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean updated;
- synchronized (this) {
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next : null);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- updated = updateConfigurationLocked(config, next);
- }
- if (!updated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivityLocked(null);
- if (DEBUG_SWITCH) Slog.i(TAG,
- "Activity config changed during resume: " + next
- + ", new next: " + nextNext);
- if (nextNext != next) {
- // Do over!
- mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
- }
- setFocusedActivityLocked(next);
- ensureActivitiesVisibleLocked(null, 0);
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- return true;
- }
-
- try {
- // Deliver all pending results.
- ArrayList a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Delivering results to " + next
- + ": " + a);
- next.app.thread.scheduleSendResult(next, a);
- }
- }
-
- if (next.newIntents != null) {
- next.app.thread.scheduleNewIntent(next.newIntents, next);
- }
-
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
- System.identityHashCode(next),
- next.task.taskId, next.shortComponentName);
-
- next.app.thread.scheduleResumeActivity(next,
- isNextTransitionForward());
-
- pauseIfSleepingLocked();
-
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- next.state = lastState;
- mResumedActivity = lastResumedActivity;
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_ICON) {
- mWindowManager.setAppStartingWindow(
- next, next.packageName, next.theme,
- next.nonLocalizedLabel,
- next.labelRes, next.icon, null, true);
- }
- }
- startSpecificActivityLocked(next, true, false);
- return true;
- }
-
- // From this point on, if something goes wrong there is no way
- // to recover the activity.
- try {
- next.visible = true;
- completeResumeLocked(next);
- } catch (Exception e) {
- // If any exception gets thrown, toss away this
- // activity and try the next one.
- Slog.w(TAG, "Exception thrown during resume of " + next, e);
- requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
- "resume-exception");
- return true;
- }
-
- // Didn't need to use the icicle, and it is now out of date.
- next.icicle = null;
- next.haveState = false;
- next.stopped = false;
-
- } else {
- // Whoops, need to restart this activity!
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_ICON) {
- mWindowManager.setAppStartingWindow(
- next, next.packageName, next.theme,
- next.nonLocalizedLabel,
- next.labelRes, next.icon, null, true);
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
- }
- startSpecificActivityLocked(next, true, true);
- }
-
- return true;
- }
-
- private final void startActivityLocked(ActivityRecord r, boolean newTask,
- boolean doResume) {
- final int NH = mHistory.size();
-
- int addPos = -1;
-
- if (!newTask) {
- // If starting in an existing task, find where that is...
- ActivityRecord next = null;
- boolean startIt = true;
- for (int i = NH-1; i >= 0; i--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(i);
- if (p.finishing) {
- continue;
- }
- if (p.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) {
- mHistory.add(addPos, r);
- r.inHistory = true;
- r.task.numActivities++;
- mWindowManager.addAppToken(addPos, r, r.task.taskId,
- r.info.screenOrientation, r.fullscreen);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- return;
- }
- break;
- }
- if (p.fullscreen) {
- startIt = false;
- }
- next = p;
- }
- }
-
- // Place a new activity at top of stack, so it is next to interact
- // with the user.
- if (addPos < 0) {
- addPos = mHistory.size();
- }
-
- // 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");
- }
-
- // Slot the activity into the history stack and proceed
- mHistory.add(addPos, r);
- r.inHistory = true;
- r.frontOfTask = newTask;
- r.task.numActivities++;
- if (NH > 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.
- boolean showStartingIcon = newTask;
- ProcessRecord proc = r.app;
- if (proc == null) {
- proc = mProcessNames.get(r.processName, r.info.applicationInfo.uid);
- }
- if (proc == null || proc.thread == null) {
- showStartingIcon = true;
- }
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare open transition: starting " + r);
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- mNoAnimActivities.add(r);
- } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_OPEN);
- mNoAnimActivities.remove(r);
- } else {
- mWindowManager.prepareAppTransition(newTask
- ? WindowManagerPolicy.TRANSIT_TASK_OPEN
- : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
- mNoAnimActivities.remove(r);
- }
- mWindowManager.addAppToken(
- addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
- boolean doShow = true;
- if (newTask) {
- // Even though this activity is starting fresh, we still need
- // to reset it to make sure we apply affinities to move any
- // existing activities from other tasks in to it.
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((r.intent.getFlags()
- &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- resetTaskIfNeededLocked(r, r);
- doShow = topRunningNonDelayedActivityLocked(null) == r;
- }
- }
- if (SHOW_APP_STARTING_ICON && doShow) {
- // Figure out if we are transitioning from another activity that is
- // "has the same starting icon" as the next one. This allows the
- // window manager to keep the previous window it had previously
- // created, if it still had one.
- ActivityRecord prev = mResumedActivity;
- if (prev != null) {
- // We don't want to reuse the previous starting preview if:
- // (1) The current activity is in a different task.
- if (prev.task != r.task) prev = null;
- // (2) The current activity is already displayed.
- else if (prev.nowVisible) prev = null;
- }
- mWindowManager.setAppStartingWindow(
- r, r.packageName, r.theme, r.nonLocalizedLabel,
- r.labelRes, r.icon, prev, showStartingIcon);
- }
- } else {
- // If this is the first activity, don't do any fancy animations,
- // because there is nothing for it to animate on top of.
- mWindowManager.addAppToken(addPos, r, r.task.taskId,
- r.info.screenOrientation, r.fullscreen);
- }
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- }
-
- /**
- * 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 continue to be used,
- * or null if none was found.
- */
- private final ActivityRecord performClearTaskLocked(int taskId,
- ActivityRecord newR, int launchFlags, boolean doClear) {
- int i = mHistory.size();
-
- // First find the requested task.
- while (i > 0) {
- i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (r.task.taskId == taskId) {
- i++;
- break;
- }
- }
-
- // Now clear it.
- while (i > 0) {
- i--;
- ActivityRecord r = (ActivityRecord)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;
- if (doClear) {
- while (i < (mHistory.size()-1)) {
- i++;
- r = (ActivityRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- 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);
- if (index >= 0) {
- finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
- null, "clear");
- }
- return null;
- }
- }
-
- return ret;
- }
- }
-
- return 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.
- */
- private final int findActivityInHistoryLocked(ActivityRecord r, int task) {
- int i = mHistory.size();
- while (i > 0) {
- i--;
- ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
- 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 = (ActivityRecord)mHistory.remove(where);
- int top = mHistory.size();
- ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
- mHistory.add(top, newTop);
- oldTop.frontOfTask = false;
- newTop.frontOfTask = true;
- return newTop;
- }
-
- /**
- * Deliver a new Intent to an existing activity, so that its onNewIntent()
- * method will be called at the proper time.
- */
- private final void deliverNewIntentLocked(ActivityRecord r, Intent intent) {
- boolean sent = false;
- if (r.state == ActivityState.RESUMED
- && r.app != null && r.app.thread != null) {
- try {
- ArrayList<Intent> ar = new ArrayList<Intent>();
- ar.add(new Intent(intent));
- r.app.thread.scheduleNewIntent(ar, r);
- sent = true;
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending new intent to " + r, e);
- }
- }
- if (!sent) {
- r.addNewIntentLocked(new Intent(intent));
- }
- }
-
- private final void logStartActivity(int tag, ActivityRecord r,
- TaskRecord task) {
- EventLog.writeEvent(tag,
- System.identityHashCode(r), task.taskId,
- r.shortComponentName, r.intent.getAction(),
- r.intent.getType(), r.intent.getDataString(),
- r.intent.getFlags());
- }
-
- private final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType,
- Uri[] grantedUriPermissions,
- int grantedMode, ActivityInfo aInfo, IBinder resultTo,
- String resultWho, int requestCode,
- int callingPid, int callingUid, boolean onlyIfNeeded,
- boolean componentSpecified) {
- Slog.i(TAG, "Starting activity: " + intent);
-
- ActivityRecord sourceRecord = null;
- ActivityRecord resultRecord = null;
- if (resultTo != null) {
- int index = indexOfTokenLocked(resultTo);
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Sending result to " + resultTo + " (index " + index + ")");
- if (index >= 0) {
- sourceRecord = (ActivityRecord)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) {
- return 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);
- }
- }
-
- int err = START_SUCCESS;
-
- if (intent.getComponent() == null) {
- // We couldn't find a class that can handle the given Intent.
- // That's the end of that!
- err = START_INTENT_NOT_RESOLVED;
- }
-
- if (err == START_SUCCESS && aInfo == null) {
- // We couldn't find the specific class specified in the Intent.
- // Also the end of the line.
- err = START_CLASS_NOT_FOUND;
- }
-
- ProcessRecord callerApp = null;
- if (err == START_SUCCESS && caller != null) {
- callerApp = 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 = START_PERMISSION_DENIED;
- }
- }
-
- if (err != START_SUCCESS) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- return err;
- }
-
- final int perm = checkComponentPermission(aInfo.permission, callingPid,
- callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- String msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + aInfo.permission;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- if (mController != null) {
- boolean abort = false;
- 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 = !mController.activityStarting(watchIntent,
- aInfo.applicationInfo.packageName);
- } catch (RemoteException e) {
- mController = null;
- }
-
- 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.
- return START_SUCCESS;
- }
- }
-
- ActivityRecord r = new ActivityRecord(this, callerApp, callingUid,
- intent, resolvedType, aInfo, mConfiguration,
- resultRecord, resultWho, requestCode, componentSpecified);
-
- if (mResumedActivity == null
- || mResumedActivity.info.applicationInfo.uid != callingUid) {
- if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
- PendingActivityLaunch pal = new PendingActivityLaunch();
- pal.r = r;
- pal.sourceRecord = sourceRecord;
- pal.grantedUriPermissions = grantedUriPermissions;
- pal.grantedMode = grantedMode;
- pal.onlyIfNeeded = onlyIfNeeded;
- mPendingActivityLaunches.add(pal);
- return START_SWITCHES_CANCELED;
- }
- }
-
- if (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).
- mAppSwitchesAllowedTime = 0;
- } else {
- mDidAppSwitch = true;
- }
-
- doPendingActivityLaunchesLocked(false);
-
- return startActivityUncheckedLocked(r, sourceRecord,
- grantedUriPermissions, grantedMode, onlyIfNeeded, true);
- }
-
- private final void doPendingActivityLaunchesLocked(boolean doResume) {
+ final void doPendingActivityLaunchesLocked(boolean doResume) {
final int N = mPendingActivityLaunches.size();
if (N <= 0) {
return;
}
for (int i=0; i<N; i++) {
PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
- startActivityUncheckedLocked(pal.r, pal.sourceRecord,
+ mMainStack.startActivityUncheckedLocked(pal.r, pal.sourceRecord,
pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded,
doResume && i == (N-1));
}
mPendingActivityLaunches.clear();
}
- private final int startActivityUncheckedLocked(ActivityRecord r,
- ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
- int grantedMode, boolean onlyIfNeeded, boolean doResume) {
- 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 (onlyIfNeeded) {
- 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.
- onlyIfNeeded = false;
- }
- }
-
- if (grantedUriPermissions != null && callingUid > 0) {
- for (int i=0; i<grantedUriPermissions.length; i++) {
- grantUriPermissionLocked(callingUid, r.packageName,
- grantedUriPermissions[i], grantedMode, r);
- }
- }
-
- grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- intent, r);
-
- 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;
- }
-
- boolean addingToTask = false;
- 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.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.
- moveTaskToFrontLocked(taskTop.task, r);
- }
- }
- // 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 (onlyIfNeeded) {
- // 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);
- }
- return START_RETURN_INTENT_TO_CALLER;
- }
- 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, true);
- 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);
- deliverNewIntentLocked(top, 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
- && taskTop.realActivity.equals(r.realActivity)) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
- if (taskTop.frontOfTask) {
- taskTop.task.setIntent(r.intent, r.info);
- }
- deliverNewIntentLocked(taskTop, 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) {
- // 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);
- }
- return 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)) {
- 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);
- }
- if (onlyIfNeeded) {
- // 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 START_RETURN_INTENT_TO_CALLER;
- }
- deliverNewIntentLocked(top, r.intent);
- return START_DELIVERED_TO_TOP;
- }
- }
- }
- }
-
- } else {
- if (r.resultTo != null) {
- sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- }
- return START_CLASS_NOT_FOUND;
- }
-
- boolean newTask = false;
-
- // Should this be considered a new task?
- if (r.resultTo == null && !addingToTask
- && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // todo: should do better management of integers.
- mCurTask++;
- if (mCurTask <= 0) {
- mCurTask = 1;
- }
- r.task = new TaskRecord(mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new task " + r.task);
- newTask = true;
- addRecentTaskLocked(r.task);
-
- } 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, true);
- if (top != null) {
- logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- deliverNewIntentLocked(top, r.intent);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return 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);
- deliverNewIntentLocked(top, r.intent);
- if (doResume) {
- resumeTopActivityLocked(null);
- }
- return 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.task = sourceRecord.task;
- 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 ? (ActivityRecord)mHistory.get(N-1) : null;
- r.task = prev != null
- ? prev.task
- : new TaskRecord(mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new guessed " + r.task);
- }
- if (newTask) {
- EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
- }
- logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
- startActivityLocked(r, newTask, doResume);
- return START_SUCCESS;
- }
-
- void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
- long thisTime, long totalTime) {
- for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
- WaitResult w = mWaitingActivityLaunched.get(i);
- w.timeout = timeout;
- if (r != null) {
- w.who = new ComponentName(r.info.packageName, r.info.name);
- }
- w.thisTime = thisTime;
- w.totalTime = totalTime;
- }
- notify();
- }
-
- void reportActivityVisibleLocked(ActivityRecord r) {
- for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
- WaitResult w = mWaitingActivityVisible.get(i);
- w.timeout = false;
- if (r != null) {
- w.who = new ComponentName(r.info.packageName, r.info.name);
- }
- w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
- w.thisTime = w.totalTime;
- }
- notify();
- }
-
- private final int startActivityMayWait(IApplicationThread caller,
- Intent intent, String resolvedType, Uri[] grantedUriPermissions,
- int grantedMode, IBinder resultTo,
- String resultWho, int requestCode, boolean onlyIfNeeded,
- boolean debug, WaitResult outResult, Configuration config) {
- // 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;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | STOCK_PM_FLAGS);
- 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 (debug) {
- if (!aInfo.processName.equals("system")) {
- setDebugApp(aInfo.processName, true, false);
- }
- }
- }
-
- synchronized (this) {
- int callingPid;
- int callingUid;
- if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
- } else {
- callingPid = callingUid = -1;
- }
-
- mConfigWillChange = config != null && mConfiguration.diff(config) != 0;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Starting activity when config will change = " + mConfigWillChange);
-
- final long origId = Binder.clearCallingIdentity();
-
- if (aInfo != null &&
- (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 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 (mHeavyWeightProcess != null &&
- (mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
- !mHeavyWeightProcess.processName.equals(aInfo.processName))) {
- int realCallingPid = callingPid;
- int realCallingUid = callingUid;
- if (caller != null) {
- ProcessRecord callerApp = 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());
- return START_PERMISSION_DENIED;
- }
- }
-
- IIntentSender target = getIntentSenderLocked(
- IActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, null, null, 0, intent,
- resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
- | PendingIntent.FLAG_ONE_SHOT);
-
- 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 (mHeavyWeightProcess.activities.size() > 0) {
- ActivityRecord hist = 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
- | STOCK_PM_FLAGS);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
- }
- }
- }
-
- int res = startActivityLocked(caller, intent, resolvedType,
- grantedUriPermissions, grantedMode, aInfo,
- resultTo, resultWho, requestCode, callingPid, callingUid,
- onlyIfNeeded, componentSpecified);
-
- if (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.
- enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
- "updateConfiguration()");
- mConfigWillChange = false;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Updating to new configuration after starting activity.");
- updateConfigurationLocked(config, null);
- }
-
- Binder.restoreCallingIdentity(origId);
-
- if (outResult != null) {
- outResult.result = res;
- if (res == IActivityManager.START_SUCCESS) {
- mWaitingActivityLaunched.add(outResult);
- do {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- } else if (res == IActivityManager.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 {
- wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- }
- }
- }
-
- return res;
- }
- }
-
public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
- return startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, null);
}
@@ -4010,7 +1988,7 @@
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
WaitResult res = new WaitResult();
- startActivityMayWait(caller, intent, resolvedType,
+ mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, res, null);
return res;
@@ -4021,7 +1999,7 @@
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, config);
}
@@ -4045,8 +2023,8 @@
synchronized (this) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
- if (mResumedActivity != null
- && mResumedActivity.info.applicationInfo.uid ==
+ if (mMainStack.mResumedActivity != null
+ && mMainStack.mResumedActivity.info.applicationInfo.uid ==
Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
}
@@ -4064,11 +2042,11 @@
}
synchronized (this) {
- int index = indexOfTokenLocked(callingActivity);
+ int index = mMainStack.indexOfTokenLocked(callingActivity);
if (index < 0) {
return false;
}
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r.app == null || r.app.thread == null) {
// The caller is not running... d'oh!
return false;
@@ -4137,7 +2115,7 @@
final long origId = Binder.clearCallingIdentity();
// XXX we are not dealing with propagating grantedUriPermissions...
// those are not yet exposed to user code, so there is no need.
- int res = startActivityLocked(r.app.thread, intent,
+ int res = mMainStack.startActivityLocked(r.app.thread, intent,
r.resolvedType, null, 0, aInfo, resultTo, resultWho,
requestCode, -1, r.launchedFromUid, false, false);
Binder.restoreCallingIdentity(origId);
@@ -4189,13 +2167,13 @@
}
synchronized(this) {
- return startActivityLocked(null, intent, resolvedType,
+ return mMainStack.startActivityLocked(null, intent, resolvedType,
null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid,
onlyIfNeeded, componentSpecified);
}
}
- private final void addRecentTaskLocked(TaskRecord task) {
+ final void addRecentTaskLocked(TaskRecord task) {
// Remove any existing entries that are the same kind of task.
int N = mRecentTasks.size();
for (int i=0; i<N; i++) {
@@ -4221,11 +2199,11 @@
public void setRequestedOrientation(IBinder token,
int requestedOrientation) {
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
mWindowManager.setAppOrientation(r, requestedOrientation);
Configuration config = mWindowManager.updateOrientationFromAppTokens(
@@ -4234,7 +2212,7 @@
if (config != null) {
r.frozenBeforeDestroy = true;
if (!updateConfigurationLocked(config, r)) {
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
}
Binder.restoreCallingIdentity(origId);
@@ -4243,250 +2221,15 @@
public int getRequestedOrientation(IBinder token) {
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return mWindowManager.getAppOrientation(r);
}
}
- private final void stopActivityLocked(ActivityRecord r) {
- if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
- || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
- if (!r.finishing) {
- requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
- "no-history");
- }
- } else if (r.app != null && r.app.thread != null) {
- if (mFocusedActivity == r) {
- setFocusedActivityLocked(topRunningActivityLocked(null));
- }
- r.resumeKeyDispatchingLocked();
- try {
- r.stopped = false;
- r.state = ActivityState.STOPPING;
- if (DEBUG_VISBILITY) Slog.v(
- TAG, "Stopping visible=" + r.visible + " for " + r);
- if (!r.visible) {
- mWindowManager.setAppVisibility(r, false);
- }
- r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
- } catch (Exception e) {
- // Maybe just ignore exceptions here... if the process
- // has crashed, our death notification will clean things
- // up.
- Slog.w(TAG, "Exception thrown during pause", e);
- // Just in case, assume it to be stopped.
- r.stopped = true;
- r.state = ActivityState.STOPPED;
- if (r.configDestroy) {
- destroyActivityLocked(r, true);
- }
- }
- }
- }
-
- /**
- * @return Returns true if the activity is being finished, false if for
- * some reason it is being left as-is.
- */
- private final boolean requestFinishActivityLocked(IBinder token, int resultCode,
- Intent resultData, String reason) {
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Finishing activity: token=" + token
- + ", result=" + resultCode + ", data=" + resultData);
-
- int index = indexOfTokenLocked(token);
- if (index < 0) {
- return false;
- }
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
-
- // Is this the last activity left?
- boolean lastActivity = true;
- for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(i);
- if (!p.finishing && p != r) {
- lastActivity = false;
- break;
- }
- }
-
- // If this is the last activity, but it is the home activity, then
- // just don't finish it.
- if (lastActivity) {
- if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
- return false;
- }
- }
-
- finishActivityLocked(r, index, resultCode, resultData, reason);
- return true;
- }
-
- /**
- * @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.
- */
- private final boolean finishActivityLocked(ActivityRecord r, int index,
- int resultCode, Intent resultData, String reason) {
- if (r.finishing) {
- Slog.w(TAG, "Duplicate finish request for " + r);
- return false;
- }
-
- r.finishing = true;
- EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName, reason);
- r.task.numActivities--;
- if (index < (mHistory.size()-1)) {
- ActivityRecord next = (ActivityRecord)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);
- }
- }
- }
-
- r.pauseKeyDispatchingLocked();
- if (mFocusedActivity == r) {
- setFocusedActivityLocked(topRunningActivityLocked(null));
- }
-
- // send the result
- ActivityRecord resultTo = r.resultTo;
- if (resultTo != null) {
- if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
- + " who=" + r.resultWho + " req=" + r.requestCode
- + " res=" + resultCode + " data=" + resultData);
- if (r.info.applicationInfo.uid > 0) {
- grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
- r.packageName, resultData, r);
- }
- resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
- resultData);
- r.resultTo = null;
- }
- else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
-
- // Make sure this HistoryRecord is not holding on to other resources,
- // because clients have remote IPC references to this object so we
- // can't assume that will go away and want to avoid circular IPC refs.
- r.results = null;
- r.pendingResults = null;
- r.newIntents = null;
- r.icicle = null;
-
- if (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.
- mCancelledThumbnails.add(r);
- }
-
- if (mResumedActivity == r) {
- boolean endTask = index <= 0
- || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare close transition: finishing " + r);
- mWindowManager.prepareAppTransition(endTask
- ? WindowManagerPolicy.TRANSIT_TASK_CLOSE
- : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
-
- // Tell window manager to prepare for this one to be removed.
- mWindowManager.setAppVisibility(r, 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");
- startPausingLocked(false, false);
- }
-
- } else if (r.state != ActivityState.PAUSING) {
- // 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) == null;
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
- }
-
- return false;
- }
-
- private static final int FINISH_IMMEDIATELY = 0;
- private static final int FINISH_AFTER_PAUSE = 1;
- private static final int FINISH_AFTER_VISIBLE = 2;
-
- private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
- int mode) {
- final int index = indexOfTokenLocked(r);
- if (index < 0) {
- return null;
- }
-
- return finishCurrentActivityLocked(r, index, mode);
- }
-
- private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
- int index, int mode) {
- // 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 we already have a few activities waiting to stop,
- // then give up on things going idle and start clearing
- // them out.
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
- }
- r.state = ActivityState.STOPPING;
- updateOomAdjLocked();
- return r;
- }
-
- // make sure the record is cleaned out of other places.
- mStoppingActivities.remove(r);
- mWaitingVisibleActivities.remove(r);
- if (mResumedActivity == r) {
- mResumedActivity = null;
- }
- final ActivityState prevState = r.state;
- r.state = ActivityState.FINISHING;
-
- if (mode == FINISH_IMMEDIATELY
- || prevState == ActivityState.STOPPED
- || prevState == ActivityState.INITIALIZING) {
- // If this activity is already stopped, we can just finish
- // it right now.
- return destroyActivityLocked(r, true) ? 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);
- }
- return r;
- }
-
/**
* This is the internal entry point for handling Activity.finish().
*
@@ -4505,7 +2248,7 @@
synchronized(this) {
if (mController != null) {
// Find the first activity that is not finishing.
- ActivityRecord next = topRunningActivityLocked(token, 0);
+ ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
@@ -4521,7 +2264,7 @@
}
}
final long origId = Binder.clearCallingIdentity();
- boolean res = requestFinishActivityLocked(token, resultCode,
+ boolean res = mMainStack.requestFinishActivityLocked(token, resultCode,
resultData, "app-request");
Binder.restoreCallingIdentity(origId);
return res;
@@ -4549,9 +2292,9 @@
for (int i=0; i<activities.size(); i++) {
ActivityRecord r = activities.get(i);
if (!r.finishing) {
- int index = indexOfTokenLocked(r);
+ int index = mMainStack.indexOfTokenLocked(r);
if (index >= 0) {
- finishActivityLocked(r, index, Activity.RESULT_CANCELED,
+ mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED,
null, "finish-heavy");
}
}
@@ -4616,50 +2359,24 @@
}
}
- void sendActivityResultLocked(int callingUid, ActivityRecord r,
- String resultWho, int requestCode, int resultCode, Intent data) {
-
- if (callingUid > 0) {
- grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- data, r);
- }
-
- if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
- + " : who=" + resultWho + " req=" + requestCode
- + " res=" + resultCode + " data=" + data);
- if (mResumedActivity == r && r.app != null && r.app.thread != null) {
- try {
- ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
- list.add(new ResultInfo(resultWho, requestCode,
- resultCode, data));
- r.app.thread.scheduleSendResult(r, list);
- return;
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending result to " + r, e);
- }
- }
-
- r.addResultLocked(null, resultWho, requestCode, resultCode, data);
- }
-
public final void finishSubActivity(IBinder token, String resultWho,
int requestCode) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- ActivityRecord self = (ActivityRecord)mHistory.get(index);
+ ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
int i;
- for (i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.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,
+ mMainStack.finishActivityLocked(r, i,
Activity.RESULT_CANCELED, null, "request-sub");
}
}
@@ -4672,8 +2389,8 @@
public boolean willActivityBeVisible(IBinder token) {
synchronized(this) {
int i;
- for (i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r == token) {
return true;
}
@@ -4688,11 +2405,11 @@
public void overridePendingTransition(IBinder token, String packageName,
int enterAnim, int exitAnim) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- ActivityRecord self = (ActivityRecord)mHistory.get(index);
+ ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index);
final long origId = Binder.clearCallingIdentity();
@@ -4707,192 +2424,6 @@
}
/**
- * Perform clean-up of service connections in an activity record.
- */
- private final void cleanUpActivityServicesLocked(ActivityRecord r) {
- // Throw away any services that have been bound by this activity.
- if (r.connections != null) {
- Iterator<ConnectionRecord> it = r.connections.iterator();
- while (it.hasNext()) {
- ConnectionRecord c = it.next();
- removeConnectionLocked(c, null, r);
- }
- r.connections = null;
- }
- }
-
- /**
- * Perform the common clean-up of an activity record. This is called both
- * as part of destroyActivityLocked() (when destroying the client-side
- * representation) and cleaning things up as a result of its hosting
- * processing going away, in which case there is no remaining client-side
- * state to destroy so only the cleanup here is needed.
- */
- private final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) {
- if (mResumedActivity == r) {
- mResumedActivity = null;
- }
- if (mFocusedActivity == r) {
- mFocusedActivity = null;
- }
-
- r.configDestroy = false;
- r.frozenBeforeDestroy = false;
-
- // Make sure this record is no longer in the pending finishes list.
- // 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);
-
- // Remove any pending results.
- if (r.finishing && r.pendingResults != null) {
- for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
- PendingIntentRecord rec = apr.get();
- if (rec != null) {
- cancelIntentSenderLocked(rec, false);
- }
- }
- r.pendingResults = null;
- }
-
- if (cleanServices) {
- cleanUpActivityServicesLocked(r);
- }
-
- if (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.
- mCancelledThumbnails.add(r);
- }
-
- // Get rid of any pending idle timeouts.
- mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
- }
-
- private final void removeActivityFromHistoryLocked(ActivityRecord r) {
- if (r.state != ActivityState.DESTROYED) {
- mHistory.remove(r);
- r.inHistory = false;
- r.state = ActivityState.DESTROYED;
- mWindowManager.removeAppToken(r);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- cleanUpActivityServicesLocked(r);
- removeActivityUriPermissionsLocked(r);
- }
- }
-
- /**
- * Destroy the current CLIENT SIDE instance of an activity. This may be
- * called both when actually finishing an activity, or when performing
- * a configuration switch where we destroy the current client-side object
- * but then create a new client-side object for this same HistoryRecord.
- */
- private final boolean destroyActivityLocked(ActivityRecord r,
- boolean removeFromApp) {
- if (DEBUG_SWITCH) Slog.v(
- TAG, "Removing activity: token=" + r
- + ", app=" + (r.app != null ? r.app.processName : "(null)"));
- EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
- System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
-
- boolean removedFromHistory = false;
-
- cleanUpActivityLocked(r, false);
-
- final boolean hadApp = r.app != null;
-
- if (hadApp) {
- if (removeFromApp) {
- int idx = r.app.activities.indexOf(r);
- if (idx >= 0) {
- r.app.activities.remove(idx);
- }
- if (mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
- mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
- }
- if (r.persistent) {
- decPersistentCountLocked(r.app);
- }
- if (r.app.activities.size() == 0) {
- // No longer have activities, so update location in
- // LRU list.
- updateLruProcessLocked(r.app, true, false);
- }
- }
-
- boolean skipDestroy = false;
-
- try {
- if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
- r.app.thread.scheduleDestroyActivity(r, r.finishing,
- r.configChangeFlags);
- } catch (Exception e) {
- // We can just ignore exceptions here... if the process
- // has crashed, our death notification will clean things
- // up.
- //Slog.w(TAG, "Exception thrown during finish", e);
- if (r.finishing) {
- removeActivityFromHistoryLocked(r);
- removedFromHistory = true;
- skipDestroy = true;
- }
- }
-
- r.app = null;
- r.nowVisible = false;
-
- if (r.finishing && !skipDestroy) {
- r.state = ActivityState.DESTROYING;
- Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
- msg.obj = r;
- mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
- } else {
- r.state = ActivityState.DESTROYED;
- }
- } else {
- // remove this record from the history.
- if (r.finishing) {
- removeActivityFromHistoryLocked(r);
- removedFromHistory = true;
- } else {
- r.state = ActivityState.DESTROYED;
- }
- }
-
- r.configChangeFlags = 0;
-
- if (!mLRUActivities.remove(r) && hadApp) {
- Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
- }
-
- return removedFromHistory;
- }
-
- private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) {
- int i = list.size();
- if (localLOGV) Slog.v(
- TAG, "Removing app " + app + " from list " + list
- + " with " + i + " entries");
- while (i > 0) {
- i--;
- ActivityRecord r = (ActivityRecord)list.get(i);
- if (localLOGV) Slog.v(
- TAG, "Record #" + i + " " + r + ": app=" + r.app);
- if (r.app == app) {
- if (localLOGV) Slog.v(TAG, "Removing this entry!");
- list.remove(i);
- }
- }
- }
-
- /**
* Main function for removing an existing process from the activity manager
* as a result of that process going away. Clears out all connections
* to the process.
@@ -4905,30 +2436,27 @@
}
// Just in case...
- if (mPausingActivity != null && mPausingActivity.app == app) {
- if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + mPausingActivity);
- mPausingActivity = null;
+ if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity);
+ mMainStack.mPausingActivity = null;
}
- if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
- mLastPausedActivity = null;
+ if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) {
+ mMainStack.mLastPausedActivity = null;
}
// Remove this application's activities from active lists.
- removeHistoryRecordsForAppLocked(mLRUActivities, app);
- removeHistoryRecordsForAppLocked(mStoppingActivities, app);
- removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
- removeHistoryRecordsForAppLocked(mFinishingActivities, app);
+ mMainStack.removeHistoryRecordsForAppLocked(app);
boolean atTop = true;
boolean hasVisibleActivities = false;
// Clean out the history list.
- int i = mHistory.size();
+ int i = mMainStack.mHistory.size();
if (localLOGV) Slog.v(
TAG, "Removing app " + app + " from history with " + i + " entries");
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (localLOGV) Slog.v(
TAG, "Record #" + i + " " + r + ": app=" + r.app);
if (r.app == app) {
@@ -4936,14 +2464,14 @@
if (localLOGV) Slog.v(
TAG, "Removing this entry! frozen=" + r.haveState
+ " finishing=" + r.finishing);
- mHistory.remove(i);
+ mMainStack.mHistory.remove(i);
r.inHistory = false;
mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
+ mWindowManager.validateAppTokens(mMainStack.mHistory);
}
- removeActivityUriPermissionsLocked(r);
+ r.removeUriPermissionsLocked();
} else {
// We have the current state for this activity, so
@@ -4960,7 +2488,7 @@
}
}
- cleanUpActivityLocked(r, true);
+ r.stack.cleanUpActivityLocked(r, true);
r.state = ActivityState.STOPPED;
}
atTop = false;
@@ -4977,14 +2505,14 @@
}
if (!restarting) {
- if (!resumeTopActivityLocked(null)) {
+ 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) {
- ensureActivitiesVisibleLocked(null, 0);
+ mMainStack.ensureActivitiesVisibleLocked(null, 0);
}
}
}
@@ -5003,7 +2531,7 @@
return -1;
}
- private final ProcessRecord getRecordForAppLocked(
+ final ProcessRecord getRecordForAppLocked(
IApplicationThread thread) {
if (thread == null) {
return null;
@@ -5013,7 +2541,7 @@
return appIndex >= 0 ? mLruProcesses.get(appIndex) : null;
}
- private final void appDiedLocked(ProcessRecord app, int pid,
+ final void appDiedLocked(ProcessRecord app, int pid,
IApplicationThread thread) {
mProcDeaths[0]++;
@@ -5253,8 +2781,7 @@
}
}
- private final void decPersistentCountLocked(ProcessRecord app)
- {
+ final void decPersistentCountLocked(ProcessRecord app) {
app.persistentActivities--;
if (app.persistentActivities > 0) {
// Still more of 'em...
@@ -5282,11 +2809,11 @@
}
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
ProcessRecord app = r.app;
if (localLOGV) Slog.v(
@@ -5497,10 +3024,10 @@
mWindowManager.closeSystemDialogs(reason);
- for (i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ for (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) {
- finishActivityLocked(r, i,
+ r.stack.finishActivityLocked(r, i,
Activity.RESULT_CANCELED, null, "close-sys");
}
}
@@ -5621,8 +3148,8 @@
boolean didSomething = killPackageProcessesLocked(name, uid, -100,
callerWillRestart, doit);
- for (i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ for (i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.packageName.equals(name)) {
if (!doit) {
return true;
@@ -5633,7 +3160,7 @@
r.app.removed = true;
}
r.app = null;
- finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall");
+ r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall");
}
}
@@ -5665,7 +3192,7 @@
ac.removePackage(name);
}
}
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
return didSomething;
@@ -5894,12 +3421,12 @@
boolean didSomething = false;
// See if the top visible activity is waiting to run in this process...
- ActivityRecord hr = topRunningActivityLocked(null);
+ ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
if (hr != null && normalMode) {
if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
- if (realStartActivityLocked(hr, app, true, true)) {
+ if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (Exception e) {
@@ -5908,7 +3435,7 @@
badApp = true;
}
} else {
- ensureActivitiesVisibleLocked(hr, null, processName, 0);
+ mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
}
}
@@ -5992,195 +3519,16 @@
public final void activityIdle(IBinder token, Configuration config) {
final long origId = Binder.clearCallingIdentity();
- activityIdleInternal(token, false, config);
+ mMainStack.activityIdleInternal(token, false, config);
Binder.restoreCallingIdentity(origId);
}
- 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);
- mWindowManager.setAppVisibility(s, false);
- }
- }
- if (!s.waitingVisible && 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;
- }
-
void enableScreenAfterBoot() {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
mWindowManager.enableScreenAfterBoot();
}
- final void activityIdleInternal(IBinder token, boolean fromTimeout,
- Configuration config) {
- if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
-
- ArrayList<ActivityRecord> stops = null;
- ArrayList<ActivityRecord> finishes = null;
- ArrayList<ActivityRecord> thumbnails = null;
- int NS = 0;
- int NF = 0;
- int NT = 0;
- IApplicationThread sendThumbnail = null;
- boolean booting = false;
- boolean enableScreen = false;
-
- synchronized (this) {
- if (token != null) {
- mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
- }
-
- // Get the activity record.
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
-
- 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();
- }
-
- // We are now idle. If someone is waiting for a thumbnail from
- // us, we can now deliver.
- r.idle = true;
- scheduleAppGcsLocked();
- 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 (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r);
- ensureActivitiesVisibleLocked(null, 0);
-
- //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (!mBooted && !fromTimeout) {
- 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=mCancelledThumbnails.size()) > 0) {
- thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails);
- mCancelledThumbnails.clear();
- }
-
- booting = mBooting;
- mBooting = false;
- }
-
- int i;
-
- // Send thumbnail if requested.
- if (sendThumbnail != null) {
- try {
- sendThumbnail.requestThumbnail(token);
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown when requesting thumbnail", e);
- sendPendingThumbnail(null, token, null, null, true);
- }
- }
-
- // 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 (this) {
- if (r.finishing) {
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);
- } else {
- stopActivityLocked(r);
- }
- }
- }
-
- // Finish any activities that are scheduled to do so but have been
- // waiting for the next one to start.
- for (i=0; i<NF; i++) {
- ActivityRecord r = (ActivityRecord)finishes.get(i);
- synchronized (this) {
- destroyActivityLocked(r, true);
- }
- }
-
- // Report back to any thumbnail receivers.
- for (i=0; i<NT; i++) {
- ActivityRecord r = (ActivityRecord)thumbnails.get(i);
- sendPendingThumbnail(r, null, null, null, true);
- }
-
- if (booting) {
- finishBooting();
- }
-
- trimApplications();
- //dump();
- //mWindowManager.dump();
-
- if (enableScreen) {
- enableScreenAfterBoot();
- }
- }
-
final void finishBooting() {
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
@@ -6249,39 +3597,10 @@
}
final long origId = Binder.clearCallingIdentity();
- activityPaused(token, icicle, false);
+ mMainStack.activityPaused(token, icicle, false);
Binder.restoreCallingIdentity(origId);
}
- final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
- if (DEBUG_PAUSE) Slog.v(
- TAG, "Activity paused: token=" + token + ", icicle=" + icicle
- + ", timeout=" + timeout);
-
- ActivityRecord r = null;
-
- synchronized (this) {
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- r = (ActivityRecord)mHistory.get(index);
- if (!timeout) {
- r.icicle = icicle;
- r.haveState = true;
- }
- mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- if (mPausingActivity == r) {
- r.state = ActivityState.PAUSED;
- completePauseLocked();
- } else {
- EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
- System.identityHashCode(r), r.shortComponentName,
- mPausingActivity != null
- ? mPausingActivity.shortComponentName : "(none)");
- }
- }
- }
- }
-
public final void activityStopped(IBinder token, Bitmap thumbnail,
CharSequence description) {
if (localLOGV) Slog.v(
@@ -6292,17 +3611,17 @@
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- r = (ActivityRecord)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
r.thumbnail = thumbnail;
r.description = description;
r.stopped = true;
r.state = ActivityState.STOPPED;
if (!r.finishing) {
if (r.configDestroy) {
- destroyActivityLocked(r, true);
- resumeTopActivityLocked(null);
+ r.stack.destroyActivityLocked(r, true);
+ r.stack.resumeTopActivityLocked(null);
}
}
}
@@ -6319,19 +3638,7 @@
public final void activityDestroyed(IBinder token) {
if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token);
- synchronized (this) {
- mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
-
- int index = indexOfTokenLocked(token);
- if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
- if (r.state == ActivityState.DESTROYING) {
- final long origId = Binder.clearCallingIdentity();
- removeActivityFromHistoryLocked(r);
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
+ mMainStack.activityDestroyed(token);
}
public String getCallingPackage(IBinder token) {
@@ -6349,9 +3656,9 @@
}
private ActivityRecord getCallingRecordLocked(IBinder token) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r != null) {
return r.resultTo;
}
@@ -6361,9 +3668,9 @@
public ComponentName getActivityClassForToken(IBinder token) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return r.intent.getComponent();
}
return null;
@@ -6372,9 +3679,9 @@
public String getPackageForToken(IBinder token) {
synchronized(this) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return r.packageName;
}
return null;
@@ -6428,11 +3735,11 @@
int requestCode, Intent intent, String resolvedType, int flags) {
ActivityRecord activity = null;
if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return null;
}
- activity = (ActivityRecord)mHistory.get(index);
+ activity = (ActivityRecord)mMainStack.mHistory.get(index);
if (activity.finishing) {
return null;
}
@@ -6749,7 +4056,7 @@
}
}
- private void grantUriPermissionLocked(int callingUid,
+ void grantUriPermissionLocked(int callingUid,
String targetPkg, Uri uri, int modeFlags, ActivityRecord activity) {
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
@@ -6878,7 +4185,7 @@
}
}
- private void grantUriPermissionFromIntentLocked(int callingUid,
+ void grantUriPermissionFromIntentLocked(int callingUid,
String targetPkg, Intent intent, ActivityRecord activity) {
if (intent == null) {
return;
@@ -6914,7 +4221,7 @@
}
}
- private void removeUriPermissionIfNeededLocked(UriPermission perm) {
+ void removeUriPermissionIfNeededLocked(UriPermission perm) {
if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION
|Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) {
HashMap<Uri, UriPermission> perms
@@ -6930,29 +4237,6 @@
}
}
- private void removeActivityUriPermissionsLocked(ActivityRecord activity) {
- if (activity.readUriPermissions != null) {
- for (UriPermission perm : activity.readUriPermissions) {
- perm.readActivities.remove(activity);
- if (perm.readActivities.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
- removeUriPermissionIfNeededLocked(perm);
- }
- }
- }
- if (activity.writeUriPermissions != null) {
- for (UriPermission perm : activity.writeUriPermissions) {
- perm.writeActivities.remove(activity);
- if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
- &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
- perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- removeUriPermissionIfNeededLocked(perm);
- }
- }
- }
- }
-
private void revokeUriPermissionLocked(int callingUid, Uri uri,
int modeFlags) {
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -7137,9 +4421,9 @@
throw new SecurityException(msg);
}
- int pos = mHistory.size()-1;
+ int pos = mMainStack.mHistory.size()-1;
ActivityRecord next =
- pos >= 0 ? (ActivityRecord)mHistory.get(pos) : null;
+ pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
ActivityRecord top = null;
CharSequence topDescription = null;
TaskRecord curTask = null;
@@ -7148,7 +4432,7 @@
while (pos >= 0 && maxNum > 0) {
final ActivityRecord r = next;
pos--;
- next = pos >= 0 ? (ActivityRecord)mHistory.get(pos) : null;
+ next = pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
// Initialize state for next task if needed.
if (top == null ||
@@ -7297,12 +4581,12 @@
private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
int j;
- TaskRecord startTask = ((ActivityRecord)mHistory.get(startIndex)).task;
+ TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task;
TaskRecord jt = startTask;
// First look backwards
for (j=startIndex-1; j>=0; j--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(j);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
if (r.task != jt) {
jt = r.task;
if (affinity.equals(jt.affinity)) {
@@ -7312,10 +4596,10 @@
}
// Now look forwards
- final int N = mHistory.size();
+ final int N = mMainStack.mHistory.size();
jt = startTask;
for (j=startIndex+1; j<N; j++) {
- ActivityRecord r = (ActivityRecord)mHistory.get(j);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j);
if (r.task != jt) {
if (affinity.equals(jt.affinity)) {
return j;
@@ -7325,7 +4609,7 @@
}
// Might it be at the top?
- if (affinity.equals(((ActivityRecord)mHistory.get(N-1)).task.affinity)) {
+ if (affinity.equals(((ActivityRecord)mMainStack.mHistory.get(N-1)).task.affinity)) {
return N-1;
}
@@ -7333,293 +4617,6 @@
}
/**
- * 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 (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;
- for (int i=mHistory.size()-1; i>=-1; i--) {
- ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
-
- if (below != null && below.finishing) {
- continue;
- }
- 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;
- }
-
- final int flags = target.info.flags;
-
- final boolean finishOnTaskLaunch =
- (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;
- }
- 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 = (ActivityRecord)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.task = p.task;
- if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
- + " out to bottom task " + p.task);
- } else {
- mCurTask++;
- if (mCurTask <= 0) {
- mCurTask = 1;
- }
- target.task = new TaskRecord(mCurTask, target.info, null,
- (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- target.task.affinityIntent = target.intent;
- if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
- + " out to new task " + target.task);
- }
- mWindowManager.setAppGroupId(target, task.taskId);
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- int dstPos = 0;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
- + " out to target's task " + target.task);
- task.numActivities--;
- p.task = target.task;
- target.task.numActivities++;
- mHistory.remove(srcPos);
- mHistory.add(dstPos, p);
- mWindowManager.moveAppToken(dstPos, p);
- mWindowManager.setAppGroupId(p, p.task.taskId);
- dstPos++;
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- i++;
- }
- if (taskTop == p) {
- taskTop = below;
- }
- if (taskTopI == replyChainEnd) {
- taskTopI = -1;
- }
- replyChainEnd = -1;
- addRecentTaskLocked(target.task);
- } 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() &&
- ((ActivityRecord)mHistory.get(
- replyChainEnd)).task == task) {
- replyChainEnd++;
- }
- replyChainEnd--;
- } else if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- ActivityRecord p = null;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset")) {
- replyChainEnd--;
- srcPos--;
- }
- }
- if (taskTop == p) {
- taskTop = below;
- }
- if (taskTopI == replyChainEnd) {
- taskTopI = -1;
- }
- 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;
- }
- } else {
- // Reached the bottom of the task -- any reply chain
- // should be left as-is.
- replyChainEnd = -1;
- }
-
- } else 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.
- // 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 (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
- // 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
- // with an affinity to it... this is really not a normal
- // case, and we will need to later pull that task to the front
- // and usually at that point we will do the reset and pick
- // up those remaining activities. (This only happens if
- // 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;
- for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (finishActivityLocked(p, srcPos,
- Activity.RESULT_CANCELED, null, "reset")) {
- taskTopI--;
- lastReparentPos--;
- replyChainEnd--;
- srcPos--;
- }
- }
- replyChainEnd = -1;
- } else {
- if (replyChainEnd < 0) {
- replyChainEnd = targetI;
- }
- for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
- if (p.finishing) {
- continue;
- }
- if (lastReparentPos < 0) {
- lastReparentPos = taskTopI;
- taskTop = p;
- } else {
- lastReparentPos--;
- }
- mHistory.remove(srcPos);
- p.task.numActivities--;
- p.task = task;
- mHistory.add(lastReparentPos, p);
- if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
- + " in to resetting task " + task);
- task.numActivities++;
- mWindowManager.moveAppToken(lastReparentPos, p);
- mWindowManager.setAppGroupId(p, p.task.taskId);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
- }
- 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 = (ActivityRecord)mHistory.get(j);
- if (p.finishing) {
- continue;
- }
- if (p.intent.getComponent().equals(target.intent.getComponent())) {
- if (finishActivityLocked(p, j,
- Activity.RESULT_CANCELED, null, "replace")) {
- taskTopI--;
- lastReparentPos--;
- }
- }
- }
- }
- }
- }
-
- target = below;
- targetI = i;
- }
-
- return taskTop;
- }
-
- /**
* TODO: Add mController hook
*/
public void moveTaskToFront(int task) {
@@ -7637,14 +4634,14 @@
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (tr.taskId == task) {
- moveTaskToFrontLocked(tr, null);
+ mMainStack.moveTaskToFrontLocked(tr, null);
return;
}
}
- for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+ for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+ ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
if (hr.task.taskId == task) {
- moveTaskToFrontLocked(hr.task, null);
+ mMainStack.moveTaskToFrontLocked(hr.task, null);
return;
}
}
@@ -7654,84 +4651,20 @@
}
}
- private final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
- if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
-
- final int task = tr.taskId;
- int top = mHistory.size()-1;
-
- if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
- // nothing to do!
- return;
- }
-
- ArrayList moved = new ArrayList();
-
- // 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 = (ActivityRecord)mHistory.get(pos);
- if (localLOGV) Slog.v(
- TAG, "At " + pos + " ckp " + r.task + ": " + r);
- boolean first = true;
- if (r.task.taskId == task) {
- if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
- mHistory.remove(pos);
- mHistory.add(top, r);
- moved.add(0, r);
- top--;
- if (first) {
- addRecentTaskLocked(r.task);
- first = false;
- }
- }
- pos--;
- }
-
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare to front transition: task=" + tr);
- if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- ActivityRecord r = topRunningActivityLocked(null);
- if (r != null) {
- mNoAnimActivities.add(r);
- }
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
- }
-
- mWindowManager.moveAppTokensToTop(moved);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- finishTaskMoveLocked(task);
- EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task);
- }
-
- private final void finishTaskMoveLocked(int task) {
- resumeTopActivityLocked(null);
- }
-
public void moveTaskToBack(int task) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToBack()");
synchronized(this) {
- if (mResumedActivity != null && mResumedActivity.task.taskId == task) {
+ if (mMainStack.mResumedActivity != null
+ && mMainStack.mResumedActivity.task.taskId == task) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), "Task to back")) {
return;
}
}
final long origId = Binder.clearCallingIdentity();
- moveTaskToBackLocked(task, null);
+ mMainStack.moveTaskToBackLocked(task, null);
Binder.restoreCallingIdentity(origId);
}
}
@@ -7750,93 +4683,13 @@
final long origId = Binder.clearCallingIdentity();
int taskId = getTaskForActivityLocked(token, !nonRoot);
if (taskId >= 0) {
- return moveTaskToBackLocked(taskId, null);
+ return mMainStack.moveTaskToBackLocked(taskId, null);
}
Binder.restoreCallingIdentity(origId);
}
return false;
}
- /**
- * Worker method for rearranging history stack. Implements the function of moving all
- * activities for a specific task (gathering them if disjoint) into a single group at the
- * bottom of the stack.
- *
- * If a watcher is installed, the action is preflighted and the watcher has an opportunity
- * to premeptively cancel the move.
- *
- * @param task The taskId to collect and move to the bottom.
- * @return Returns true if the move completed, false if not.
- */
- private 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 (mController != null) {
- ActivityRecord next = topRunningActivityLocked(null, task);
- if (next == null) {
- next = topRunningActivityLocked(null, 0);
- }
- if (next != null) {
- // ask watcher if this is allowed
- boolean moveOK = true;
- try {
- moveOK = mController.activityResuming(next.packageName);
- } catch (RemoteException e) {
- mController = null;
- }
- if (!moveOK) {
- return false;
- }
- }
- }
-
- ArrayList moved = new ArrayList();
-
- 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 = (ActivityRecord)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));
- mHistory.remove(pos);
- mHistory.add(bottom, r);
- moved.add(r);
- bottom++;
- }
- pos++;
- }
-
- if (reason != null &&
- (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
- ActivityRecord r = topRunningActivityLocked(null);
- if (r != null) {
- mNoAnimActivities.add(r);
- }
- } else {
- mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
- }
- mWindowManager.moveAppTokensToBottom(moved);
- if (VALIDATE_TOKENS) {
- mWindowManager.validateAppTokens(mHistory);
- }
-
- finishTaskMoveLocked(task);
- return true;
- }
-
public void moveTaskBackwards(int task) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskBackwards()");
@@ -7863,10 +4716,10 @@
}
int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
- final int N = mHistory.size();
+ final int N = mMainStack.mHistory.size();
TaskRecord lastTask = null;
for (int i=0; i<N; i++) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r == token) {
if (!onlyRoot || lastTask != r.task) {
return r.task.taskId;
@@ -7879,89 +4732,17 @@
return -1;
}
- /**
- * 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) {
- ComponentName cls = intent.getComponent();
- if (info.targetActivity != null) {
- cls = new ComponentName(info.packageName, info.targetActivity);
- }
-
- TaskRecord cp = null;
-
- final int N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (!r.finishing && r.task != cp
- && 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);
- return r;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Returns the first activity (starting from the top of the stack) that
- * is the same as the given activity. Returns null if no such activity
- * is found.
- */
- private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
- ComponentName cls = intent.getComponent();
- if (info.targetActivity != null) {
- cls = new ComponentName(info.packageName, info.targetActivity);
- }
-
- final int N = mHistory.size();
- for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
- if (!r.finishing) {
- if (r.intent.getComponent().equals(cls)) {
- //Slog.i(TAG, "Found matching class!");
- //dump();
- //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
- return r;
- }
- }
- }
-
- return null;
- }
-
public void finishOtherInstances(IBinder token, ComponentName className) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
- int N = mHistory.size();
+ int N = mMainStack.mHistory.size();
TaskRecord lastTask = null;
for (int i=0; i<N; i++) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.realActivity.equals(className)
&& r != token && lastTask != r.task) {
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
null, "others")) {
i--;
N--;
@@ -7995,11 +4776,11 @@
synchronized(this) {
if (r == null) {
- int index = indexOfTokenLocked(token);
+ int index = mMainStack.indexOfTokenLocked(token);
if (index < 0) {
return;
}
- r = (ActivityRecord)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
}
if (thumbnail == null) {
thumbnail = r.thumbnail;
@@ -8531,12 +5312,12 @@
"unhandledBack()");
synchronized(this) {
- int count = mHistory.size();
+ int count = mMainStack.mHistory.size();
if (DEBUG_SWITCH) Slog.d(
TAG, "Performing unhandledBack(): stack size = " + count);
if (count > 1) {
final long origId = Binder.clearCallingIdentity();
- finishActivityLocked((ActivityRecord)mHistory.get(count-1),
+ mMainStack.finishActivityLocked((ActivityRecord)mMainStack.mHistory.get(count-1),
count-1, Activity.RESULT_CANCELED, null, "unhandled-back");
Binder.restoreCallingIdentity(origId);
}
@@ -8579,8 +5360,8 @@
mSleeping = true;
mWindowManager.setEventDispatching(false);
- if (mResumedActivity != null) {
- pauseIfSleepingLocked();
+ if (mMainStack.mResumedActivity != null) {
+ mMainStack.pauseIfSleepingLocked();
} else {
Slog.w(TAG, "goingToSleep with no resumed activity!");
}
@@ -8600,10 +5381,11 @@
mShuttingDown = true;
mWindowManager.setEventDispatching(false);
- if (mResumedActivity != null) {
- pauseIfSleepingLocked();
+ if (mMainStack.mResumedActivity != null) {
+ mMainStack.pauseIfSleepingLocked();
final long endTime = System.currentTimeMillis() + timeout;
- while (mResumedActivity != null || mPausingActivity != null) {
+ while (mMainStack.mResumedActivity != null
+ || mMainStack.mPausingActivity != null) {
long delay = endTime - System.currentTimeMillis();
if (delay <= 0) {
Slog.w(TAG, "Activity manager shutdown timed out");
@@ -8624,35 +5406,14 @@
return timedout;
}
- void pauseIfSleepingLocked() {
- if (mSleeping || mShuttingDown) {
- if (!mGoingToSleep.isHeld()) {
- mGoingToSleep.acquire();
- if (mLaunchingActivity.isHeld()) {
- mLaunchingActivity.release();
- mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- }
- }
-
- // If we are not currently pausing an activity, get the current
- // one to pause. If we are pausing one, we will just let that stuff
- // run and release the wake lock when all done.
- if (mPausingActivity == null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
- startPausingLocked(false, true);
- }
- }
- }
-
public void wakingUp() {
synchronized(this) {
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
+ if (mMainStack.mGoingToSleep.isHeld()) {
+ mMainStack.mGoingToSleep.release();
}
mWindowManager.setEventDispatching(true);
mSleeping = false;
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
}
@@ -8782,29 +5543,29 @@
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
- int index = (token != null) ? indexOfTokenLocked(token) : -1;
+ int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
if (index < 0) {
throw new IllegalArgumentException();
}
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
r.immersive = immersive;
}
}
public boolean isImmersive(IBinder token) {
synchronized (this) {
- int index = (token != null) ? indexOfTokenLocked(token) : -1;
+ int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
if (index < 0) {
throw new IllegalArgumentException();
}
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
return r.immersive;
}
}
public boolean isTopActivityImmersive() {
synchronized (this) {
- ActivityRecord r = topRunningActivityLocked(null);
+ ActivityRecord r = mMainStack.topRunningActivityLocked(null);
return (r != null) ? r.immersive : false;
}
}
@@ -9238,7 +5999,7 @@
} catch (RemoteException e) {
}
- resumeTopActivityLocked(null);
+ mMainStack.resumeTopActivityLocked(null);
}
}
@@ -9326,12 +6087,12 @@
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.info.processName, app.info.uid);
killServicesLocked(app, false);
- for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ 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());
- finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
+ r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed");
}
}
if (!app.persistent) {
@@ -9349,28 +6110,28 @@
return false;
}
} else {
- ActivityRecord r = topRunningActivityLocked(null);
+ ActivityRecord r = mMainStack.topRunningActivityLocked(null);
if (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 = indexOfTokenLocked(r);
- finishActivityLocked(r, index,
+ int index = mMainStack.indexOfTokenLocked(r);
+ r.stack.finishActivityLocked(r, index,
Activity.RESULT_CANCELED, null, "crashed");
// Also terminate an 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)mHistory.get(index);
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
if (r.state == ActivityState.RESUMED
|| r.state == ActivityState.PAUSING
|| r.state == ActivityState.PAUSED) {
if (!r.isHomeActivity) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishActivityLocked(r, index,
+ r.stack.finishActivityLocked(r, index,
Activity.RESULT_CANCELED, null, "crashed");
}
}
@@ -10075,31 +6836,31 @@
if (needHeader) {
pw.println(" Activity stack:");
}
- dumpHistoryList(pw, mHistory, " ", "Hist", true);
+ dumpHistoryList(pw, mMainStack.mHistory, " ", "Hist", true);
pw.println(" ");
pw.println(" Running activities (most recent first):");
- dumpHistoryList(pw, mLRUActivities, " ", "Run", false);
- if (mWaitingVisibleActivities.size() > 0) {
+ dumpHistoryList(pw, mMainStack.mLRUActivities, " ", "Run", false);
+ if (mMainStack.mWaitingVisibleActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting for another to become visible:");
- dumpHistoryList(pw, mWaitingVisibleActivities, " ", "Wait", false);
+ dumpHistoryList(pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false);
}
- if (mStoppingActivities.size() > 0) {
+ if (mMainStack.mStoppingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to stop:");
- dumpHistoryList(pw, mStoppingActivities, " ", "Stop", false);
+ dumpHistoryList(pw, mMainStack.mStoppingActivities, " ", "Stop", false);
}
- if (mFinishingActivities.size() > 0) {
+ if (mMainStack.mFinishingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to finish:");
- dumpHistoryList(pw, mFinishingActivities, " ", "Fin", false);
+ dumpHistoryList(pw, mMainStack.mFinishingActivities, " ", "Fin", false);
}
pw.println(" ");
- pw.println(" mPausingActivity: " + mPausingActivity);
- pw.println(" mResumedActivity: " + mResumedActivity);
+ pw.println(" mPausingActivity: " + mMainStack.mPausingActivity);
+ pw.println(" mResumedActivity: " + mMainStack.mResumedActivity);
pw.println(" mFocusedActivity: " + mFocusedActivity);
- pw.println(" mLastPausedActivity: " + mLastPausedActivity);
+ pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity);
if (dumpAll && mRecentTasks.size() > 0) {
pw.println(" ");
@@ -10268,7 +7029,7 @@
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
}
pw.println(" mConfiguration: " + mConfiguration);
- pw.println(" mConfigWillChange: " + mConfigWillChange);
+ pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange);
pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
@@ -10287,8 +7048,8 @@
+ " mBooting=" + mBooting
+ " mBooted=" + mBooted
+ " mFactoryTest=" + mFactoryTest);
- pw.println(" mGoingToSleep=" + mGoingToSleep);
- pw.println(" mLaunchingActivity=" + mLaunchingActivity);
+ pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep);
+ pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity);
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
}
@@ -10778,22 +7539,6 @@
return false;
}
- private final int indexOfTokenLocked(IBinder token) {
- int count = mHistory.size();
-
- // convert the token to an entry in the history.
- int index = -1;
- for (int i=count-1; i>=0; i--) {
- Object o = mHistory.get(i);
- if (o == token) {
- index = i;
- break;
- }
- }
-
- return index;
- }
-
private final void killServicesLocked(ProcessRecord app,
boolean allowRestart) {
// Report disconnected services.
@@ -12071,12 +8816,12 @@
ActivityRecord activity = null;
if (token != null) {
- int aindex = indexOfTokenLocked(token);
+ int aindex = mMainStack.indexOfTokenLocked(token);
if (aindex < 0) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
- activity = (ActivityRecord)mHistory.get(aindex);
+ activity = (ActivityRecord)mMainStack.mHistory.get(aindex);
}
int clientLabel = 0;
@@ -12180,7 +8925,7 @@
return 1;
}
- private void removeConnectionLocked(
+ void removeConnectionLocked(
ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
@@ -14012,18 +10757,18 @@
// 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 = topRunningActivityLocked(null);
+ starting = mMainStack.topRunningActivityLocked(null);
}
if (starting != null) {
- kept = ensureActivityConfigurationLocked(starting, changes);
+ kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
if (kept) {
// If this didn't result in the starting activity being
// destroyed, then we need to make sure at this point that all
// other activities are made visible.
if (DEBUG_SWITCH) Slog.i(TAG, "Config didn't destroy " + starting
+ ", ensuring others are correct.");
- ensureActivitiesVisibleLocked(starting, changes);
+ mMainStack.ensureActivitiesVisibleLocked(starting, changes);
}
}
@@ -14033,160 +10778,6 @@
return kept;
}
-
- private final boolean relaunchActivityLocked(ActivityRecord r,
- int changes, boolean andResume) {
- List<ResultInfo> results = null;
- List<Intent> newIntents = null;
- if (andResume) {
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
- + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
- EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
- : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
-
- r.startFreezingScreenLocked(r.app, 0);
-
- try {
- if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
- r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
- changes, !andResume, mConfiguration);
- // Note: don't need to call pauseIfSleepingLocked() here, because
- // the caller will only pass in 'andResume' if this activity is
- // currently resumed, which implies we aren't sleeping.
- } catch (RemoteException e) {
- return false;
- }
-
- if (andResume) {
- r.results = null;
- r.newIntents = null;
- reportResumedActivityLocked(r);
- }
-
- return true;
- }
-
- /**
- * Make sure the given activity matches the current configuration. Returns
- * false if the activity had to be destroyed. Returns true if the
- * configuration is the same, or the activity will remain running as-is
- * for whatever reason. Ensures the HistoryRecord is updated with the
- * correct configuration and all other bookkeeping is handled.
- */
- private final boolean ensureActivityConfigurationLocked(ActivityRecord r,
- int globalChanges) {
- if (mConfigWillChange) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Skipping config check (will change): " + r);
- return true;
- }
-
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Ensuring correct configuration: " + r);
-
- // Short circuit: if the two configurations are the exact same
- // object (the common case), then there is nothing to do.
- Configuration newConfig = mConfiguration;
- if (r.configuration == newConfig) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration unchanged in " + r);
- return true;
- }
-
- // We don't worry about activities that are finishing.
- if (r.finishing) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration doesn't matter in finishing " + r);
- r.stopFreezingScreenLocked(false);
- return true;
- }
-
- // Okay we now are going to make this activity have the new config.
- // But then we need to figure out how it needs to deal with that.
- Configuration oldConfig = r.configuration;
- r.configuration = newConfig;
-
- // If the activity isn't currently running, just leave the new
- // configuration and it will pick that up next time it starts.
- if (r.app == null || r.app.thread == null) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Configuration doesn't matter not running " + r);
- r.stopFreezingScreenLocked(false);
- return true;
- }
-
- // If the activity isn't persistent, there is a chance we will
- // need to restart it.
- if (!r.persistent) {
-
- // Figure out what has changed between the two configurations.
- int changes = oldConfig.diff(newConfig);
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
- + Integer.toHexString(changes) + ", handles=0x"
- + Integer.toHexString(r.info.configChanges)
- + ", newConfig=" + newConfig);
- }
- if ((changes&(~r.info.configChanges)) != 0) {
- // Aha, the activity isn't handling the change, so DIE DIE DIE.
- r.configChangeFlags |= changes;
- r.startFreezingScreenLocked(r.app, globalChanges);
- if (r.app == null || r.app.thread == null) {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is destroying non-running " + r);
- destroyActivityLocked(r, true);
- } else if (r.state == ActivityState.PAUSING) {
- // A little annoying: we are waiting for this activity to
- // finish pausing. Let's not do anything now, but just
- // flag that it needs to be restarted when done pausing.
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is skipping already pausing " + r);
- r.configDestroy = true;
- return true;
- } else if (r.state == ActivityState.RESUMED) {
- // Try to optimize this case: the configuration is changing
- // and we need to restart the top, resumed activity.
- // Instead of doing the normal handshaking, just say
- // "restart!".
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is restarting resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, true);
- r.configChangeFlags = 0;
- } else {
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
- "Switch is restarting non-resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, false);
- r.configChangeFlags = 0;
- }
-
- // All done... tell the caller we weren't able to keep this
- // activity around.
- return false;
- }
- }
-
- // Default case: the activity can handle this new configuration, so
- // hand it over. Note that we don't need to give it the new
- // configuration, since we always send configuration changes to all
- // process when they happen so it can just use whatever configuration
- // it last got.
- if (r.app != null && r.app.thread != null) {
- try {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
- r.app.thread.scheduleActivityConfigurationChanged(r);
- } catch (RemoteException e) {
- // If process died, whatever.
- }
- }
- r.stopFreezingScreenLocked(false);
-
- return true;
- }
/**
* Save the locale. You must be inside a synchronized (this) block.
@@ -14542,8 +11133,8 @@
private final boolean canGcNowLocked() {
return mParallelBroadcasts.size() == 0
&& mOrderedBroadcasts.size() == 0
- && (mSleeping || (mResumedActivity != null &&
- mResumedActivity.idle));
+ && (mSleeping || (mMainStack.mResumedActivity != null &&
+ mMainStack.mResumedActivity.idle));
}
/**
@@ -14714,11 +11305,11 @@
}
private final ActivityRecord resumedAppLocked() {
- ActivityRecord resumedActivity = mResumedActivity;
+ ActivityRecord resumedActivity = mMainStack.mResumedActivity;
if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = mPausingActivity;
+ resumedActivity = mMainStack.mPausingActivity;
if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = topRunningActivityLocked(null);
+ resumedActivity = mMainStack.topRunningActivityLocked(null);
}
}
return resumedActivity;
@@ -14746,7 +11337,7 @@
return res;
}
- private final boolean updateOomAdjLocked() {
+ final boolean updateOomAdjLocked() {
boolean didOomAdj = true;
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
@@ -14810,7 +11401,7 @@
return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj;
}
- private final void trimApplications() {
+ final void trimApplications() {
synchronized (this) {
int i;
@@ -14942,7 +11533,7 @@
for (j=0; j<NUMA; j++) {
ActivityRecord r = app.activities.get(j);
if (!r.finishing) {
- destroyActivityLocked(r, false);
+ r.stack.destroyActivityLocked(r, false);
}
r.resultTo = null;
}
@@ -14979,25 +11570,25 @@
// Finally, if there are too many activities now running, try to
// finish as many as we can to get back down to the limit.
for ( i=0;
- i<mLRUActivities.size()
- && mLRUActivities.size() > curMaxActivities;
+ i<mMainStack.mLRUActivities.size()
+ && mMainStack.mLRUActivities.size() > curMaxActivities;
i++) {
final ActivityRecord r
- = (ActivityRecord)mLRUActivities.get(i);
+ = (ActivityRecord)mMainStack.mLRUActivities.get(i);
// We can finish this one if we have its icicle saved and
// it is not persistent.
if ((r.haveState || !r.stateNotNeeded) && !r.visible
&& r.stopped && !r.persistent && !r.finishing) {
- final int origSize = mLRUActivities.size();
- destroyActivityLocked(r, true);
+ final int origSize = mMainStack.mLRUActivities.size();
+ r.stack.destroyActivityLocked(r, true);
// This will remove it from the LRU list, so keep
// our index at the same value. Note that this check to
// see if the size changes is just paranoia -- if
// something unexpected happens, we don't want to end up
// in an infinite loop.
- if (origSize > mLRUActivities.size()) {
+ if (origSize > mMainStack.mLRUActivities.size()) {
i--;
}
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 22ac58d..79756a7 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,7 @@
package com.android.server.am;
import com.android.server.AttributeCache;
-import com.android.server.am.ActivityManagerService.ActivityState;
+import com.android.server.am.ActivityStack.ActivityState;
import android.app.Activity;
import android.content.ComponentName;
@@ -32,6 +32,7 @@
import android.os.SystemClock;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.view.IApplicationToken;
import java.io.PrintWriter;
@@ -44,6 +45,7 @@
*/
class ActivityRecord extends IApplicationToken.Stub {
final ActivityManagerService service; // owner
+ final ActivityStack stack; // owner
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid who started the activity.
final Intent intent; // the original intent that generated us
@@ -80,7 +82,7 @@
ProcessRecord app; // if non-null, hosting application
Bitmap thumbnail; // icon representation of paused screen
CharSequence description; // textual description of paused screen
- ActivityManagerService.ActivityState state; // current state we are in
+ ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
boolean frontOfTask; // is this the root activity of its task?
boolean launchFailed; // set if a launched failed, to abort on 2nd try
@@ -175,12 +177,13 @@
}
}
- ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
+ ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller,
int _launchedFromUid, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified) {
service = _service;
+ stack = _stack;
info = aInfo;
launchedFromUid = _launchedFromUid;
intent = _intent;
@@ -191,7 +194,7 @@
resultTo = _resultTo;
resultWho = _resultWho;
requestCode = _reqCode;
- state = ActivityManagerService.ActivityState.INITIALIZING;
+ state = ActivityState.INITIALIZING;
frontOfTask = false;
launchFailed = false;
haveState = false;
@@ -332,6 +335,52 @@
}
newIntents.add(intent);
}
+
+ /**
+ * Deliver a new Intent to an existing activity, so that its onNewIntent()
+ * method will be called at the proper time.
+ */
+ final void deliverNewIntentLocked(Intent intent) {
+ boolean sent = false;
+ if (state == ActivityState.RESUMED
+ && app != null && app.thread != null) {
+ try {
+ ArrayList<Intent> ar = new ArrayList<Intent>();
+ ar.add(new Intent(intent));
+ app.thread.scheduleNewIntent(ar, this);
+ sent = true;
+ } catch (Exception e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Exception thrown sending new intent to " + this, e);
+ }
+ }
+ if (!sent) {
+ addNewIntentLocked(new Intent(intent));
+ }
+ }
+
+ void removeUriPermissionsLocked() {
+ if (readUriPermissions != null) {
+ for (UriPermission perm : readUriPermissions) {
+ perm.readActivities.remove(this);
+ if (perm.readActivities.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ }
+ }
+ if (writeUriPermissions != null) {
+ for (UriPermission perm : writeUriPermissions) {
+ perm.writeActivities.remove(this);
+ if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
+ &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+ perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ service.removeUriPermissionIfNeededLocked(perm);
+ }
+ }
+ }
+ }
void pauseKeyDispatchingLocked() {
if (!keysPaused) {
@@ -375,8 +424,8 @@
if (startTime != 0) {
final long curTime = SystemClock.uptimeMillis();
final long thisTime = curTime - startTime;
- final long totalTime = service.mInitialStartTime != 0
- ? (curTime - service.mInitialStartTime) : thisTime;
+ final long totalTime = stack.mInitialStartTime != 0
+ ? (curTime - stack.mInitialStartTime) : thisTime;
if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {
EventLog.writeEvent(EventLogTags.ACTIVITY_LAUNCH_TIME,
System.identityHashCode(this), shortComponentName,
@@ -392,14 +441,14 @@
sb.append(" ms)");
Log.i(ActivityManagerService.TAG, sb.toString());
}
- service.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
+ stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
if (totalTime > 0) {
service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
}
startTime = 0;
- service.mInitialStartTime = 0;
+ stack.mInitialStartTime = 0;
}
- service.reportActivityVisibleLocked(this);
+ stack.reportActivityVisibleLocked(this);
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG, "windowsVisible(): " + this);
if (!nowVisible) {
@@ -408,27 +457,27 @@
// 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.
- service.processStoppingActivitiesLocked(false);
+ stack.processStoppingActivitiesLocked(false);
} else {
// If this activity was already idle, then we now need to
// make sure we perform the full stop of any activities
// that are waiting to do so. This is because we won't
// do that while they are still waiting for this one to
// become visible.
- final int N = service.mWaitingVisibleActivities.size();
+ final int N = stack.mWaitingVisibleActivities.size();
if (N > 0) {
for (int i=0; i<N; i++) {
ActivityRecord r = (ActivityRecord)
- service.mWaitingVisibleActivities.get(i);
+ stack.mWaitingVisibleActivities.get(i);
r.waitingVisible = false;
if (ActivityManagerService.DEBUG_SWITCH) Log.v(
ActivityManagerService.TAG,
"Was waiting for visible: " + r);
}
- service.mWaitingVisibleActivities.clear();
+ stack.mWaitingVisibleActivities.clear();
Message msg = Message.obtain();
- msg.what = ActivityManagerService.IDLE_NOW_MSG;
- service.mHandler.sendMessage(msg);
+ msg.what = ActivityStack.IDLE_NOW_MSG;
+ stack.mHandler.sendMessage(msg);
}
}
service.scheduleAppGcsLocked();
@@ -449,9 +498,9 @@
ActivityRecord r = this;
if (r.waitingVisible) {
// Hmmm, who might we be waiting for?
- r = service.mResumedActivity;
+ r = stack.mResumedActivity;
if (r == null) {
- r = service.mPausingActivity;
+ r = stack.mPausingActivity;
}
// Both of those null? Fall back to 'this' again
if (r == null) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
new file mode 100644
index 0000000..de7b15c
--- /dev/null
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -0,0 +1,3521 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
+
+import android.app.Activity;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import static android.app.IActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.IActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+import static android.app.IActivityManager.START_INTENT_NOT_RESOLVED;
+import static android.app.IActivityManager.START_PERMISSION_DENIED;
+import static android.app.IActivityManager.START_RETURN_INTENT_TO_CALLER;
+import static android.app.IActivityManager.START_SUCCESS;
+import static android.app.IActivityManager.START_SWITCHES_CANCELED;
+import static android.app.IActivityManager.START_TASK_TO_FRONT;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
+import android.app.ResultInfo;
+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.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.view.WindowManagerPolicy;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * State and management of a single stack of activities.
+ */
+public class ActivityStack {
+ static final String TAG = ActivityManagerService.TAG;
+ 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_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 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;
+
+ // How long we wait until giving up on the last activity to pause. This
+ // is short because it directly impacts the responsiveness of starting the
+ // next activity.
+ static final int PAUSE_TIMEOUT = 500;
+
+ // How long we can hold the launch wake lock before giving up.
+ static final int LAUNCH_TIMEOUT = 10*1000;
+
+ // 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
+ // 30 minutes.
+ static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+
+ // 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,
+ PAUSING,
+ PAUSED,
+ STOPPING,
+ STOPPED,
+ FINISHING,
+ DESTROYING,
+ DESTROYED
+ }
+
+ final ActivityManagerService mService;
+ final boolean mMainStack;
+
+ final Context mContext;
+
+ /**
+ * The back history of all previous (and possibly still
+ * running) activities. It contains HistoryRecord objects.
+ */
+ final ArrayList mHistory = new ArrayList();
+
+ /**
+ * List of running activities, sorted by recent usage.
+ * The first entry in the list is the least recently used.
+ * It contains HistoryRecord objects.
+ */
+ final ArrayList mLRUActivities = new ArrayList();
+
+ /**
+ * 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>();
+
+ /**
+ * Animations that for the current transition have requested not to
+ * be considered for the transition animation.
+ */
+ final ArrayList<ActivityRecord> mNoAnimActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
+ * List of activities that are ready to be finished, but waiting
+ * for the previous activity to settle down before doing so. It contains
+ * HistoryRecord objects.
+ */
+ 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.
+ */
+ final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
+ = new ArrayList<IActivityManager.WaitResult>();
+
+ /**
+ * Set when the system is going to sleep, until we have
+ * successfully paused the current activity and released our wake lock.
+ * At that point the system is allowed to actually sleep.
+ */
+ final PowerManager.WakeLock mGoingToSleep;
+
+ /**
+ * We don't want to allow the device to go to sleep while in the process
+ * of launching an activity. This is primarily to allow alarm intent
+ * receivers to launch an activity and get that to run before the device
+ * goes back to sleep.
+ */
+ final PowerManager.WakeLock mLaunchingActivity;
+
+ /**
+ * When we are in the process of pausing an activity, before starting the
+ * next one, this variable holds the activity that is currently being paused.
+ */
+ ActivityRecord mPausingActivity = null;
+
+ /**
+ * This is the last activity that we put into the paused state. This is
+ * used to determine if we need to do an activity transition while sleeping,
+ * when we normally hold the top activity paused.
+ */
+ ActivityRecord mLastPausedActivity = null;
+
+ /**
+ * Current activity that is resumed, or null if there is none.
+ */
+ ActivityRecord mResumedActivity = 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;
+
+ static final int PAUSE_TIMEOUT_MSG = 9;
+ static final int IDLE_TIMEOUT_MSG = 10;
+ static final int IDLE_NOW_MSG = 11;
+ static final int LAUNCH_TIMEOUT_MSG = 16;
+ static final int DESTROY_TIMEOUT_MSG = 17;
+ static final int RESUME_TOP_ACTIVITY_MSG = 19;
+
+ final Handler mHandler = new Handler() {
+ //public Handler() {
+ // if (localLOGV) Slog.v(TAG, "Handler started!");
+ //}
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case PAUSE_TIMEOUT_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ // 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 pause timeout for " + token);
+ activityPaused(token, null, true);
+ } break;
+ case IDLE_TIMEOUT_MSG: {
+ if (mService.mDidDexOpt) {
+ mService.mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+ nmsg.obj = msg.obj;
+ mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
+ return;
+ }
+ // We don't at this point know if the activity is fullscreen,
+ // so we need to be conservative and assume it isn't.
+ IBinder token = (IBinder)msg.obj;
+ Slog.w(TAG, "Activity idle timeout for " + token);
+ activityIdleInternal(token, true, null);
+ } break;
+ case DESTROY_TIMEOUT_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ // 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 " + token);
+ activityDestroyed(token);
+ } break;
+ case IDLE_NOW_MSG: {
+ IBinder token = (IBinder)msg.obj;
+ activityIdleInternal(token, false, null);
+ } break;
+ case LAUNCH_TIMEOUT_MSG: {
+ if (mService.mDidDexOpt) {
+ mService.mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
+ return;
+ }
+ synchronized (mService) {
+ if (mLaunchingActivity.isHeld()) {
+ Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+ mLaunchingActivity.release();
+ }
+ }
+ } break;
+ case RESUME_TOP_ACTIVITY_MSG: {
+ synchronized (mService) {
+ resumeTopActivityLocked(null);
+ }
+ } break;
+ }
+ }
+ };
+
+ ActivityStack(ActivityManagerService service, Context context, boolean mainStack) {
+ 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);
+ }
+
+ final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && r != notTop) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && !r.delayedResume && r != notTop) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ /**
+ * 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 = (ActivityRecord)mHistory.get(i);
+ // Note: the taskId check depends on real taskId fields being non-zero
+ if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
+ final int indexOfTokenLocked(IBinder token) {
+ int count = mHistory.size();
+
+ // convert the token to an entry in the history.
+ int index = -1;
+ for (int i=count-1; i>=0; i--) {
+ Object o = mHistory.get(i);
+ if (o == token) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ private final boolean updateLRUListLocked(ActivityRecord r) {
+ final boolean hadit = mLRUActivities.remove(r);
+ mLRUActivities.add(r);
+ return hadit;
+ }
+
+ /**
+ * 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) {
+ ComponentName cls = intent.getComponent();
+ if (info.targetActivity != null) {
+ cls = new ComponentName(info.packageName, info.targetActivity);
+ }
+
+ TaskRecord cp = null;
+
+ final int N = mHistory.size();
+ for (int i=(N-1); i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing && r.task != cp
+ && 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);
+ return r;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the first activity (starting from the top of the stack) that
+ * is the same as the given activity. Returns null if no such activity
+ * is found.
+ */
+ private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) {
+ ComponentName cls = intent.getComponent();
+ if (info.targetActivity != null) {
+ cls = new ComponentName(info.packageName, info.targetActivity);
+ }
+
+ final int N = mHistory.size();
+ for (int i=(N-1); i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (!r.finishing) {
+ if (r.intent.getComponent().equals(cls)) {
+ //Slog.i(TAG, "Found matching class!");
+ //dump();
+ //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
+ return r;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ final boolean realStartActivityLocked(ActivityRecord r,
+ ProcessRecord app, boolean andResume, boolean checkConfig)
+ throws RemoteException {
+
+ r.startFreezingScreenLocked(app, 0);
+ mService.mWindowManager.setAppVisibility(r, true);
+
+ // 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 : null);
+ mService.updateConfigurationLocked(config, r);
+ }
+
+ r.app = app;
+
+ if (localLOGV) Slog.v(TAG, "Launching: " + r);
+
+ int idx = app.activities.indexOf(r);
+ if (idx < 0) {
+ app.activities.add(r);
+ }
+ mService.updateLruProcessLocked(app, true, 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,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+ }
+ if (r.isHomeActivity) {
+ mService.mHomeProcess = app;
+ }
+ mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
+ System.identityHashCode(r),
+ r.info, r.icicle, results, newIntents, !andResume,
+ mService.isNextTransitionForward());
+
+ if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 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, Activity.RESULT_CANCELED, null,
+ "2nd-crash");
+ 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;
+ r.icicle = null;
+ r.haveState = false;
+ r.stopped = false;
+ mResumedActivity = r;
+ r.task.touchActiveTime();
+ completeResumeLocked(r);
+ pauseIfSleepingLocked();
+ } 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.
+ 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;
+ }
+
+ 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);
+
+ if (r.startTime == 0) {
+ r.startTime = SystemClock.uptimeMillis();
+ if (mInitialStartTime == 0) {
+ mInitialStartTime = r.startTime;
+ }
+ } else if (mInitialStartTime == 0) {
+ mInitialStartTime = SystemClock.uptimeMillis();
+ }
+
+ if (app != null && app.thread != null) {
+ try {
+ 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);
+ }
+
+ void pauseIfSleepingLocked() {
+ if (mService.mSleeping || mService.mShuttingDown) {
+ if (!mGoingToSleep.isHeld()) {
+ mGoingToSleep.acquire();
+ if (mLaunchingActivity.isHeld()) {
+ mLaunchingActivity.release();
+ mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ }
+ }
+
+ // If we are not currently pausing an activity, get the current
+ // one to pause. If we are pausing one, we will just let that stuff
+ // run and release the wake lock when all done.
+ if (mPausingActivity == null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
+ if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
+ startPausingLocked(false, true);
+ }
+ }
+ }
+
+ 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);
+ }
+ ActivityRecord prev = mResumedActivity;
+ if (prev == null) {
+ RuntimeException e = new RuntimeException();
+ Slog.e(TAG, "Trying to pause when nothing is resumed", e);
+ resumeTopActivityLocked(null);
+ return;
+ }
+ if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
+ mResumedActivity = null;
+ mPausingActivity = prev;
+ mLastPausedActivity = prev;
+ prev.state = ActivityState.PAUSING;
+ prev.task.touchActiveTime();
+
+ mService.updateCpuStats();
+
+ if (prev.app != null && prev.app.thread != null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
+ try {
+ EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
+ System.identityHashCode(prev),
+ prev.shortComponentName);
+ prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving,
+ prev.configChangeFlags);
+ if (mMainStack) {
+ mService.updateUsageStats(prev, false);
+ }
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ }
+ } else {
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ }
+
+ // 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) {
+ mLaunchingActivity.acquire();
+ if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
+ // To be safe, don't allow the wake lock to be held for too long.
+ Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);
+ }
+ }
+
+
+ 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
+ // the screen is being turned off and the UI is sleeping, don't interrupt
+ // key dispatch; the same activity will pick it up again on wakeup.
+ if (!uiSleeping) {
+ prev.pauseKeyDispatchingLocked();
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
+ }
+
+ // Schedule a pause timeout in case the app doesn't respond.
+ // We don't give it much time because this directly impacts the
+ // responsiveness seen by the user.
+ Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+ msg.obj = prev;
+ mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+ if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ } else {
+ // 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);
+ }
+ }
+
+ final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
+ if (DEBUG_PAUSE) Slog.v(
+ TAG, "Activity paused: token=" + token + ", icicle=" + icicle
+ + ", timeout=" + timeout);
+
+ ActivityRecord r = null;
+
+ synchronized (mService) {
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ r = (ActivityRecord)mHistory.get(index);
+ if (!timeout) {
+ r.icicle = icicle;
+ r.haveState = true;
+ }
+ mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ if (mPausingActivity == r) {
+ r.state = ActivityState.PAUSED;
+ completePauseLocked();
+ } else {
+ EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
+ System.identityHashCode(r), r.shortComponentName,
+ mPausingActivity != null
+ ? mPausingActivity.shortComponentName : "(none)");
+ }
+ }
+ }
+ }
+
+ 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);
+ prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
+ } else if (prev.app != null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
+ if (prev.waitingVisible) {
+ prev.waitingVisible = false;
+ mWaitingVisibleActivities.remove(prev);
+ if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
+ TAG, "Complete pause, no longer waiting: " + prev);
+ }
+ if (prev.configDestroy) {
+ // The previous is being paused because the configuration
+ // is changing, which means it is actually stopping...
+ // To juggle the fact that we are also starting a new
+ // instance right now, we need to first completely stop
+ // the current instance before starting the new one.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
+ destroyActivityLocked(prev, true);
+ } else {
+ mStoppingActivities.add(prev);
+ if (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.
+ if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
+ Message msg = Message.obtain();
+ msg.what = IDLE_NOW_MSG;
+ mHandler.sendMessage(msg);
+ }
+ }
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
+ prev = null;
+ }
+ mPausingActivity = null;
+ }
+
+ if (!mService.mSleeping && !mService.mShuttingDown) {
+ resumeTopActivityLocked(prev);
+ } else {
+ if (mGoingToSleep.isHeld()) {
+ mGoingToSleep.release();
+ }
+ if (mService.mShuttingDown) {
+ mService.notifyAll();
+ }
+ }
+
+ if (prev != null) {
+ prev.resumeKeyDispatchingLocked();
+ }
+
+ if (prev.app != null && prev.cpuTimeAtResume > 0
+ && mService.mBatteryStatsService.isOnBattery()) {
+ long diff = 0;
+ synchronized (mService.mProcessStatsThread) {
+ diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid)
+ - prev.cpuTimeAtResume;
+ }
+ if (diff > 0) {
+ BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics();
+ synchronized (bsi) {
+ BatteryStatsImpl.Uid.Proc ps =
+ bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
+ prev.info.packageName);
+ if (ps != null) {
+ ps.addForegroundTimeLocked(diff);
+ }
+ }
+ }
+ }
+ prev.cpuTimeAtResume = 0; // reset it
+ }
+
+ /**
+ * 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),
+ * this function updates the rest of our state to match that fact.
+ */
+ private final void completeResumeLocked(ActivityRecord next) {
+ next.idle = false;
+ next.results = null;
+ next.newIntents = null;
+
+ // schedule an idle timeout in case the app doesn't do it for us.
+ Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+ msg.obj = next;
+ mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
+
+ if (false) {
+ // The activity was never told to pause, so just keep
+ // things going as-is. To maintain our own state,
+ // we need to emulate it coming back and saying it is
+ // idle.
+ msg = mHandler.obtainMessage(IDLE_NOW_MSG);
+ msg.obj = next;
+ mHandler.sendMessage(msg);
+ }
+
+ if (mMainStack) {
+ mService.reportResumedActivityLocked(next);
+ }
+
+ next.thumbnail = null;
+ if (mMainStack) {
+ mService.setFocusedActivityLocked(next);
+ }
+ next.resumeKeyDispatchingLocked();
+ 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,
+ // not after the onResume. But for subsequent starts, onResume is fine.
+ if (next.app != null) {
+ synchronized (mService.mProcessStatsThread) {
+ next.cpuTimeAtResume = mService.mProcessStats.getCpuTimeForPid(next.app.pid);
+ }
+ } else {
+ next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
+ }
+ }
+
+ /**
+ * Make sure that all activities that need to be visible (that is, they
+ * currently can be seen by the user) actually are.
+ */
+ final void ensureActivitiesVisibleLocked(ActivityRecord top,
+ ActivityRecord starting, String onlyThisProcess, int configChanges) {
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "ensureActivitiesVisible behind " + top
+ + " configChanges=0x" + Integer.toHexString(configChanges));
+
+ // 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 = (ActivityRecord)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.
+ 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, true);
+ }
+ if (r != starting) {
+ startSpecificActivityLocked(r, false, false);
+ }
+ }
+
+ } else if (r.visible) {
+ // If this activity is already visible, then there is nothing
+ // else to do here.
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Skipping: already visible at " + r);
+ r.stopFreezingScreenLocked(false);
+
+ } 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, true);
+ r.app.thread.scheduleWindowVisibility(r, 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);
+ }
+ }
+ }
+
+ // Aggregate current change flags.
+ configChanges |= r.configChangeFlags;
+
+ 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;
+ }
+ }
+
+ // 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 = (ActivityRecord)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 (DEBUG_VISBILITY) Slog.v(
+ TAG, "Making invisible: " + r);
+ r.visible = false;
+ try {
+ mService.mWindowManager.setAppVisibility(r, 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);
+ r.app.thread.scheduleWindowVisibility(r, false);
+ }
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making hidden: "
+ + r.intent.getComponent(), e);
+ }
+ } else {
+ 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--;
+ }
+ }
+
+ /**
+ * Version of ensureActivitiesVisible that can easily be called anywhere.
+ */
+ final void ensureActivitiesVisibleLocked(ActivityRecord starting,
+ int configChanges) {
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ ensureActivitiesVisibleLocked(r, starting, null, configChanges);
+ }
+ }
+
+ /**
+ * Ensure that the top activity in the stack is resumed.
+ *
+ * @param prev The previously resumed activity, for when in the process
+ * of pausing; can be null to call from elsewhere.
+ *
+ * @return Returns true if something is being resumed, or false if
+ * nothing happened.
+ */
+ final boolean resumeTopActivityLocked(ActivityRecord prev) {
+ // 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;
+
+ if (next == null) {
+ // There are no more activities! Let's just start up the
+ // Launcher...
+ if (mMainStack) {
+ return mService.startHomeActivityLocked();
+ }
+ }
+
+ next.delayedResume = false;
+
+ // If the top activity is the resumed one, nothing to do.
+ if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return false;
+ }
+
+ // 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)
+ && mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return false;
+ }
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mStoppingActivities.remove(next);
+ mWaitingVisibleActivities.remove(next);
+
+ if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
+
+ // If we are currently pausing an activity, then don't do anything
+ // until that is done.
+ if (mPausingActivity != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
+ return false;
+ }
+
+ // We need to start pausing the current activity so the top one
+ // can be resumed...
+ if (mResumedActivity != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
+ startPausingLocked(userLeaving, false);
+ return true;
+ }
+
+ if (prev != null && prev != next) {
+ if (!prev.waitingVisible && next != null && !next.nowVisible) {
+ prev.waitingVisible = true;
+ mWaitingVisibleActivities.add(prev);
+ if (DEBUG_SWITCH) Slog.v(
+ TAG, "Resuming top, waiting visible to hide: " + prev);
+ } else {
+ // The next activity is already visible, so hide the previous
+ // activity's windows right now so we can show the new one ASAP.
+ // We only do this if the previous is finishing, which should mean
+ // it is on top of the one being resumed so hiding it quickly
+ // is good. Otherwise, we want to do the normal route of allowing
+ // the resumed activity to be shown so we can decide if the
+ // previous should actually be hidden depending on whether the
+ // new one is found to be full-screen or not.
+ if (prev.finishing) {
+ mService.mWindowManager.setAppVisibility(prev, false);
+ if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
+ + prev + ", waitingVisible="
+ + (prev != null ? prev.waitingVisible : null)
+ + ", nowVisible=" + next.nowVisible);
+ } else {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
+ + prev + ", waitingVisible="
+ + (prev != null ? prev.waitingVisible : null)
+ + ", nowVisible=" + next.nowVisible);
+ }
+ }
+ }
+
+ // We are starting up the next activity, so tell the window manager
+ // that the previous one will be hidden soon. This way it can know
+ // to ignore it when computing the desired screen orientation.
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare close transition: prev=" + prev);
+ if (mNoAnimActivities.contains(prev)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(prev.task == next.task
+ ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
+ : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
+ }
+ mService.mWindowManager.setAppWillBeHidden(prev);
+ mService.mWindowManager.setAppVisibility(prev, false);
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: prev=" + prev);
+ if (mNoAnimActivities.contains(next)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(prev.task == next.task
+ ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
+ : WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ }
+ }
+ if (false) {
+ mService.mWindowManager.setAppWillBeHidden(prev);
+ mService.mWindowManager.setAppVisibility(prev, false);
+ }
+ } else if (mHistory.size() > 1) {
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: no previous");
+ if (mNoAnimActivities.contains(next)) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ }
+ }
+
+ if (next.app != null && next.app.thread != null) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
+
+ // This activity is now becoming visible.
+ mService.mWindowManager.setAppVisibility(next, true);
+
+ ActivityRecord lastResumedActivity = mResumedActivity;
+ ActivityState lastState = next.state;
+
+ mService.updateCpuStats();
+
+ next.state = ActivityState.RESUMED;
+ mResumedActivity = next;
+ next.task.touchActiveTime();
+ mService.updateLruProcessLocked(next.app, true, 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 : null);
+ if (config != null) {
+ next.frozenBeforeDestroy = true;
+ }
+ updated = mService.updateConfigurationLocked(config, next);
+ }
+ }
+ if (!updated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivityLocked(null);
+ if (DEBUG_SWITCH) Slog.i(TAG,
+ "Activity config changed during resume: " + next
+ + ", new next: " + nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG);
+ }
+ if (mMainStack) {
+ mService.setFocusedActivityLocked(next);
+ }
+ ensureActivitiesVisibleLocked(null, 0);
+ mService.mWindowManager.executeAppTransition();
+ mNoAnimActivities.clear();
+ return true;
+ }
+
+ try {
+ // Deliver all pending results.
+ ArrayList a = next.results;
+ if (a != null) {
+ final int N = a.size();
+ if (!next.finishing && N > 0) {
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Delivering results to " + next
+ + ": " + a);
+ next.app.thread.scheduleSendResult(next, a);
+ }
+ }
+
+ if (next.newIntents != null) {
+ next.app.thread.scheduleNewIntent(next.newIntents, next);
+ }
+
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
+ System.identityHashCode(next),
+ next.task.taskId, next.shortComponentName);
+
+ next.app.thread.scheduleResumeActivity(next,
+ mService.isNextTransitionForward());
+
+ pauseIfSleepingLocked();
+
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ next.state = lastState;
+ mResumedActivity = lastResumedActivity;
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW && mMainStack) {
+ mService.mWindowManager.setAppStartingWindow(
+ next, next.packageName, next.theme,
+ next.nonLocalizedLabel,
+ next.labelRes, next.icon, null, true);
+ }
+ }
+ startSpecificActivityLocked(next, true, false);
+ return true;
+ }
+
+ // From this point on, if something goes wrong there is no way
+ // to recover the activity.
+ try {
+ next.visible = true;
+ completeResumeLocked(next);
+ } catch (Exception e) {
+ // If any exception gets thrown, toss away this
+ // activity and try the next one.
+ Slog.w(TAG, "Exception thrown during resume of " + next, e);
+ requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null,
+ "resume-exception");
+ return true;
+ }
+
+ // Didn't need to use the icicle, and it is now out of date.
+ next.icicle = null;
+ next.haveState = false;
+ next.stopped = false;
+
+ } else {
+ // Whoops, need to restart this activity!
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW) {
+ mService.mWindowManager.setAppStartingWindow(
+ next, next.packageName, next.theme,
+ next.nonLocalizedLabel,
+ next.labelRes, next.icon, null, true);
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
+ }
+ startSpecificActivityLocked(next, true, true);
+ }
+
+ return true;
+ }
+
+ private final void startActivityLocked(ActivityRecord r, boolean newTask,
+ boolean doResume) {
+ final int NH = mHistory.size();
+
+ int addPos = -1;
+
+ if (!newTask) {
+ // If starting in an existing task, find where that is...
+ ActivityRecord next = null;
+ boolean startIt = true;
+ for (int i = NH-1; i >= 0; i--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ if (p.finishing) {
+ continue;
+ }
+ if (p.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) {
+ mHistory.add(addPos, r);
+ r.inHistory = true;
+ r.task.numActivities++;
+ mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
+ r.info.screenOrientation, r.fullscreen);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ return;
+ }
+ break;
+ }
+ if (p.fullscreen) {
+ startIt = false;
+ }
+ next = p;
+ }
+ }
+
+ // Place a new activity at top of stack, so it is next to interact
+ // with the user.
+ if (addPos < 0) {
+ addPos = mHistory.size();
+ }
+
+ // 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");
+ }
+
+ // Slot the activity into the history stack and proceed
+ mHistory.add(addPos, r);
+ r.inHistory = true;
+ r.frontOfTask = newTask;
+ r.task.numActivities++;
+ if (NH > 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.
+ boolean showStartingIcon = newTask;
+ ProcessRecord proc = r.app;
+ if (proc == null) {
+ proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
+ }
+ if (proc == null || proc.thread == null) {
+ showStartingIcon = true;
+ }
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare open transition: starting " + r);
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mNoAnimActivities.add(r);
+ } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ mNoAnimActivities.remove(r);
+ } else {
+ mService.mWindowManager.prepareAppTransition(newTask
+ ? WindowManagerPolicy.TRANSIT_TASK_OPEN
+ : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ mNoAnimActivities.remove(r);
+ }
+ mService.mWindowManager.addAppToken(
+ addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen);
+ boolean doShow = true;
+ if (newTask) {
+ // Even though this activity is starting fresh, we still need
+ // to reset it to make sure we apply affinities to move any
+ // existing activities from other tasks in to it.
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((r.intent.getFlags()
+ &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ resetTaskIfNeededLocked(r, r);
+ doShow = topRunningNonDelayedActivityLocked(null) == r;
+ }
+ }
+ if (SHOW_APP_STARTING_PREVIEW && doShow) {
+ // Figure out if we are transitioning from another activity that is
+ // "has the same starting icon" as the next one. This allows the
+ // window manager to keep the previous window it had previously
+ // created, if it still had one.
+ ActivityRecord prev = mResumedActivity;
+ if (prev != null) {
+ // We don't want to reuse the previous starting preview if:
+ // (1) The current activity is in a different task.
+ if (prev.task != r.task) prev = null;
+ // (2) The current activity is already displayed.
+ else if (prev.nowVisible) prev = null;
+ }
+ mService.mWindowManager.setAppStartingWindow(
+ r, r.packageName, r.theme, r.nonLocalizedLabel,
+ r.labelRes, r.icon, prev, showStartingIcon);
+ }
+ } 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, r.task.taskId,
+ r.info.screenOrientation, r.fullscreen);
+ }
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ }
+
+ /**
+ * 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 (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;
+ for (int i=mHistory.size()-1; i>=-1; i--) {
+ ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
+
+ if (below != null && below.finishing) {
+ continue;
+ }
+ 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;
+ }
+
+ final int flags = target.info.flags;
+
+ final boolean finishOnTaskLaunch =
+ (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;
+ }
+ 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 = (ActivityRecord)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.task = p.task;
+ 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.task = new TaskRecord(mService.mCurTask, target.info, null,
+ (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ 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, task.taskId);
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ int dstPos = 0;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+ + " out to target's task " + target.task);
+ task.numActivities--;
+ p.task = target.task;
+ target.task.numActivities++;
+ mHistory.remove(srcPos);
+ mHistory.add(dstPos, p);
+ mService.mWindowManager.moveAppToken(dstPos, p);
+ mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+ dstPos++;
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ i++;
+ }
+ if (taskTop == p) {
+ taskTop = below;
+ }
+ if (taskTopI == replyChainEnd) {
+ taskTopI = -1;
+ }
+ replyChainEnd = -1;
+ if (mMainStack) {
+ mService.addRecentTaskLocked(target.task);
+ }
+ } 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() &&
+ ((ActivityRecord)mHistory.get(
+ replyChainEnd)).task == task) {
+ replyChainEnd++;
+ }
+ replyChainEnd--;
+ } else if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ ActivityRecord p = null;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(p, srcPos,
+ Activity.RESULT_CANCELED, null, "reset")) {
+ replyChainEnd--;
+ srcPos--;
+ }
+ }
+ if (taskTop == p) {
+ taskTop = below;
+ }
+ if (taskTopI == replyChainEnd) {
+ taskTopI = -1;
+ }
+ 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;
+ }
+ } else {
+ // Reached the bottom of the task -- any reply chain
+ // should be left as-is.
+ replyChainEnd = -1;
+ }
+
+ } else 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.
+ // 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 (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
+ // 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
+ // with an affinity to it... this is really not a normal
+ // case, and we will need to later pull that task to the front
+ // and usually at that point we will do the reset and pick
+ // up those remaining activities. (This only happens if
+ // 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;
+ for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
+ p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(p, srcPos,
+ Activity.RESULT_CANCELED, null, "reset")) {
+ taskTopI--;
+ lastReparentPos--;
+ replyChainEnd--;
+ srcPos--;
+ }
+ }
+ replyChainEnd = -1;
+ } else {
+ if (replyChainEnd < 0) {
+ replyChainEnd = targetI;
+ }
+ for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
+ if (p.finishing) {
+ continue;
+ }
+ if (lastReparentPos < 0) {
+ lastReparentPos = taskTopI;
+ taskTop = p;
+ } else {
+ lastReparentPos--;
+ }
+ mHistory.remove(srcPos);
+ p.task.numActivities--;
+ p.task = task;
+ mHistory.add(lastReparentPos, p);
+ if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+ + " in to resetting task " + task);
+ task.numActivities++;
+ mService.mWindowManager.moveAppToken(lastReparentPos, p);
+ mService.mWindowManager.setAppGroupId(p, p.task.taskId);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ }
+ 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 = (ActivityRecord)mHistory.get(j);
+ if (p.finishing) {
+ continue;
+ }
+ if (p.intent.getComponent().equals(target.intent.getComponent())) {
+ if (finishActivityLocked(p, j,
+ Activity.RESULT_CANCELED, null, "replace")) {
+ taskTopI--;
+ lastReparentPos--;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ target = below;
+ targetI = i;
+ }
+
+ 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 continue to be used,
+ * or null if none was found.
+ */
+ private final ActivityRecord performClearTaskLocked(int taskId,
+ ActivityRecord newR, int launchFlags, boolean doClear) {
+ int i = mHistory.size();
+
+ // First find the requested task.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId == taskId) {
+ i++;
+ break;
+ }
+ }
+
+ // Now clear it.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)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;
+ if (doClear) {
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ 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);
+ if (index >= 0) {
+ finishActivityLocked(ret, index, Activity.RESULT_CANCELED,
+ null, "clear");
+ }
+ return null;
+ }
+ }
+
+ return ret;
+ }
+ }
+
+ return 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.
+ */
+ private final int findActivityInHistoryLocked(ActivityRecord r, int task) {
+ int i = mHistory.size();
+ while (i > 0) {
+ i--;
+ ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
+ 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 = (ActivityRecord)mHistory.remove(where);
+ int top = mHistory.size();
+ ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
+ mHistory.add(top, newTop);
+ oldTop.frontOfTask = false;
+ newTop.frontOfTask = true;
+ return newTop;
+ }
+
+ final int startActivityLocked(IApplicationThread caller,
+ Intent intent, String resolvedType,
+ Uri[] grantedUriPermissions,
+ int grantedMode, ActivityInfo aInfo, IBinder resultTo,
+ String resultWho, int requestCode,
+ int callingPid, int callingUid, boolean onlyIfNeeded,
+ boolean componentSpecified) {
+ Slog.i(TAG, "Starting activity: " + intent);
+
+ ActivityRecord sourceRecord = null;
+ ActivityRecord resultRecord = null;
+ if (resultTo != null) {
+ int index = indexOfTokenLocked(resultTo);
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Sending result to " + resultTo + " (index " + index + ")");
+ if (index >= 0) {
+ sourceRecord = (ActivityRecord)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) {
+ return 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);
+ }
+ }
+
+ int err = START_SUCCESS;
+
+ if (intent.getComponent() == null) {
+ // We couldn't find a class that can handle the given Intent.
+ // That's the end of that!
+ err = START_INTENT_NOT_RESOLVED;
+ }
+
+ if (err == START_SUCCESS && aInfo == null) {
+ // We couldn't find the specific class specified in the Intent.
+ // Also the end of the line.
+ err = START_CLASS_NOT_FOUND;
+ }
+
+ ProcessRecord callerApp = null;
+ if (err == START_SUCCESS && 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 = START_PERMISSION_DENIED;
+ }
+ }
+
+ if (err != START_SUCCESS) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ return err;
+ }
+
+ final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,
+ callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + aInfo.permission;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ if (mMainStack) {
+ if (mService.mController != null) {
+ boolean abort = false;
+ 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) {
+ 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.
+ return START_SUCCESS;
+ }
+ }
+ }
+
+ ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
+ intent, resolvedType, aInfo, mService.mConfiguration,
+ resultRecord, resultWho, requestCode, componentSpecified);
+
+ 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.grantedUriPermissions = grantedUriPermissions;
+ pal.grantedMode = grantedMode;
+ pal.onlyIfNeeded = onlyIfNeeded;
+ mService.mPendingActivityLaunches.add(pal);
+ return 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);
+ }
+
+ return startActivityUncheckedLocked(r, sourceRecord,
+ grantedUriPermissions, grantedMode, onlyIfNeeded, true);
+ }
+
+ final int startActivityUncheckedLocked(ActivityRecord r,
+ ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
+ int grantedMode, boolean onlyIfNeeded, boolean doResume) {
+ 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 (onlyIfNeeded) {
+ 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.
+ onlyIfNeeded = false;
+ }
+ }
+
+ if (grantedUriPermissions != null && callingUid > 0) {
+ for (int i=0; i<grantedUriPermissions.length; i++) {
+ mService.grantUriPermissionLocked(callingUid, r.packageName,
+ grantedUriPermissions[i], grantedMode, r);
+ }
+ }
+
+ mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ intent, r);
+
+ 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;
+ }
+
+ boolean addingToTask = false;
+ 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.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.
+ moveTaskToFrontLocked(taskTop.task, r);
+ }
+ }
+ // 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 (onlyIfNeeded) {
+ // 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);
+ }
+ return START_RETURN_INTENT_TO_CALLER;
+ }
+ 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, true);
+ 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(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
+ && 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(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) {
+ // 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);
+ }
+ return 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)) {
+ 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);
+ }
+ if (onlyIfNeeded) {
+ // 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 START_RETURN_INTENT_TO_CALLER;
+ }
+ top.deliverNewIntentLocked(r.intent);
+ return START_DELIVERED_TO_TOP;
+ }
+ }
+ }
+ }
+
+ } else {
+ if (r.resultTo != null) {
+ sendActivityResultLocked(-1,
+ r.resultTo, r.resultWho, r.requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ return START_CLASS_NOT_FOUND;
+ }
+
+ boolean newTask = false;
+
+ // Should this be considered a new task?
+ if (r.resultTo == null && !addingToTask
+ && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ // todo: should do better management of integers.
+ mService.mCurTask++;
+ if (mService.mCurTask <= 0) {
+ mService.mCurTask = 1;
+ }
+ r.task = new TaskRecord(mService.mCurTask, r.info, intent,
+ (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new task " + r.task);
+ newTask = true;
+ if (mMainStack) {
+ mService.addRecentTaskLocked(r.task);
+ }
+
+ } 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, true);
+ if (top != null) {
+ logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(r.intent);
+ // For paranoia, make sure we have correctly
+ // resumed the top activity.
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return 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.deliverNewIntentLocked(r.intent);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
+ return 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.task = sourceRecord.task;
+ 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 ? (ActivityRecord)mHistory.get(N-1) : null;
+ r.task = prev != null
+ ? prev.task
+ : new TaskRecord(mService.mCurTask, r.info, intent,
+ (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new guessed " + r.task);
+ }
+ if (newTask) {
+ EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
+ }
+ logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
+ startActivityLocked(r, newTask, doResume);
+ return START_SUCCESS;
+ }
+
+ final int startActivityMayWait(IApplicationThread caller,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded,
+ boolean debug, WaitResult outResult, Configuration config) {
+ // 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;
+ try {
+ ResolveInfo rInfo =
+ AppGlobals.getPackageManager().resolveIntent(
+ intent, resolvedType,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS);
+ 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 (debug) {
+ if (!aInfo.processName.equals("system")) {
+ mService.setDebugApp(aInfo.processName, true, false);
+ }
+ }
+ }
+
+ synchronized (mService) {
+ int callingPid;
+ int callingUid;
+ 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_HEAVY_WEIGHT) != 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());
+ return START_PERMISSION_DENIED;
+ }
+ }
+
+ IIntentSender target = mService.getIntentSenderLocked(
+ IActivityManager.INTENT_SENDER_ACTIVITY, "android",
+ realCallingUid, null, null, 0, intent,
+ resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT);
+
+ 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);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+ }
+ }
+ }
+
+ int res = startActivityLocked(caller, intent, resolvedType,
+ grantedUriPermissions, grantedMode, aInfo,
+ resultTo, resultWho, requestCode, callingPid, callingUid,
+ onlyIfNeeded, componentSpecified);
+
+ 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);
+ }
+
+ Binder.restoreCallingIdentity(origId);
+
+ if (outResult != null) {
+ outResult.result = res;
+ if (res == IActivityManager.START_SUCCESS) {
+ mWaitingActivityLaunched.add(outResult);
+ do {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ } else if (res == IActivityManager.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 {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ } while (!outResult.timeout && outResult.who == null);
+ }
+ }
+ }
+
+ return res;
+ }
+ }
+
+ void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
+ long thisTime, long totalTime) {
+ for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
+ WaitResult w = mWaitingActivityLaunched.get(i);
+ w.timeout = timeout;
+ if (r != null) {
+ w.who = new ComponentName(r.info.packageName, r.info.name);
+ }
+ w.thisTime = thisTime;
+ w.totalTime = totalTime;
+ }
+ mService.notifyAll();
+ }
+
+ void reportActivityVisibleLocked(ActivityRecord r) {
+ for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
+ WaitResult w = mWaitingActivityVisible.get(i);
+ w.timeout = false;
+ if (r != null) {
+ w.who = new ComponentName(r.info.packageName, r.info.name);
+ }
+ w.totalTime = SystemClock.uptimeMillis() - w.thisTime;
+ w.thisTime = w.totalTime;
+ }
+ mService.notifyAll();
+ }
+
+ void sendActivityResultLocked(int callingUid, ActivityRecord r,
+ String resultWho, int requestCode, int resultCode, Intent data) {
+
+ if (callingUid > 0) {
+ mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+ data, r);
+ }
+
+ if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+ + " : who=" + resultWho + " req=" + requestCode
+ + " res=" + resultCode + " data=" + data);
+ if (mResumedActivity == r && r.app != null && r.app.thread != null) {
+ try {
+ ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+ list.add(new ResultInfo(resultWho, requestCode,
+ resultCode, data));
+ r.app.thread.scheduleSendResult(r, list);
+ return;
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception thrown sending result to " + r, e);
+ }
+ }
+
+ r.addResultLocked(null, resultWho, requestCode, resultCode, data);
+ }
+
+ private final void stopActivityLocked(ActivityRecord r) {
+ if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
+ if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+ || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
+ if (!r.finishing) {
+ requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,
+ "no-history");
+ }
+ } else if (r.app != null && r.app.thread != null) {
+ if (mMainStack) {
+ if (mService.mFocusedActivity == r) {
+ mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+ }
+ }
+ r.resumeKeyDispatchingLocked();
+ try {
+ r.stopped = false;
+ r.state = ActivityState.STOPPING;
+ if (DEBUG_VISBILITY) Slog.v(
+ TAG, "Stopping visible=" + r.visible + " for " + r);
+ if (!r.visible) {
+ mService.mWindowManager.setAppVisibility(r, false);
+ }
+ r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
+ } catch (Exception e) {
+ // Maybe just ignore exceptions here... if the process
+ // has crashed, our death notification will clean things
+ // up.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ // Just in case, assume it to be stopped.
+ r.stopped = true;
+ r.state = ActivityState.STOPPED;
+ if (r.configDestroy) {
+ destroyActivityLocked(r, true);
+ }
+ }
+ }
+ }
+
+ 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, false);
+ }
+ }
+ if (!s.waitingVisible && 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 activityIdleInternal(IBinder token, boolean fromTimeout,
+ Configuration config) {
+ if (localLOGV) Slog.v(TAG, "Activity idle: " + token);
+
+ ArrayList<ActivityRecord> stops = null;
+ ArrayList<ActivityRecord> finishes = null;
+ ArrayList<ActivityRecord> thumbnails = null;
+ int NS = 0;
+ int NF = 0;
+ int NT = 0;
+ IApplicationThread sendThumbnail = null;
+ boolean booting = false;
+ boolean enableScreen = false;
+
+ synchronized (mService) {
+ if (token != null) {
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);
+ }
+
+ // Get the activity record.
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+
+ 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();
+ }
+
+ // We are now idle. If someone is waiting for a thumbnail from
+ // us, we can now deliver.
+ r.idle = true;
+ mService.scheduleAppGcsLocked();
+ 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 (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 && !fromTimeout) {
+ 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();
+ }
+
+ if (mMainStack) {
+ booting = mService.mBooting;
+ mService.mBooting = false;
+ }
+ }
+
+ int i;
+
+ // 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);
+ }
+ }
+
+ // 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);
+ } else {
+ stopActivityLocked(r);
+ }
+ }
+ }
+
+ // Finish any activities that are scheduled to do so but have been
+ // waiting for the next one to start.
+ for (i=0; i<NF; i++) {
+ ActivityRecord r = (ActivityRecord)finishes.get(i);
+ synchronized (mService) {
+ destroyActivityLocked(r, true);
+ }
+ }
+
+ // 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);
+ }
+
+ if (booting) {
+ mService.finishBooting();
+ }
+
+ mService.trimApplications();
+ //dump();
+ //mWindowManager.dump();
+
+ if (enableScreen) {
+ mService.enableScreenAfterBoot();
+ }
+ }
+
+ /**
+ * @return Returns true if the activity is being finished, false if for
+ * some reason it is being left as-is.
+ */
+ final boolean requestFinishActivityLocked(IBinder token, int resultCode,
+ Intent resultData, String reason) {
+ if (DEBUG_RESULTS) Slog.v(
+ TAG, "Finishing activity: token=" + token
+ + ", result=" + resultCode + ", data=" + resultData);
+
+ int index = indexOfTokenLocked(token);
+ if (index < 0) {
+ return false;
+ }
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+
+ // Is this the last activity left?
+ boolean lastActivity = true;
+ for (int i=mHistory.size()-1; i>=0; i--) {
+ ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ if (!p.finishing && p != r) {
+ lastActivity = false;
+ break;
+ }
+ }
+
+ // If this is the last activity, but it is the home activity, then
+ // just don't finish it.
+ if (lastActivity) {
+ if (r.intent.hasCategory(Intent.CATEGORY_HOME)) {
+ return false;
+ }
+ }
+
+ finishActivityLocked(r, index, resultCode, resultData, reason);
+ return true;
+ }
+
+ /**
+ * @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) {
+ if (r.finishing) {
+ Slog.w(TAG, "Duplicate finish request for " + r);
+ return false;
+ }
+
+ r.finishing = true;
+ EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName, reason);
+ r.task.numActivities--;
+ if (index < (mHistory.size()-1)) {
+ ActivityRecord next = (ActivityRecord)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);
+ }
+ }
+ }
+
+ r.pauseKeyDispatchingLocked();
+ if (mMainStack) {
+ if (mService.mFocusedActivity == r) {
+ mService.setFocusedActivityLocked(topRunningActivityLocked(null));
+ }
+ }
+
+ // send the result
+ ActivityRecord resultTo = r.resultTo;
+ if (resultTo != null) {
+ if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
+ + " who=" + r.resultWho + " req=" + r.requestCode
+ + " res=" + resultCode + " data=" + resultData);
+ if (r.info.applicationInfo.uid > 0) {
+ mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
+ r.packageName, resultData, r);
+ }
+ resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode,
+ resultData);
+ r.resultTo = null;
+ }
+ else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
+
+ // Make sure this HistoryRecord is not holding on to other resources,
+ // because clients have remote IPC references to this object so we
+ // can't assume that will go away and want to avoid circular IPC refs.
+ r.results = null;
+ r.pendingResults = null;
+ r.newIntents = null;
+ r.icicle = null;
+
+ 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);
+ }
+
+ if (mResumedActivity == r) {
+ boolean endTask = index <= 0
+ || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
+ if (DEBUG_TRANSITION) Slog.v(TAG,
+ "Prepare close transition: finishing " + r);
+ mService.mWindowManager.prepareAppTransition(endTask
+ ? WindowManagerPolicy.TRANSIT_TASK_CLOSE
+ : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
+
+ // Tell window manager to prepare for this one to be removed.
+ mService.mWindowManager.setAppVisibility(r, 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");
+ startPausingLocked(false, false);
+ }
+
+ } else if (r.state != ActivityState.PAUSING) {
+ // 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) == null;
+ } else {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
+ }
+
+ return false;
+ }
+
+ private static final int FINISH_IMMEDIATELY = 0;
+ private static final int FINISH_AFTER_PAUSE = 1;
+ private static final int FINISH_AFTER_VISIBLE = 2;
+
+ private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
+ int mode) {
+ final int index = indexOfTokenLocked(r);
+ if (index < 0) {
+ return null;
+ }
+
+ return finishCurrentActivityLocked(r, index, mode);
+ }
+
+ private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
+ int index, int mode) {
+ // 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 we already have a few activities waiting to stop,
+ // then give up on things going idle and start clearing
+ // them out.
+ Message msg = Message.obtain();
+ msg.what = IDLE_NOW_MSG;
+ mHandler.sendMessage(msg);
+ }
+ }
+ r.state = ActivityState.STOPPING;
+ mService.updateOomAdjLocked();
+ return r;
+ }
+
+ // make sure the record is cleaned out of other places.
+ mStoppingActivities.remove(r);
+ mWaitingVisibleActivities.remove(r);
+ if (mResumedActivity == r) {
+ mResumedActivity = null;
+ }
+ final ActivityState prevState = r.state;
+ r.state = ActivityState.FINISHING;
+
+ if (mode == FINISH_IMMEDIATELY
+ || prevState == ActivityState.STOPPED
+ || prevState == ActivityState.INITIALIZING) {
+ // If this activity is already stopped, we can just finish
+ // it right now.
+ return destroyActivityLocked(r, true) ? 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);
+ }
+ return r;
+ }
+
+ /**
+ * Perform the common clean-up of an activity record. This is called both
+ * as part of destroyActivityLocked() (when destroying the client-side
+ * representation) and cleaning things up as a result of its hosting
+ * processing going away, in which case there is no remaining client-side
+ * state to destroy so only the cleanup here is needed.
+ */
+ final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) {
+ if (mResumedActivity == r) {
+ mResumedActivity = null;
+ }
+ if (mService.mFocusedActivity == r) {
+ mService.mFocusedActivity = null;
+ }
+
+ r.configDestroy = false;
+ r.frozenBeforeDestroy = false;
+
+ // Make sure this record is no longer in the pending finishes list.
+ // 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);
+
+ // Remove any pending results.
+ if (r.finishing && r.pendingResults != null) {
+ for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
+ PendingIntentRecord rec = apr.get();
+ if (rec != null) {
+ mService.cancelIntentSenderLocked(rec, false);
+ }
+ }
+ r.pendingResults = null;
+ }
+
+ if (cleanServices) {
+ 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);
+ }
+
+ // Get rid of any pending idle timeouts.
+ mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+ }
+
+ private final void removeActivityFromHistoryLocked(ActivityRecord r) {
+ if (r.state != ActivityState.DESTROYED) {
+ mHistory.remove(r);
+ r.inHistory = false;
+ r.state = ActivityState.DESTROYED;
+ mService.mWindowManager.removeAppToken(r);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+ cleanUpActivityServicesLocked(r);
+ r.removeUriPermissionsLocked();
+ }
+ }
+
+ /**
+ * Perform clean-up of service connections in an activity record.
+ */
+ final void cleanUpActivityServicesLocked(ActivityRecord r) {
+ // Throw away any services that have been bound by this activity.
+ if (r.connections != null) {
+ Iterator<ConnectionRecord> it = r.connections.iterator();
+ while (it.hasNext()) {
+ ConnectionRecord c = it.next();
+ mService.removeConnectionLocked(c, null, r);
+ }
+ r.connections = null;
+ }
+ }
+
+ /**
+ * Destroy the current CLIENT SIDE instance of an activity. This may be
+ * called both when actually finishing an activity, or when performing
+ * a configuration switch where we destroy the current client-side object
+ * but then create a new client-side object for this same HistoryRecord.
+ */
+ final boolean destroyActivityLocked(ActivityRecord r,
+ boolean removeFromApp) {
+ if (DEBUG_SWITCH) Slog.v(
+ TAG, "Removing activity: token=" + r
+ + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+ EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
+ System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+
+ boolean removedFromHistory = false;
+
+ cleanUpActivityLocked(r, false);
+
+ final boolean hadApp = r.app != null;
+
+ if (hadApp) {
+ if (removeFromApp) {
+ int idx = r.app.activities.indexOf(r);
+ if (idx >= 0) {
+ r.app.activities.remove(idx);
+ }
+ if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
+ mService.mHeavyWeightProcess = null;
+ mService.mHandler.sendEmptyMessage(
+ ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
+ if (r.persistent) {
+ mService.decPersistentCountLocked(r.app);
+ }
+ if (r.app.activities.size() == 0) {
+ // No longer have activities, so update location in
+ // LRU list.
+ mService.updateLruProcessLocked(r.app, true, false);
+ }
+ }
+
+ boolean skipDestroy = false;
+
+ try {
+ if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
+ r.app.thread.scheduleDestroyActivity(r, r.finishing,
+ r.configChangeFlags);
+ } catch (Exception e) {
+ // We can just ignore exceptions here... if the process
+ // has crashed, our death notification will clean things
+ // up.
+ //Slog.w(TAG, "Exception thrown during finish", e);
+ if (r.finishing) {
+ removeActivityFromHistoryLocked(r);
+ removedFromHistory = true;
+ skipDestroy = true;
+ }
+ }
+
+ r.app = null;
+ r.nowVisible = false;
+
+ if (r.finishing && !skipDestroy) {
+ r.state = ActivityState.DESTROYING;
+ Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
+ msg.obj = r;
+ mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
+ } else {
+ r.state = ActivityState.DESTROYED;
+ }
+ } else {
+ // remove this record from the history.
+ if (r.finishing) {
+ removeActivityFromHistoryLocked(r);
+ removedFromHistory = true;
+ } else {
+ r.state = ActivityState.DESTROYED;
+ }
+ }
+
+ 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) {
+ mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token);
+
+ int index = indexOfTokenLocked(token);
+ if (index >= 0) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ if (r.state == ActivityState.DESTROYING) {
+ final long origId = Binder.clearCallingIdentity();
+ removeActivityFromHistoryLocked(r);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+ }
+
+ private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) {
+ int i = list.size();
+ if (localLOGV) Slog.v(
+ TAG, "Removing app " + app + " from list " + list
+ + " with " + i + " entries");
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)list.get(i);
+ if (localLOGV) Slog.v(
+ TAG, "Record #" + i + " " + r + ": app=" + r.app);
+ if (r.app == app) {
+ if (localLOGV) Slog.v(TAG, "Removing this entry!");
+ list.remove(i);
+ }
+ }
+ }
+
+ void removeHistoryRecordsForAppLocked(ProcessRecord app) {
+ removeHistoryRecordsForAppLocked(mLRUActivities, app);
+ removeHistoryRecordsForAppLocked(mStoppingActivities, app);
+ removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
+ removeHistoryRecordsForAppLocked(mFinishingActivities, app);
+ }
+
+ final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
+ if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
+
+ final int task = tr.taskId;
+ int top = mHistory.size()-1;
+
+ if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
+ // nothing to do!
+ return;
+ }
+
+ ArrayList moved = new ArrayList();
+
+ // 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 = (ActivityRecord)mHistory.get(pos);
+ if (localLOGV) Slog.v(
+ TAG, "At " + pos + " ckp " + r.task + ": " + r);
+ boolean first = true;
+ if (r.task.taskId == task) {
+ if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
+ mHistory.remove(pos);
+ mHistory.add(top, r);
+ moved.add(0, r);
+ top--;
+ if (first && mMainStack) {
+ mService.addRecentTaskLocked(r.task);
+ first = false;
+ }
+ }
+ pos--;
+ }
+
+ 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(WindowManagerPolicy.TRANSIT_NONE);
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ mNoAnimActivities.add(r);
+ }
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
+ }
+
+ mService.mWindowManager.moveAppTokensToTop(moved);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ finishTaskMoveLocked(task);
+ EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task);
+ }
+
+ private final void finishTaskMoveLocked(int task) {
+ resumeTopActivityLocked(null);
+ }
+
+ /**
+ * Worker method for rearranging history stack. Implements the function of moving all
+ * activities for a specific task (gathering them if disjoint) into a single group at the
+ * bottom of the stack.
+ *
+ * If a watcher is installed, the action is preflighted and the watcher has an opportunity
+ * to premeptively cancel the move.
+ *
+ * @param task The taskId to collect and move to the bottom.
+ * @return Returns true if the move completed, false if not.
+ */
+ 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) {
+ ActivityRecord next = topRunningActivityLocked(null, task);
+ if (next == null) {
+ next = topRunningActivityLocked(null, 0);
+ }
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean moveOK = true;
+ try {
+ moveOK = mService.mController.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ }
+ if (!moveOK) {
+ return false;
+ }
+ }
+ }
+
+ ArrayList moved = new ArrayList();
+
+ 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 = (ActivityRecord)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));
+ mHistory.remove(pos);
+ mHistory.add(bottom, r);
+ moved.add(r);
+ bottom++;
+ }
+ pos++;
+ }
+
+ if (reason != null &&
+ (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null) {
+ mNoAnimActivities.add(r);
+ }
+ } else {
+ mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
+ }
+ mService.mWindowManager.moveAppTokensToBottom(moved);
+ if (VALIDATE_TOKENS) {
+ mService.mWindowManager.validateAppTokens(mHistory);
+ }
+
+ finishTaskMoveLocked(task);
+ return true;
+ }
+
+ private final void logStartActivity(int tag, ActivityRecord r,
+ TaskRecord task) {
+ EventLog.writeEvent(tag,
+ System.identityHashCode(r), task.taskId,
+ r.shortComponentName, r.intent.getAction(),
+ r.intent.getType(), r.intent.getDataString(),
+ r.intent.getFlags());
+ }
+
+ /**
+ * Make sure the given activity matches the current configuration. Returns
+ * false if the activity had to be destroyed. Returns true if the
+ * configuration is the same, or the activity will remain running as-is
+ * for whatever reason. Ensures the HistoryRecord is updated with the
+ * correct configuration and all other bookkeeping is handled.
+ */
+ final boolean ensureActivityConfigurationLocked(ActivityRecord r,
+ int globalChanges) {
+ if (mConfigWillChange) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Skipping config check (will change): " + r);
+ return true;
+ }
+
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Ensuring correct configuration: " + r);
+
+ // Short circuit: if the two configurations are the exact same
+ // object (the common case), then there is nothing to do.
+ Configuration newConfig = mService.mConfiguration;
+ if (r.configuration == newConfig) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration unchanged in " + r);
+ return true;
+ }
+
+ // We don't worry about activities that are finishing.
+ if (r.finishing) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration doesn't matter in finishing " + r);
+ r.stopFreezingScreenLocked(false);
+ return true;
+ }
+
+ // Okay we now are going to make this activity have the new config.
+ // But then we need to figure out how it needs to deal with that.
+ Configuration oldConfig = r.configuration;
+ r.configuration = newConfig;
+
+ // If the activity isn't currently running, just leave the new
+ // configuration and it will pick that up next time it starts.
+ if (r.app == null || r.app.thread == null) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Configuration doesn't matter not running " + r);
+ r.stopFreezingScreenLocked(false);
+ return true;
+ }
+
+ // If the activity isn't persistent, there is a chance we will
+ // need to restart it.
+ if (!r.persistent) {
+
+ // Figure out what has changed between the two configurations.
+ int changes = oldConfig.diff(newConfig);
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
+ + Integer.toHexString(changes) + ", handles=0x"
+ + Integer.toHexString(r.info.configChanges)
+ + ", newConfig=" + newConfig);
+ }
+ if ((changes&(~r.info.configChanges)) != 0) {
+ // Aha, the activity isn't handling the change, so DIE DIE DIE.
+ r.configChangeFlags |= changes;
+ r.startFreezingScreenLocked(r.app, globalChanges);
+ if (r.app == null || r.app.thread == null) {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is destroying non-running " + r);
+ destroyActivityLocked(r, true);
+ } else if (r.state == ActivityState.PAUSING) {
+ // A little annoying: we are waiting for this activity to
+ // finish pausing. Let's not do anything now, but just
+ // flag that it needs to be restarted when done pausing.
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is skipping already pausing " + r);
+ r.configDestroy = true;
+ return true;
+ } else if (r.state == ActivityState.RESUMED) {
+ // Try to optimize this case: the configuration is changing
+ // and we need to restart the top, resumed activity.
+ // Instead of doing the normal handshaking, just say
+ // "restart!".
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is restarting resumed " + r);
+ relaunchActivityLocked(r, r.configChangeFlags, true);
+ r.configChangeFlags = 0;
+ } else {
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
+ "Switch is restarting non-resumed " + r);
+ relaunchActivityLocked(r, r.configChangeFlags, false);
+ r.configChangeFlags = 0;
+ }
+
+ // All done... tell the caller we weren't able to keep this
+ // activity around.
+ return false;
+ }
+ }
+
+ // Default case: the activity can handle this new configuration, so
+ // hand it over. Note that we don't need to give it the new
+ // configuration, since we always send configuration changes to all
+ // process when they happen so it can just use whatever configuration
+ // it last got.
+ if (r.app != null && r.app.thread != null) {
+ try {
+ if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
+ r.app.thread.scheduleActivityConfigurationChanged(r);
+ } catch (RemoteException e) {
+ // If process died, whatever.
+ }
+ }
+ r.stopFreezingScreenLocked(false);
+
+ return true;
+ }
+
+ private final boolean relaunchActivityLocked(ActivityRecord r,
+ int changes, boolean andResume) {
+ List<ResultInfo> results = null;
+ List<Intent> newIntents = null;
+ if (andResume) {
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
+ EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
+ : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r),
+ r.task.taskId, r.shortComponentName);
+
+ r.startFreezingScreenLocked(r.app, 0);
+
+ try {
+ if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
+ r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
+ changes, !andResume, mService.mConfiguration);
+ // Note: don't need to call pauseIfSleepingLocked() here, because
+ // the caller will only pass in 'andResume' if this activity is
+ // currently resumed, which implies we aren't sleeping.
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ if (andResume) {
+ r.results = null;
+ r.newIntents = null;
+ if (mMainStack) {
+ mService.reportResumedActivityLocked(r);
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index e7e9130..7a85eb8 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -218,7 +218,7 @@
}
break;
case IActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
- owner.sendActivityResultLocked(-1, key.activity,
+ key.activity.stack.sendActivityResultLocked(-1, key.activity,
key.who, key.requestCode, code, finalIntent);
break;
case IActivityManager.INTENT_SENDER_BROADCAST:
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 5fd44b1..6e0bc9d 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -83,5 +83,7 @@
int getWifiApEnabledState();
WifiConfiguration getWifiApConfiguration();
+
+ void setWifiApConfiguration(in WifiConfiguration wifiConfig);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 970d5fc..4a22b68 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -824,6 +824,21 @@
}
/**
+ * Sets the Wi-Fi AP Configuration.
+ * @return {@code true} if the operation succeeded, {@code false} otherwise
+ *
+ * @hide Dont open yet
+ */
+ public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
+ try {
+ mService.setWifiApConfiguration(wifiConfig);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
* Acquiring a WifiLock will keep the radio on until the lock is released. Multiple