merge in ics-mr1-release history after reset to ics-mr1
diff --git a/api/current.txt b/api/current.txt
index 808cb6f..4e41c29 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18813,6 +18813,7 @@
method public boolean areDefaultsEnforced();
method public java.lang.String getDefaultEngine();
method public java.util.List<android.speech.tts.TextToSpeech.EngineInfo> getEngines();
+ method public java.util.Set<java.lang.String> getFeatures(java.util.Locale);
method public java.util.Locale getLanguage();
method public int isLanguageAvailable(java.util.Locale);
method public boolean isSpeaking();
@@ -18858,6 +18859,8 @@
field public static final java.lang.String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
field public static final java.lang.String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
field public static final java.lang.String INTENT_ACTION_TTS_SERVICE = "android.intent.action.TTS_SERVICE";
+ field public static final java.lang.String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
+ field public static final java.lang.String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
field public static final java.lang.String KEY_PARAM_PAN = "pan";
field public static final java.lang.String KEY_PARAM_STREAM = "streamType";
field public static final java.lang.String KEY_PARAM_UTTERANCE_ID = "utteranceId";
@@ -18883,6 +18886,7 @@
public abstract class TextToSpeechService extends android.app.Service {
ctor public TextToSpeechService();
method public android.os.IBinder onBind(android.content.Intent);
+ method protected java.util.Set<java.lang.String> onGetFeaturesForLanguage(java.lang.String, java.lang.String, java.lang.String);
method protected abstract java.lang.String[] onGetLanguage();
method protected abstract int onIsLanguageAvailable(java.lang.String, java.lang.String, java.lang.String);
method protected abstract int onLoadLanguage(java.lang.String, java.lang.String, java.lang.String);
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 30aed33..ca66a4e 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -209,6 +209,8 @@
run_command("LIST OF OPEN FILES", 10, "su", "root", "lsof", NULL);
+ for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
+
#ifdef BOARD_HAS_DUMPSTATE
printf("========================================================\n");
printf("== Board\n");
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 597ab1f..6d66b1b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -45,6 +45,9 @@
/* Displays a blocked processes in-kernel wait channel */
void show_wchan(int pid, const char *name);
+/* Runs "showmap" for a process */
+void do_showmap(int pid, const char *name);
+
/* Play a sound via Stagefright */
void play_sound(const char* path);
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index b2f9e80..14984ec 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -96,6 +96,15 @@
return;
}
+void do_showmap(int pid, const char *name) {
+ char title[255];
+ char arg[255];
+
+ sprintf(title, "SHOW MAP %d (%s)", pid, name);
+ sprintf(arg, "%d", pid);
+ run_command(title, 10, "su", "root", "showmap", arg, NULL);
+}
+
/* prints the contents of a file */
int dump_file(const char *title, const char* path) {
char buffer[32768];
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3eb7647..8541748d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -153,10 +153,14 @@
public static final int GET_PERMISSIONS = 0x00001000;
/**
- * Flag parameter to retrieve all applications(even uninstalled ones) with data directories.
- * This state could have resulted if applications have been deleted with flag
- * DONT_DELETE_DATA
- * with a possibility of being replaced or reinstalled in future
+ * Flag parameter to retrieve some information about all applications (even
+ * uninstalled ones) which have data directories. This state could have
+ * resulted if applications have been deleted with flag
+ * {@code DONT_DELETE_DATA} with a possibility of being replaced or
+ * reinstalled in future.
+ * <p>
+ * Note: this flag may cause less information about currently installed
+ * applications to be returned.
*/
public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index be87946..6ecc640 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -105,6 +105,18 @@
void removeRoute(String iface, in RouteInfo route);
/**
+ * Add the specified route to a secondary interface
+ * This will go into a special route table to be accessed
+ * via ip rules
+ */
+ void addSecondaryRoute(String iface, in RouteInfo route);
+
+ /**
+ * Remove the specified secondary route.
+ */
+ void removeSecondaryRoute(String iface, in RouteInfo route);
+
+ /**
* Shuts down the service
*/
void shutdown();
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index cc2fa85..99f58a0 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -116,6 +116,14 @@
private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
/**
+ * Boolean system property to disable strict mode checks outright.
+ * Set this to 'true' to force disable; 'false' has no effect on other
+ * enable/disable policy.
+ * @hide
+ */
+ public static final String DISABLE_PROPERTY = "persist.sys.strictmode.disable";
+
+ /**
* The boolean system property to control screen flashes on violations.
*
* @hide
@@ -891,16 +899,24 @@
* @hide
*/
public static boolean conditionallyEnableDebugLogging() {
- boolean doFlashes = !amTheSystemServerProcess() &&
- SystemProperties.getBoolean(VISUAL_PROPERTY, IS_ENG_BUILD);
+ boolean doFlashes = SystemProperties.getBoolean(VISUAL_PROPERTY, false)
+ && !amTheSystemServerProcess();
+ final boolean suppress = SystemProperties.getBoolean(DISABLE_PROPERTY, false);
// For debug builds, log event loop stalls to dropbox for analysis.
// Similar logic also appears in ActivityThread.java for system apps.
- if (IS_USER_BUILD && !doFlashes) {
+ if (!doFlashes && (IS_USER_BUILD || suppress)) {
setCloseGuardEnabled(false);
return false;
}
+ // Eng builds have flashes on all the time. The suppression property
+ // overrides this, so we force the behavior only after the short-circuit
+ // check above.
+ if (IS_ENG_BUILD) {
+ doFlashes = true;
+ }
+
// Thread policy controls BlockGuard.
int threadPolicyMask = StrictMode.DETECT_DISK_WRITE |
StrictMode.DETECT_DISK_READ |
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 4bc0892..ff28596 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4666,6 +4666,13 @@
* @hide
*/
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/phone_lookup";
+
+ /**
+ * Boolean parameter that is used to look up a SIP address.
+ *
+ * @hide
+ */
+ public static final String QUERY_PARAMETER_SIP_ADDRESS = "sip";
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5754e60..a0652f7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3620,6 +3620,13 @@
"pdp_watchdog_max_pdp_reset_fail_count";
/**
+ * The number of milliseconds to delay when checking for data stalls
+ * @hide
+ */
+ public static final String DATA_STALL_ALARM_DELAY_IN_MS =
+ "data_stall_alarm_delay_in_ms";
+
+ /**
* The interval in milliseconds at which to check gprs registration
* after the first registration mismatch of gprs and voice service,
* to detect possible data network registration problems.
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
index ff3fa11..1a8c1fb 100644
--- a/core/java/android/speech/tts/ITextToSpeechService.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -114,6 +114,21 @@
int isLanguageAvailable(in String lang, in String country, in String variant);
/**
+ * Returns a list of features available for a given language. Elements of the returned
+ * string array can be passed in as keys to {@link TextToSpeech#speak} and
+ * {@link TextToSpeech#synthesizeToFile} to select a given feature or features to be
+ * used during synthesis.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return An array of strings containing the set of features supported for
+ * the supplied locale. The array of strings must not contain
+ * duplicates.
+ */
+ String[] getFeaturesForLanguage(in String lang, in String country, in String variant);
+
+ /**
* Notifies the engine that it should load a speech synthesis language.
*
* @param lang ISO-3 language code.
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 98ab310..954a6fe 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -31,10 +31,13 @@
import android.text.TextUtils;
import android.util.Log;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
/**
*
@@ -147,7 +150,25 @@
}
/**
- * Constants and parameter names for controlling text-to-speech.
+ * Constants and parameter names for controlling text-to-speech. These include:
+ *
+ * <ul>
+ * <li>
+ * Intents to ask engine to install data or check its data and
+ * extras for a TTS engine's check data activity.
+ * </li>
+ * <li>
+ * Keys for the parameters passed with speak commands, e.g.
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID}, {@link Engine#KEY_PARAM_STREAM}.
+ * </li>
+ * <li>
+ * A list of feature strings that engines might support, e.g
+ * {@link Engine#KEY_FEATURE_NETWORK_SYNTHESIS}). These values may be passed in to
+ * {@link TextToSpeech#speak} and {@link TextToSpeech#synthesizeToFile} to modify
+ * engine behaviour. The engine can be queried for the set of features it supports
+ * through {@link TextToSpeech#getFeatures(java.util.Locale)}.
+ * </li>
+ * </ul>
*/
public class Engine {
@@ -435,6 +456,25 @@
*/
public static final String KEY_PARAM_PAN = "pan";
+ /**
+ * Feature key for network synthesis. See {@link TextToSpeech#getFeatures(Locale)}
+ * for a description of how feature keys work. If set (and supported by the engine
+ * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must
+ * use network based synthesis.
+ *
+ * @see TextToSpeech#speak(String, int, java.util.HashMap)
+ * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
+ * @see TextToSpeech#getFeatures(java.util.Locale)
+ */
+ public static final String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
+
+ /**
+ * Feature key for embedded synthesis. See {@link TextToSpeech#getFeatures(Locale)}
+ * for a description of how feature keys work. If set and supported by the engine
+ * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must synthesize
+ * text on-device (without making network requests).
+ */
+ public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
}
private final Context mContext;
@@ -812,6 +852,36 @@
}
/**
+ * Queries the engine for the set of features it supports for a given locale.
+ * Features can either be framework defined, e.g.
+ * {@link TextToSpeech.Engine#KEY_FEATURE_NETWORK_SYNTHESIS} or engine specific.
+ * Engine specific keys must be prefixed by the name of the engine they
+ * are intended for. These keys can be used as parameters to
+ * {@link TextToSpeech#speak(String, int, java.util.HashMap)} and
+ * {@link TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)}.
+ *
+ * Features are boolean flags, and their values in the synthesis parameters
+ * must be behave as per {@link Boolean#parseBoolean(String)}.
+ *
+ * @param locale The locale to query features for.
+ */
+ public Set<String> getFeatures(final Locale locale) {
+ return runAction(new Action<Set<String>>() {
+ @Override
+ public Set<String> run(ITextToSpeechService service) throws RemoteException {
+ String[] features = service.getFeaturesForLanguage(
+ locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
+ if (features != null) {
+ final Set<String> featureSet = new HashSet<String>();
+ Collections.addAll(featureSet, features);
+ return featureSet;
+ }
+ return null;
+ }
+ }, null, "getFeatures");
+ }
+
+ /**
* Checks whether the TTS engine is busy speaking. Note that a speech item is
* considered complete once it's audio data has been sent to the audio mixer, or
* written to a file. There might be a finite lag between this point, and when
@@ -1017,6 +1087,9 @@
copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
+ // Copy feature strings defined by the framework.
+ copyStringParam(bundle, params, Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
+
// Copy over all parameters that start with the name of the
// engine that we are currently connected to. The engine is
// free to interpret them as it chooses.
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 48739ba..245271d 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -36,6 +36,7 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
+import java.util.Set;
/**
@@ -67,7 +68,6 @@
* any. Any pending data from the current synthesis will be discarded.
*
*/
-// TODO: Add a link to the sample TTS engine once it's done.
public abstract class TextToSpeechService extends Service {
private static final boolean DBG = false;
@@ -196,6 +196,18 @@
protected abstract void onSynthesizeText(SynthesisRequest request,
SynthesisCallback callback);
+ /**
+ * Queries the service for a set of features supported for a given language.
+ *
+ * @param lang ISO-3 language code.
+ * @param country ISO-3 country code. May be empty or null.
+ * @param variant Language variant. May be empty or null.
+ * @return A list of features supported for the given language.
+ */
+ protected Set<String> onGetFeaturesForLanguage(String lang, String country, String variant) {
+ return null;
+ }
+
private int getDefaultSpeechRate() {
return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE);
}
@@ -778,6 +790,13 @@
return onIsLanguageAvailable(lang, country, variant);
}
+ public String[] getFeaturesForLanguage(String lang, String country, String variant) {
+ Set<String> features = onGetFeaturesForLanguage(lang, country, variant);
+ String[] featuresArray = new String[features.size()];
+ features.toArray(featuresArray);
+ return featuresArray;
+ }
+
/*
* There is no point loading a non default language if defaults
* are enforced.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 24423c3..f7078ec 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -218,6 +218,7 @@
boolean mNewSurfaceNeeded;
boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
+ InputEventMessage mPendingInputEvents = null;
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -832,10 +833,24 @@
}
}
+ private void processInputEvents(boolean outOfOrder) {
+ while (mPendingInputEvents != null) {
+ handleMessage(mPendingInputEvents.mMessage);
+ InputEventMessage tmpMessage = mPendingInputEvents;
+ mPendingInputEvents = mPendingInputEvents.mNext;
+ tmpMessage.recycle();
+ if (outOfOrder) {
+ removeMessages(PROCESS_INPUT_EVENTS);
+ }
+ }
+ }
+
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
+ processInputEvents(true);
+
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
@@ -2336,6 +2351,7 @@
public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021;
public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022;
public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023;
+ public final static int PROCESS_INPUT_EVENTS = 1024;
@Override
public String getMessageName(Message message) {
@@ -2388,7 +2404,9 @@
return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT:
return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT";
-
+ case PROCESS_INPUT_EVENTS:
+ return "PROCESS_INPUT_EVENTS";
+
}
return super.getMessageName(message);
}
@@ -2447,6 +2465,9 @@
case DISPATCH_GENERIC_MOTION:
deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0);
break;
+ case PROCESS_INPUT_EVENTS:
+ processInputEvents(false);
+ break;
case DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
break;
@@ -3744,7 +3765,7 @@
msg.obj = ri;
sendMessage(msg);
}
-
+
private long mInputEventReceiveTimeNanos;
private long mInputEventDeliverTimeNanos;
private long mInputEventDeliverPostImeTimeNanos;
@@ -3762,6 +3783,78 @@
}
};
+ /**
+ * Utility class used to queue up input events which are then handled during
+ * performTraversals(). Doing it this way allows us to ensure that we are up to date with
+ * all input events just prior to drawing, instead of placing those events on the regular
+ * handler queue, potentially behind a drawing event.
+ */
+ static class InputEventMessage {
+ Message mMessage;
+ InputEventMessage mNext;
+
+ private static final Object sPoolSync = new Object();
+ private static InputEventMessage sPool;
+ private static int sPoolSize = 0;
+
+ private static final int MAX_POOL_SIZE = 10;
+
+ private InputEventMessage(Message m) {
+ mMessage = m;
+ mNext = null;
+ }
+
+ /**
+ * Return a new Message instance from the global pool. Allows us to
+ * avoid allocating new objects in many cases.
+ */
+ public static InputEventMessage obtain(Message msg) {
+ synchronized (sPoolSync) {
+ if (sPool != null) {
+ InputEventMessage m = sPool;
+ sPool = m.mNext;
+ m.mNext = null;
+ sPoolSize--;
+ m.mMessage = msg;
+ return m;
+ }
+ }
+ return new InputEventMessage(msg);
+ }
+
+ /**
+ * Return the message to the pool.
+ */
+ public void recycle() {
+ mMessage.recycle();
+ synchronized (sPoolSync) {
+ if (sPoolSize < MAX_POOL_SIZE) {
+ mNext = sPool;
+ sPool = this;
+ sPoolSize++;
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Place the input event message at the end of the current pending list
+ */
+ private void enqueueInputEvent(Message msg, long when) {
+ InputEventMessage inputMessage = InputEventMessage.obtain(msg);
+ if (mPendingInputEvents == null) {
+ mPendingInputEvents = inputMessage;
+ } else {
+ InputEventMessage currMessage = mPendingInputEvents;
+ while (currMessage.mNext != null) {
+ currMessage = currMessage.mNext;
+ }
+ currMessage.mNext = inputMessage;
+ }
+ sendEmptyMessageAtTime(PROCESS_INPUT_EVENTS, when);
+ }
+
public void dispatchKey(KeyEvent event) {
dispatchKey(event, false);
}
@@ -3786,7 +3879,7 @@
if (LOCAL_LOGV) Log.v(
TAG, "sending key " + event + " to " + mView);
- sendMessageAtTime(msg, event.getEventTime());
+ enqueueInputEvent(msg, event.getEventTime());
}
private void dispatchMotion(MotionEvent event, boolean sendDone) {
@@ -3804,21 +3897,21 @@
Message msg = obtainMessage(DISPATCH_POINTER);
msg.obj = event;
msg.arg1 = sendDone ? 1 : 0;
- sendMessageAtTime(msg, event.getEventTime());
+ enqueueInputEvent(msg, event.getEventTime());
}
private void dispatchTrackball(MotionEvent event, boolean sendDone) {
Message msg = obtainMessage(DISPATCH_TRACKBALL);
msg.obj = event;
msg.arg1 = sendDone ? 1 : 0;
- sendMessageAtTime(msg, event.getEventTime());
+ enqueueInputEvent(msg, event.getEventTime());
}
private void dispatchGenericMotion(MotionEvent event, boolean sendDone) {
Message msg = obtainMessage(DISPATCH_GENERIC_MOTION);
msg.obj = event;
msg.arg1 = sendDone ? 1 : 0;
- sendMessageAtTime(msg, event.getEventTime());
+ enqueueInputEvent(msg, event.getEventTime());
}
public void dispatchAppVisibility(boolean visible) {
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
index 42581c2..fe5908e 100644
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ b/core/java/android/webkit/HTML5VideoInline.java
@@ -74,11 +74,13 @@
public SurfaceTexture getSurfaceTexture(int videoLayerId) {
// Create the surface texture.
if (videoLayerId != mVideoLayerUsingSurfaceTexture
- || mSurfaceTexture == null) {
- if (mTextureNames == null) {
- mTextureNames = new int[1];
- GLES20.glGenTextures(1, mTextureNames, 0);
+ || mSurfaceTexture == null
+ || mTextureNames == null) {
+ if (mTextureNames != null) {
+ GLES20.glDeleteTextures(1, mTextureNames, 0);
}
+ mTextureNames = new int[1];
+ GLES20.glGenTextures(1, mTextureNames, 0);
mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
}
mVideoLayerUsingSurfaceTexture = videoLayerId;
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 399d217..e84ae97 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -151,6 +151,14 @@
}
};
+ private Runnable mReleaseCursorRunnable = new Runnable() {
+ public void run() {
+ if (mSuggestionsAdapter != null && mSuggestionsAdapter instanceof SuggestionsAdapter) {
+ mSuggestionsAdapter.changeCursor(null);
+ }
+ }
+ };
+
// For voice searching
private final Intent mVoiceWebSearchIntent;
private final Intent mVoiceAppSearchIntent;
@@ -759,6 +767,7 @@
@Override
protected void onDetachedFromWindow() {
removeCallbacks(mUpdateDrawableStateRunnable);
+ post(mReleaseCursorRunnable);
super.onDetachedFromWindow();
}
@@ -1028,7 +1037,9 @@
}
}
mQueryTextView.setInputType(inputType);
-
+ if (mSuggestionsAdapter != null) {
+ mSuggestionsAdapter.changeCursor(null);
+ }
// attach the suggestions adapter, if suggestions are available
// The existence of a suggestions authority is the proxy for "suggestions available here"
if (mSearchable.getSuggestAuthority() != null) {
@@ -1177,7 +1188,6 @@
public void onActionViewCollapsed() {
clearFocus();
updateViewsVisibility(true);
- mQueryTextView.setText("");
mQueryTextView.setImeOptions(mCollapsedImeOptions);
mExpandedInActionView = false;
}
@@ -1192,6 +1202,7 @@
mExpandedInActionView = true;
mCollapsedImeOptions = mQueryTextView.getImeOptions();
mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
+ mQueryTextView.setText("");
setIconified(false);
}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 5fbbe4d..e929e7d 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -84,9 +84,14 @@
private void setLocale(Locale locale) {
final TextServicesManager textServicesManager = (TextServicesManager)
mTextView.getContext().getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
- mSpellCheckerSession = textServicesManager.newSpellCheckerSession(
- null /* Bundle not currently used by the textServicesManager */,
- locale, this, false /* means any available languages from current spell checker */);
+ if (!textServicesManager.isSpellCheckerEnabled()) {
+ mSpellCheckerSession = null;
+ } else {
+ mSpellCheckerSession = textServicesManager.newSpellCheckerSession(
+ null /* Bundle not currently used by the textServicesManager */,
+ locale, this,
+ false /* means any available languages from current spell checker */);
+ }
mCurrentLocale = locale;
// Restore SpellCheckSpans in pool
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
index 9e32c9a..c44d431 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -29,9 +29,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.Cursor;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.text.Spannable;
@@ -39,7 +37,6 @@
import android.text.TextUtils;
import android.text.style.TextAppearanceSpan;
import android.util.Log;
-import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
@@ -113,7 +110,6 @@
mOutsideDrawablesCache = outsideDrawablesCache;
-
// mStartSpinnerRunnable = new Runnable() {
// public void run() {
// // mSearchView.setWorking(true); // TODO:
@@ -185,6 +181,10 @@
* the results.
*/
Cursor cursor = null;
+ if (mSearchView.getVisibility() != View.VISIBLE
+ || mSearchView.getWindowVisibility() != View.VISIBLE) {
+ return null;
+ }
//mSearchView.getWindow().getDecorView().post(mStartSpinnerRunnable); // TODO:
try {
cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml
index f699a46..ca1dc88 100644
--- a/core/res/res/layout/search_view.xml
+++ b/core/res/res/layout/search_view.xml
@@ -93,7 +93,7 @@
android:singleLine="true"
android:ellipsize="end"
android:background="@null"
- android:inputType="text|textAutoComplete"
+ android:inputType="text|textAutoComplete|textNoSuggestions"
android:imeOptions="actionSearch"
android:dropDownHeight="wrap_content"
android:dropDownAnchor="@id/search_edit_frame"
diff --git a/docs/html/guide/topics/admin/device-admin.jd b/docs/html/guide/topics/admin/device-admin.jd
index 7bbf5e6..820c3c0 100644
--- a/docs/html/guide/topics/admin/device-admin.jd
+++ b/docs/html/guide/topics/admin/device-admin.jd
@@ -27,6 +27,12 @@
<li>{@link android.app.admin.DevicePolicyManager}</li>
<li>{@link android.app.admin.DeviceAdminInfo}</li>
</ol>
+ <h2>Related samples</h2>
+ <ol>
+ <li><a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
+DeviceAdminSample</a></li>
+</ol>
</div>
</div>
@@ -201,6 +207,16 @@
<td>Specifies that the storage area should be encrypted, if the device supports it.
Introduced in Android 3.0.</td> </tr>
+<tr>
+ <td>Disable camera</td>
+
+ <td>Specifies that the camera should be disabled. Note that this doesn't have
+to be a permanent disabling. The camera can be enabled/disabled dynamically
+based on context, time, and so on. Introduced in Android 4.0.</td>
+
+</tr>
+
+
</table>
<h4>Other features</h4>
@@ -247,6 +263,7 @@
locks.</li>
<li>Make the device lock immediately.</li>
<li>Wipe the device's data (that is, restore factory settings).</li>
+ <li>Disable the camera.</li>
</ul>
@@ -280,46 +297,38 @@
<li>A declaration of security policies used in metadata.</li>
</ul>
<p>Here is an excerpt from the Device Administration sample manifest:</p>
-<pre><activity android:name=".app.DeviceAdminSample$Controller"
- android:label="@string/activity_sample_device_admin">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.SAMPLE_CODE" />
- </intent-filter>
+<pre><activity android:name=".app.DeviceAdminSample"
+ android:label="@string/activity_sample_device_admin">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
</activity>
-
-<receiver android:name=".app.DeviceAdminSample"
- android:label="@string/sample_device_admin"
- android:description="@string/sample_device_admin_description"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
- <meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
- <intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
- </intent-filter>
+<receiver android:name=".app.DeviceAdminSample$DeviceAdminSampleReceiver"
+ android:label="@string/sample_device_admin"
+ android:description="@string/sample_device_admin_description"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_sample" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
</receiver></pre>
<p>Note that:</p>
<ul>
- <li>The activity in the sample application is an {@link android.app.Activity}
-subclass called <code>Controller</code>. The syntax
-<code>".app.DeviceAdminSample$Controller"</code> indicates that
-<code>Controller</code> is an inner class that is nested inside the
-<code>DeviceAdminSample</code> class. Note that an Activity does not need to be
-an inner class; it just is in this example.</li>
-
<li>The following attributes refer to string resources that for the sample application reside in
<code>ApiDemos/res/values/strings.xml</code>. For more information about resources, see
<a
href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.
<ul>
-<li><code>android:label="@string/activity_sample_device_admin"</code> refers to the
+<li><code>android:label="@string/activity_sample_device_admin"</code> refers to the
user-readable label for the activity.</li>
-<li><code>android:label="@string/sample_device_admin"</code> refers to the
+<li><code>android:label="@string/sample_device_admin"</code> refers to the
user-readable label for the permission.</li>
-<li><code>android:description="@string/sample_device_admin_description"</code> refers to
+<li><code>android:description="@string/sample_device_admin_description"</code> refers to
the user-readable description of the permission. A descripton is typically longer and more
informative than
a label.</li>
@@ -357,6 +366,9 @@
<reset-password />
<force-lock />
<wipe-data />
+ <expire-password />
+ <encrypted-storage />
+ <disable-camera />
</uses-policies>
</device-admin>
</pre>
@@ -401,33 +413,34 @@
events. For example:</p>
<pre>public class DeviceAdminSample extends DeviceAdminReceiver {
-...
+ void showToast(Context context, String msg) {
+ String status = context.getString(R.string.admin_receiver_status, msg);
+ Toast.makeText(context, status, Toast.LENGTH_SHORT).show();
+ }
+
@Override
- public void onEnabled(Context context, Intent intent) {
- showToast(context, "Sample Device Admin: enabled");
- }
+ public void onEnabled(Context context, Intent intent) {
+ showToast(context, context.getString(R.string.admin_receiver_status_enabled));
+ }
- @Override
- public CharSequence onDisableRequested(Context context, Intent intent) {
- return "This is an optional message to warn the user about disabling.";
- }
+ @Override
+ public CharSequence onDisableRequested(Context context, Intent intent) {
+ return context.getString(R.string.admin_receiver_status_disable_warning);
+ }
- @Override
- public void onDisabled(Context context, Intent intent) {
- showToast(context, "Sample Device Admin: disabled");
- }
+ @Override
+ public void onDisabled(Context context, Intent intent) {
+ showToast(context, context.getString(R.string.admin_receiver_status_disabled));
+ }
- @Override
- public void onPasswordChanged(Context context, Intent intent) {
- showToast(context, "Sample Device Admin: pw changed");
- }
-
- void showToast(Context context, CharSequence msg) {
- Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
+ @Override
+ public void onPasswordChanged(Context context, Intent intent) {
+ showToast(context, context.getString(R.string.admin_receiver_status_pw_changed));
}
...
}</pre>
+
<h4 id="enabling">Enabling the application</h4>
<p>One of the major events a device admin application has to handle is the user
enabling the application. The user must explicitly enable the application for
@@ -438,43 +451,50 @@
action that triggers the {@link android.app.admin.DevicePolicyManager#ACTION_ADD_DEVICE_ADMIN}
intent. In the
sample application, this happens when the user clicks the <strong>Enable
-Admin</strong> button. </p>
-<p>When the user clicks the <strong>Enable Admin</strong> button, the display
-changes to prompt the user to enable the device admin application, as shown in figure
+Admin</strong> checkbox. </p>
+<p>When the user clicks the <strong>Enable Admin</strong> checkbox, the display
+changes to prompt the user to activate the device admin application, as shown in figure
2.</p>
<img src="{@docRoot}images/admin/device-admin-activate-prompt.png"/>
<p class="img-caption"><strong>Figure 2.</strong> Sample Application: Activating the Application</p>
-<p>Below is the code that gets executed when the user clicks the <strong>Enable
-Admin</strong> button shown in figure 1. </p>
-<pre> private OnClickListener mEnableListener = new OnClickListener() {
- public void onClick(View v) {
- // Launch the activity to have the user enable our admin.
- Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
- intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- mDeviceAdminSample);
- intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
- "Additional text explaining why this needs to be added.");
- startActivityForResult(intent, RESULT_ENABLE);
- }
-};
+<p>Below is the code that gets executed when the user clicks the <strong>Enable Admin</strong> checkbox. This has the effect of triggering the
+{@link android.preference.Preference.OnPreferenceChangeListener#onPreferenceChange(android.preference.Preference, java.lang.Object) onPreferenceChange()}
+callback. This callback is invoked when the value of this {@link android.preference.Preference} has been changed by the user and is about to be set and/or persisted. If the user is enabling the application, the display
+changes to prompt the user to activate the device admin application, as shown in figure
+2. Otherwise, the device admin application is disabled. </p>
-...
-// This code checks whether the device admin app was successfully enabled.
-@Override
-protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case RESULT_ENABLE:
- if (resultCode == Activity.RESULT_OK) {
- Log.i("DeviceAdminSample", "Administration enabled!");
- } else {
- Log.i("DeviceAdminSample", "Administration enable FAILED!");
- }
- return;
- }
- super.onActivityResult(requestCode, resultCode, data);
-}</pre>
+<pre>@Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (super.onPreferenceChange(preference, newValue)) {
+ return true;
+ }
+ boolean value = (Boolean) newValue;
+ if (preference == mEnableCheckbox) {
+ if (value != mAdminActive) {
+ if (value) {
+ // Launch the activity to have the user enable our admin.
+ Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
+ intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdminSample);
+ intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
+ mActivity.getString(R.string.add_admin_extra_app_text));
+ startActivityForResult(intent, REQUEST_CODE_ENABLE_ADMIN);
+ // return false - don't update checkbox until we're really active
+ return false;
+ } else {
+ mDPM.removeActiveAdmin(mDeviceAdminSample);
+ enableDeviceCapabilitiesArea(false);
+ mAdminActive = false;
+ }
+ }
+ } else if (preference == mDisableCameraCheckbox) {
+ mDPM.setCameraDisabled(mDeviceAdminSample, value);
+ ...
+ }
+ return true;
+ }</pre>
+
<p>The line
<code>intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
@@ -489,18 +509,17 @@
{@link android.app.admin.DevicePolicyManager#isAdminActive(android.content.ComponentName) isAdminActive()}. Notice that the {@link android.app.admin.DevicePolicyManager}
method {@link android.app.admin.DevicePolicyManager#isAdminActive(android.content.ComponentName) isAdminActive()} takes a {@link android.app.admin.DeviceAdminReceiver}
component as its argument:</p>
+
<pre>
DevicePolicyManager mDPM;
...
-boolean active = mDPM.isAdminActive(mDeviceAdminSample);
-if (active) {
- // Admin app is active, so do some admin stuff
- ...
-} else {
- // do something else
+private boolean isActiveAdmin() {
+ return mDPM.isAdminActive(mDeviceAdminSample);
}
</pre>
+
+
<h3 id="admin_ops">Managing policies</h3>
<p>{@link android.app.admin.DevicePolicyManager} is a public class for managing policies
enforced on a device. {@link android.app.admin.DevicePolicyManager} manages policies for one
@@ -618,49 +637,6 @@
...
mDPM.setPasswordExpirationTimeout(mDeviceAdminSample, pwExpiration);
</pre>
-
-<p>From the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"
->Device Administration API sample</a>, here is the code
-that updates the password expiration status:</p>
-
-<pre>
-DevicePolicyManager mDPM;
-ComponentName mDeviceAdminSample;
-private TextView mPasswordExpirationStatus;
-...
-void updatePasswordExpirationStatus() {
- boolean active = mDPM.isAdminActive(mDeviceAdminSample);
- String statusText;
- if (active) {
- long now = System.currentTimeMillis();
- // Query the DevicePolicyManager twice - first for the expiration values
- // set by the sample app, and later, for the system values (which may be different
- // if there is another administrator active.)
- long expirationDate = mDPM.getPasswordExpiration(mDeviceAdminSample);
- long mSecUntilExpiration = expirationDate - now;
- if (mSecUntilExpiration >= 0) {
- statusText = "Expiration in " + countdownString(mSecUntilExpiration);
- } else {
- statusText = "Expired " + countdownString(-mSecUntilExpiration) + " ago";
- }
-
- // expirationTimeout is the cycle time between required password refresh
- long expirationTimeout = mDPM.getPasswordExpirationTimeout(mDeviceAdminSample);
- statusText += " / timeout period " + countdownString(expirationTimeout);
-
- // Now report the aggregate (global) expiration time
- statusText += " / Aggregate ";
- expirationDate = mDPM.getPasswordExpiration(null);
- mSecUntilExpiration = expirationDate - now;
- if (mSecUntilExpiration >= 0) {
- statusText += "expiration in " + countdownString(mSecUntilExpiration);
- } else {
- statusText += "expired " + countdownString(-mSecUntilExpiration) + " ago";
- }
- } else {
- statusText = "<inactive>";
- }
- mPasswordExpirationStatus.setText(statusText);</pre>
<h5 id="history">Restrict password based on history</h5>
@@ -718,6 +694,19 @@
<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its parameter a bit mask of
additional options. Currently the value must be 0. </p>
+<h4>Disable camera</h4>
+<p>Beginning with Android 4.0, you can disable the camera. Note that this doesn't have to be a permanent disabling. The camera can be enabled/disabled dynamically based on context, time, and so on. </p>
+<p>You control whether the camera is disabled by using the
+{@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean) setCameraDisabled()} method. For example, this snippet sets the camera to be enabled or disabled based on a checkbox setting:</p>
+
+<pre>private CheckBoxPreference mDisableCameraCheckbox;
+DevicePolicyManager mDPM;
+ComponentName mDeviceAdminSample;
+...
+mDPM.setCameraDisabled(mDeviceAdminSample, mDisableCameraCheckbox.isChecked());<br />
+</pre>
+
+
<h4 id=storage">Storage encryption</h4>
<p>Beginning with Android 3.0, you can use the
{@link android.app.admin.DevicePolicyManager#setStorageEncryption(android.content.ComponentName,boolean) setStorageEncryption()}
diff --git a/docs/html/images/admin/device-admin-activate-prompt.png b/docs/html/images/admin/device-admin-activate-prompt.png
index 2851194..3786788 100644
--- a/docs/html/images/admin/device-admin-activate-prompt.png
+++ b/docs/html/images/admin/device-admin-activate-prompt.png
Binary files differ
diff --git a/docs/html/images/admin/device-admin-app.png b/docs/html/images/admin/device-admin-app.png
index c96defc..6b23aba 100644
--- a/docs/html/images/admin/device-admin-app.png
+++ b/docs/html/images/admin/device-admin-app.png
Binary files differ
diff --git a/docs/html/resources/dashboard/opengl.jd b/docs/html/resources/dashboard/opengl.jd
index 9089937..07a0e43 100644
--- a/docs/html/resources/dashboard/opengl.jd
+++ b/docs/html/resources/dashboard/opengl.jd
@@ -57,7 +57,7 @@
<div class="dashboard-panel">
<img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1|GL%202.0%20%26%201.1&chd=t%3A9.2,90.8" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1|GL%202.0%20%26%201.1&chd=t%3A9.8,90.2" />
<table>
<tr>
@@ -66,14 +66,14 @@
</tr>
<tr>
<td>1.1</th>
-<td>9.2%</td>
+<td>9.8%</td>
</tr>
<tr>
<td>2.0</th>
-<td>90.8%</td>
+<td>90.2%</td>
</tr>
</table>
-<p><em>Data collected during a 7-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 7-day period ending on November 3, 2011</em></p>
</div>
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 135c6f2..8041096 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
<div class="dashboard-panel">
<img alt="" height="250" width="470"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,1.4,11.7,45.3,0.5,38.2,0.2,0.9,0.7&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.9,1.4,10.7,40.7,0.5,43.9,0.1,0.9,0.9&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2&chco=c4df9b,6fad0c" />
<table>
<tr>
@@ -61,21 +61,21 @@
<th>API Level</th>
<th>Distribution</th>
</tr>
-<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td> <td>3</td><td>1.1%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td> <td>3</td><td>0.9%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-1.6.html">Android 1.6</a></td><td>Donut</td> <td>4</td><td>1.4%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td> <td>7</td><td>11.7%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td> <td>8</td><td>45.3%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td> <td>7</td><td>10.7%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td> <td>8</td><td>40.7%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-2.3.html">Android 2.3 -<br/>
Android 2.3.2</a></td><td rowspan="2">Gingerbread</td> <td>9</td><td>0.5%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-2.3.3.html">Android 2.3.3 -<br/>
- Android 2.3.7</a></td><!-- Gingerbread --> <td>10</td><td>38.2%</td></tr>
+ Android 2.3.7</a></td><!-- Gingerbread --> <td>10</td><td>43.9%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-3.0.html">Android 3.0</a></td>
- <td rowspan="3">Honeycomb</td> <td>11</td><td>0.2%</td></tr>
+ <td rowspan="3">Honeycomb</td> <td>11</td><td>0.1%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-3.1.html">Android 3.1</a></td><!-- Honeycomb --><td>12</td><td>0.9%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>0.7%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>0.9%</td></tr>
</table>
-<p><em>Data collected during a 14-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 14-day period ending on November 3, 2011</em></p>
<!--
<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
-->
@@ -104,9 +104,9 @@
<div class="dashboard-panel">
<img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.7,99.6,99.6,99.5,99.4,99.3,99.2,99.0,98.8,98.7,98.5,98.5,98.2|97.0,97.1,97.3,97.5,97.5,97.5,97.7,97.6,97.5,97.5,97.5,97.5,97.1|93.5,93.9,94.3,94.8,95.0,95.2,95.5,95.5,95.5,95.6,95.7,95.8,95.6|66.4,68.0,69.8,71.5,73.9,75.4,77.6,79.0,80.2,81.1,82.4,83.3,83.8|2.5,3.1,4.0,6.1,9.5,13.6,17.8,20.6,24.3,27.5,31.2,34.7,38.3|1.7,2.2,3.0,5.1,8.4,12.6,16.8,20.0,23.7,26.9,30.6,34.1,37.8&chm=b,c3df9b,0,1,0|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|tAndroid 2.3.3,131d02,5,5,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/01%7C10/15%7C11/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.6,99.5,99.4,99.3,99.2,99.0,98.8,98.7,98.5,98.5,98.2,98.1,98.0|97.3,97.5,97.5,97.5,97.7,97.6,97.5,97.5,97.5,97.5,97.1,97.1,97.1|94.3,94.8,95.0,95.2,95.5,95.5,95.5,95.6,95.7,95.8,95.6,95.9,95.7|69.8,71.5,73.9,75.4,77.6,79.0,80.2,81.1,82.4,83.3,83.8,84.9,85.0|4.0,6.1,9.5,13.6,17.8,20.6,24.3,27.5,31.2,34.7,38.3,41.3,44.0|3.0,5.1,8.4,12.6,16.8,20.0,23.7,26.9,30.6,34.1,37.8,40.8,43.5&chm=b,c3df9b,0,1,0|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|tAndroid 2.3.3,131d02,5,3,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
-<p><em>Last historical dataset collected during a 14-day period ending on October 3, 2011</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on November 3, 2011</em></p>
</div><!-- end dashboard-panel -->
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index 67b47d0..ec3034d 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -60,7 +60,7 @@
<div class="dashboard-panel">
<img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A2.6,0.1,3.0,71.9,0.9,17.6,2.7,1.2" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A2.9,0.1,3.1,70.8,1.0,17.7,3.0,1.3" />
<table>
<tr>
@@ -71,31 +71,31 @@
<th scope="col">xhdpi</th>
</tr>
<tr><th scope="row">small</th>
-<td>1.2%</td> <!-- small/ldpi -->
+<td>1.3%</td> <!-- small/ldpi -->
<td></td> <!-- small/mdpi -->
-<td>2.7%</td> <!-- small/hdpi -->
+<td>3.0%</td> <!-- small/hdpi -->
<td></td> <!-- small/xhdpi -->
</tr>
<tr><th scope="row">normal</th>
-<td>0.9%</td> <!-- normal/ldpi -->
-<td>17.6%</td> <!-- normal/mdpi -->
-<td>71.9%</td> <!-- normal/hdpi -->
+<td>1.0%</td> <!-- normal/ldpi -->
+<td>17.7%</td> <!-- normal/mdpi -->
+<td>70.8%</td> <!-- normal/hdpi -->
<td></td> <!-- normal/xhdpi -->
</tr>
<tr><th scope="row">large</th>
<td>0.1%</td> <!-- large/ldpi -->
-<td>3.0%</td> <!-- large/mdpi -->
+<td>3.1%</td> <!-- large/mdpi -->
<td></td> <!-- large/hdpi -->
<td></td> <!-- large/xhdpi -->
</tr>
<tr><th scope="row">xlarge</th>
<td></td> <!-- xlarge/ldpi -->
-<td>2.6%</td> <!-- xlarge/mdpi -->
+<td>2.9%</td> <!-- xlarge/mdpi -->
<td></td> <!-- xlarge/hdpi -->
<td></td> <!-- xlarge/xhdpi -->
</tr>
</table>
-<p><em>Data collected during a 7-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 7-day period ending on November 3, 2011</em></p>
</div>
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index bc7e906..18b8bc7 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -202,18 +202,30 @@
@Override
public void setAlpha(int alpha) {
+ if (mPaint == null && alpha == 0xFF) {
+ // Fast common case -- leave at normal alpha.
+ return;
+ }
getPaint().setAlpha(alpha);
invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter cf) {
+ if (mPaint == null && cf == null) {
+ // Fast common case -- leave at no color filter.
+ return;
+ }
getPaint().setColorFilter(cf);
invalidateSelf();
}
@Override
public void setDither(boolean dither) {
+ if (mPaint == null && dither == DEFAULT_DITHER) {
+ // Fast common case -- leave at default dither.
+ return;
+ }
getPaint().setDither(dither);
invalidateSelf();
}
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 3fa2acb..33b2f00 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -110,7 +110,8 @@
// Place a file descriptor into the parcel. The given fd must remain
// valid for the lifetime of the parcel.
- status_t writeFileDescriptor(int fd);
+ // The Parcel does not take ownership of the given fd unless you ask it to.
+ status_t writeFileDescriptor(int fd, bool takeOwnership = false);
// Place a file descriptor into the parcel. A dup of the fd is made, which
// will be closed once the parcel is destroyed.
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
index dc45ff0..4f342a2 100644
--- a/include/utils/BlobCache.h
+++ b/include/utils/BlobCache.h
@@ -19,19 +19,21 @@
#include <stddef.h>
+#include <utils/Flattenable.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/threads.h>
namespace android {
-// A BlobCache is an in-memory cache for binary key/value pairs. All the public
-// methods are thread-safe.
+// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache
+// does NOT provide any thread-safety guarantees.
//
-// The cache contents can be serialized to a file and reloaded in a subsequent
-// execution of the program. This serialization is non-portable and should only
-// be loaded by the device that generated it.
-class BlobCache : public RefBase {
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program. This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache : public RefBase, public Flattenable {
public:
// Create an empty blob cache. The blob cache will cache key/value pairs
@@ -58,14 +60,13 @@
void set(const void* key, size_t keySize, const void* value,
size_t valueSize);
- // The get function retrieves from the cache the binary value associated
- // with a given binary key. If the key is present in the cache then the
- // length of the binary value associated with that key is returned. If the
- // value argument is non-NULL and the size of the cached value is less than
- // valueSize bytes then the cached value is copied into the buffer pointed
- // to by the value argument. If the key is not present in the cache then 0
- // is returned and the buffer pointed to by the value argument is not
- // modified.
+ // get retrieves from the cache the binary value associated with a given
+ // binary key. If the key is present in the cache then the length of the
+ // binary value associated with that key is returned. If the value argument
+ // is non-NULL and the size of the cached value is less than valueSize bytes
+ // then the cached value is copied into the buffer pointed to by the value
+ // argument. If the key is not present in the cache then 0 is returned and
+ // the buffer pointed to by the value argument is not modified.
//
// Note that when calling get multiple times with the same key, the later
// calls may fail, returning 0, even if earlier calls succeeded. The return
@@ -77,6 +78,37 @@
// 0 <= valueSize
size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
+ // getFlattenedSize returns the number of bytes needed to store the entire
+ // serialized cache.
+ virtual size_t getFlattenedSize() const;
+
+ // getFdCount returns the number of file descriptors that will result from
+ // flattening the cache. This will always return 0 so as to allow the
+ // flattened cache to be saved to disk and then later restored.
+ virtual size_t getFdCount() const;
+
+ // flatten serializes the current contents of the cache into the memory
+ // pointed to by 'buffer'. The serialized cache contents can later be
+ // loaded into a BlobCache object using the unflatten method. The contents
+ // of the BlobCache object will not be modified.
+ //
+ // Preconditions:
+ // size >= this.getFlattenedSize()
+ // count == 0
+ virtual status_t flatten(void* buffer, size_t size, int fds[],
+ size_t count) const;
+
+ // unflatten replaces the contents of the cache with the serialized cache
+ // contents in the memory pointed to by 'buffer'. The previous contents of
+ // the BlobCache will be evicted from the cache. If an error occurs while
+ // unflattening the serialized cache contents then the BlobCache will be
+ // left in an empty state.
+ //
+ // Preconditions:
+ // count == 0
+ virtual status_t unflatten(void const* buffer, size_t size, int fds[],
+ size_t count);
+
private:
// Copying is disallowed.
BlobCache(const BlobCache&);
@@ -144,6 +176,46 @@
sp<Blob> mValue;
};
+ // A Header is the header for the entire BlobCache serialization format. No
+ // need to make this portable, so we simply write the struct out.
+ struct Header {
+ // mMagicNumber is the magic number that identifies the data as
+ // serialized BlobCache contents. It must always contain 'Blb$'.
+ uint32_t mMagicNumber;
+
+ // mBlobCacheVersion is the serialization format version.
+ uint32_t mBlobCacheVersion;
+
+ // mDeviceVersion is the device-specific version of the cache. This can
+ // be used to invalidate the cache.
+ uint32_t mDeviceVersion;
+
+ // mNumEntries is number of cache entries following the header in the
+ // data.
+ size_t mNumEntries;
+ };
+
+ // An EntryHeader is the header for a serialized cache entry. No need to
+ // make this portable, so we simply write the struct out. Each EntryHeader
+ // is followed imediately by the key data and then the value data.
+ //
+ // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+ // number of bytes that a serialized cache entry will occupy is:
+ //
+ // ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+ //
+ struct EntryHeader {
+ // mKeySize is the size of the entry key in bytes.
+ size_t mKeySize;
+
+ // mValueSize is the size of the entry value in bytes.
+ size_t mValueSize;
+
+ // mData contains both the key and value data for the cache entry. The
+ // key comes first followed immediately by the value.
+ uint8_t mData[];
+ };
+
// mMaxKeySize is the maximum key size that will be cached. Calls to
// BlobCache::set with a keySize parameter larger than mMaxKeySize will
// simply not add the key/value pair to the cache.
@@ -166,17 +238,12 @@
size_t mTotalSize;
// mRandState is the pseudo-random number generator state. It is passed to
- // nrand48 to generate random numbers when needed. It must be protected by
- // mMutex.
+ // nrand48 to generate random numbers when needed.
unsigned short mRandState[3];
// mCacheEntries stores all the cache entries that are resident in memory.
// Cache entries are added to it by the 'set' method.
SortedVector<CacheEntry> mCacheEntries;
-
- // mMutex is used to synchronize access to all member variables. It must be
- // locked any time the member variables are written or read.
- Mutex mMutex;
};
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index c7180ce..6b4c1a6 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -710,24 +710,19 @@
return err;
}
-status_t Parcel::writeFileDescriptor(int fd)
+status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
{
flat_binder_object obj;
obj.type = BINDER_TYPE_FD;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj.handle = fd;
- obj.cookie = (void*)0;
+ obj.cookie = (void*) (takeOwnership ? 1 : 0);
return writeObject(obj, true);
}
status_t Parcel::writeDupFileDescriptor(int fd)
{
- flat_binder_object obj;
- obj.type = BINDER_TYPE_FD;
- obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj.handle = dup(fd);
- obj.cookie = (void*)1;
- return writeObject(obj, true);
+ return writeFileDescriptor(dup(fd), true /*takeOwnership*/);
}
status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob)
@@ -764,7 +759,7 @@
} else {
status = writeInt32(1);
if (!status) {
- status = writeFileDescriptor(fd);
+ status = writeFileDescriptor(fd, true /*takeOwnership*/);
if (!status) {
outBlob->init(true /*mapped*/, ptr, len);
return NO_ERROR;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index c72a45b..6f84206 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -116,7 +116,7 @@
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
- ST_LOGV("SurfaceTexture::SurfaceTexture");
+ ST_LOGV("SurfaceTexture");
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
mNextCrop.makeInvalid();
@@ -125,7 +125,7 @@
}
SurfaceTexture::~SurfaceTexture() {
- ST_LOGV("SurfaceTexture::~SurfaceTexture");
+ ST_LOGV("~SurfaceTexture");
freeAllBuffersLocked();
}
@@ -169,7 +169,7 @@
}
status_t SurfaceTexture::setBufferCount(int bufferCount) {
- ST_LOGV("SurfaceTexture::setBufferCount");
+ ST_LOGV("setBufferCount: count=%d", bufferCount);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -217,6 +217,7 @@
status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
{
+ ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
if (!w || !h) {
ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
w, h);
@@ -230,7 +231,7 @@
}
status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
- ST_LOGV("SurfaceTexture::requestBuffer");
+ ST_LOGV("requestBuffer: slot=%d", slot);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
@@ -248,7 +249,7 @@
status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
uint32_t format, uint32_t usage) {
- ST_LOGV("SurfaceTexture::dequeueBuffer");
+ ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
if ((w && !h) || (!w && h)) {
ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
@@ -342,6 +343,8 @@
// clients are not allowed to dequeue more than one buffer
// if they didn't set a buffer count.
if (!mClientBufferCount && dequeuedCount) {
+ ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
+ "setting the buffer count");
return -EINVAL;
}
@@ -375,6 +378,8 @@
}
if (found == INVALID_BUFFER_SLOT) {
+ // This should not happen.
+ ST_LOGE("dequeueBuffer: no available buffer slots");
return -EBUSY;
}
@@ -427,10 +432,13 @@
}
returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
}
+ ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
+ mSlots[buf].mGraphicBuffer->handle, returnFlags);
return returnFlags;
}
status_t SurfaceTexture::setSynchronousMode(bool enabled) {
+ ST_LOGV("setSynchronousMode: enabled=%d", enabled);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -462,7 +470,7 @@
status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
- ST_LOGV("SurfaceTexture::queueBuffer");
+ ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
sp<FrameAvailableListener> listener;
@@ -534,7 +542,7 @@
}
void SurfaceTexture::cancelBuffer(int buf) {
- ST_LOGV("SurfaceTexture::cancelBuffer");
+ ST_LOGV("cancelBuffer: slot=%d", buf);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -556,7 +564,9 @@
}
status_t SurfaceTexture::setCrop(const Rect& crop) {
- ST_LOGV("SurfaceTexture::setCrop");
+ ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
+ crop.bottom);
+
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
ST_LOGE("setCrop: SurfaceTexture has been abandoned!");
@@ -567,7 +577,7 @@
}
status_t SurfaceTexture::setTransform(uint32_t transform) {
- ST_LOGV("SurfaceTexture::setTransform");
+ ST_LOGV("setTransform: xform=%#x", transform);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
ST_LOGE("setTransform: SurfaceTexture has been abandoned!");
@@ -579,7 +589,7 @@
status_t SurfaceTexture::connect(int api,
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
- ST_LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
+ ST_LOGV("connect: api=%d", api);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -612,7 +622,7 @@
}
status_t SurfaceTexture::disconnect(int api) {
- ST_LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
+ ST_LOGV("disconnect: api=%d", api);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -640,6 +650,7 @@
}
break;
default:
+ ST_LOGE("disconnect: unknown API %d", api);
err = -EINVAL;
break;
}
@@ -647,13 +658,14 @@
}
status_t SurfaceTexture::setScalingMode(int mode) {
- ST_LOGV("SurfaceTexture::setScalingMode(%d)", mode);
+ ST_LOGV("setScalingMode: mode=%d", mode);
switch (mode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
break;
default:
+ ST_LOGE("unknown scaling mode: %d", mode);
return BAD_VALUE;
}
@@ -663,7 +675,7 @@
}
status_t SurfaceTexture::updateTexImage() {
- ST_LOGV("SurfaceTexture::updateTexImage");
+ ST_LOGV("updateTexImage");
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -713,6 +725,10 @@
return -EINVAL;
}
+ ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
+ mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf,
+ mSlots[buf].mGraphicBuffer->handle);
+
if (mCurrentTexture != INVALID_BUFFER_SLOT) {
// The current buffer becomes FREE if it was still in the queued
// state. If it has already been given to the client
@@ -771,7 +787,7 @@
}
void SurfaceTexture::computeCurrentTransformMatrix() {
- ST_LOGV("SurfaceTexture::computeCurrentTransformMatrix");
+ ST_LOGV("computeCurrentTransformMatrix");
float xform[16];
for (int i = 0; i < 16; i++) {
@@ -862,14 +878,14 @@
}
nsecs_t SurfaceTexture::getTimestamp() {
- ST_LOGV("SurfaceTexture::getTimestamp");
+ ST_LOGV("getTimestamp");
Mutex::Autolock lock(mMutex);
return mCurrentTimestamp;
}
void SurfaceTexture::setFrameAvailableListener(
const sp<FrameAvailableListener>& listener) {
- ST_LOGV("SurfaceTexture::setFrameAvailableListener");
+ ST_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
}
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 24ec4e8..75b07de 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -170,8 +170,11 @@
patchCache.clear();
dropShadowCache.clear();
gradientCache.clear();
+ fontRenderer.clear();
// fall through
case kFlushMode_Moderate:
+ fontRenderer.flush();
+ textureCache.flush();
pathCache.clear();
roundRectShapeCache.clear();
circleShapeCache.clear();
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index e8362dc..eb863e9 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -67,20 +67,63 @@
const float whiteGamma = 1.0f / gamma;
for (uint32_t i = 0; i <= 255; i++) {
- mDefault[i] = i;
+ mGammaTable[i] = i;
const float v = i / 255.0f;
const float black = pow(v, blackGamma);
const float white = pow(v, whiteGamma);
- mBlackGamma[i] = uint8_t((float)::floor(black * 255.0f + 0.5f));
- mWhiteGamma[i] = uint8_t((float)::floor(white * 255.0f + 0.5f));
+ mGammaTable[256 + i] = uint8_t((float)::floor(black * 255.0f + 0.5f));
+ mGammaTable[512 + i] = uint8_t((float)::floor(white * 255.0f + 0.5f));
}
- // Configure the font renderers
- mDefaultRenderer.setGammaTable(&mDefault[0]);
- mBlackGammaRenderer.setGammaTable(&mBlackGamma[0]);
- mWhiteGammaRenderer.setGammaTable(&mWhiteGamma[0]);
+ memset(mRenderers, 0, sizeof(FontRenderer*) * kGammaCount);
+ memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount);
+}
+
+GammaFontRenderer::~GammaFontRenderer() {
+ for (int i = 0; i < kGammaCount; i++) {
+ delete mRenderers[i];
+ }
+}
+
+void GammaFontRenderer::clear() {
+ for (int i = 0; i < kGammaCount; i++) {
+ delete mRenderers[i];
+ mRenderers[i] = NULL;
+ }
+}
+
+void GammaFontRenderer::flush() {
+ int count = 0;
+ int min = -1;
+ uint32_t minCount = UINT_MAX;
+
+ for (int i = 0; i < kGammaCount; i++) {
+ if (mRenderers[i]) {
+ count++;
+ if (mRenderersUsageCount[i] < minCount) {
+ minCount = mRenderersUsageCount[i];
+ min = i;
+ }
+ }
+ }
+
+ if (count <= 1 || min < 0) return;
+
+ delete mRenderers[min];
+ mRenderers[min] = NULL;
+}
+
+FontRenderer* GammaFontRenderer::getRenderer(Gamma gamma) {
+ FontRenderer* renderer = mRenderers[gamma];
+ if (!renderer) {
+ renderer = new FontRenderer();
+ mRenderers[gamma] = renderer;
+ renderer->setGammaTable(&mGammaTable[gamma * 256]);
+ }
+ mRenderersUsageCount[gamma]++;
+ return renderer;
}
FontRenderer& GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
@@ -92,12 +135,12 @@
const int luminance = (r * 2 + g * 5 + b) >> 3;
if (luminance <= mBlackThreshold) {
- return mBlackGammaRenderer;
+ return *getRenderer(kGammaBlack);
} else if (luminance >= mWhiteThreshold) {
- return mWhiteGammaRenderer;
+ return *getRenderer(kGammaWhite);
}
}
- return mDefaultRenderer;
+ return *getRenderer(kGammaDefault);
}
}; // namespace uirenderer
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 96d960c..54c208e 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -26,36 +26,43 @@
struct GammaFontRenderer {
GammaFontRenderer();
+ ~GammaFontRenderer();
+
+ enum Gamma {
+ kGammaDefault = 0,
+ kGammaBlack = 1,
+ kGammaWhite = 2,
+ kGammaCount = 3
+ };
+
+ void clear();
+ void flush();
FontRenderer& getFontRenderer(const SkPaint* paint);
uint32_t getFontRendererCount() const {
- return 3;
+ return kGammaCount;
}
uint32_t getFontRendererSize(uint32_t fontRenderer) const {
- switch (fontRenderer) {
- case 0:
- return mDefaultRenderer.getCacheHeight() * mDefaultRenderer.getCacheWidth();
- case 1:
- return mBlackGammaRenderer.getCacheHeight() * mBlackGammaRenderer.getCacheWidth();
- case 2:
- return mWhiteGammaRenderer.getCacheHeight() * mWhiteGammaRenderer.getCacheWidth();
- }
- return 0;
+ if (fontRenderer >= kGammaCount) return 0;
+
+ FontRenderer* renderer = mRenderers[fontRenderer];
+ if (!renderer) return 0;
+
+ return renderer->getCacheHeight() * renderer->getCacheWidth();
}
private:
- FontRenderer mDefaultRenderer;
- FontRenderer mBlackGammaRenderer;
- FontRenderer mWhiteGammaRenderer;
+ FontRenderer* getRenderer(Gamma gamma);
+
+ uint32_t mRenderersUsageCount[kGammaCount];
+ FontRenderer* mRenderers[kGammaCount];
int mBlackThreshold;
int mWhiteThreshold;
- uint8_t mDefault[256];
- uint8_t mBlackGamma[256];
- uint8_t mWhiteGamma[256];
+ uint8_t mGammaTable[256 * kGammaCount];
};
}; // namespace uirenderer
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 5bd0d4f..8c01e3a 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -61,6 +61,9 @@
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
+// These properties are defined in percentage (range 0..1)
+#define PROPERTY_TEXTURE_CACHE_FLUSH_RATE "ro.hwui.texture_cache_flush_rate"
+
// These properties are defined in pixels
#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
#define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height"
@@ -82,6 +85,8 @@
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
#define DEFAULT_FBO_CACHE_SIZE 16
+#define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f
+
#define DEFAULT_TEXT_GAMMA 1.4f
#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index fbdbf92..018ce3e 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -34,7 +34,8 @@
TextureCache::TextureCache():
mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
- mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) {
+ mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
+ mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
INIT_LOGD(" Setting texture cache size to %sMB", property);
@@ -43,6 +44,15 @@
INIT_LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
}
+ if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, NULL) > 0) {
+ float flushRate = atof(property);
+ INIT_LOGD(" Setting texture cache flush rate to %.2f%%", flushRate * 100.0f);
+ setFlushRate(flushRate);
+ } else {
+ INIT_LOGD(" Using default texture cache flush rate of %.2f%%",
+ DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f);
+ }
+
init();
}
@@ -84,6 +94,10 @@
}
}
+void TextureCache::setFlushRate(float flushRate) {
+ mFlushRate = fmaxf(0.0f, fminf(1.0f, flushRate));
+}
+
///////////////////////////////////////////////////////////////////////////////
// Callbacks
///////////////////////////////////////////////////////////////////////////////
@@ -168,6 +182,21 @@
TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize);
}
+void TextureCache::flush() {
+ if (mFlushRate >= 1.0f || mCache.size() == 0) return;
+ if (mFlushRate <= 0.0f) {
+ clear();
+ return;
+ }
+
+ uint32_t targetSize = uint32_t(mSize * mFlushRate);
+ TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize);
+
+ while (mSize > targetSize) {
+ mCache.removeOldest();
+ }
+}
+
void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
SkAutoLockPixels alp(*bitmap);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index f7707f7..ce924b4 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -98,6 +98,17 @@
*/
uint32_t getSize();
+ /**
+ * Partially flushes the cache. The amount of memory freed by a flush
+ * is defined by the flush rate.
+ */
+ void flush();
+ /**
+ * Indicates the percentage of the cache to retain when a
+ * memory trim is requested (see Caches::flush).
+ */
+ void setFlushRate(float flushRate);
+
private:
/**
* Generates the texture from a bitmap into the specified texture structure.
@@ -119,6 +130,8 @@
uint32_t mMaxSize;
GLint mMaxTextureSize;
+ float mFlushRate;
+
bool mDebugEnabled;
Vector<SkBitmap*> mGarbage;
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
index 590576a..d38aae9 100644
--- a/libs/utils/BlobCache.cpp
+++ b/libs/utils/BlobCache.cpp
@@ -21,10 +21,20 @@
#include <string.h>
#include <utils/BlobCache.h>
+#include <utils/Errors.h>
#include <utils/Log.h>
namespace android {
+// BlobCache::Header::mMagicNumber value
+static const uint32_t blobCacheMagic = '_Bb$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 1;
+
+// BlobCache::Header::mDeviceVersion value
+static const uint32_t blobCacheDeviceVersion = 1;
+
BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
mMaxKeySize(maxKeySize),
mMaxValueSize(maxValueSize),
@@ -67,12 +77,10 @@
return;
}
- Mutex::Autolock lock(mMutex);
sp<Blob> dummyKey(new Blob(key, keySize, false));
CacheEntry dummyEntry(dummyKey, NULL);
while (true) {
-
ssize_t index = mCacheEntries.indexOf(dummyEntry);
if (index < 0) {
// Create a new cache entry.
@@ -129,7 +137,6 @@
keySize, mMaxKeySize);
return 0;
}
- Mutex::Autolock lock(mMutex);
sp<Blob> dummyKey(new Blob(key, keySize, false));
CacheEntry dummyEntry(dummyKey, NULL);
ssize_t index = mCacheEntries.indexOf(dummyEntry);
@@ -152,6 +159,133 @@
return valueBlobSize;
}
+static inline size_t align4(size_t size) {
+ return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+ size_t size = sizeof(Header);
+ for (size_t i = 0; i < mCacheEntries.size(); i++) {
+ const CacheEntry& e(mCacheEntries[i]);
+ sp<Blob> keyBlob = e.getKey();
+ sp<Blob> valueBlob = e.getValue();
+ size = align4(size);
+ size += sizeof(EntryHeader) + keyBlob->getSize() +
+ valueBlob->getSize();
+ }
+ return size;
+}
+
+size_t BlobCache::getFdCount() const {
+ return 0;
+}
+
+status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count)
+ const {
+ if (count != 0) {
+ LOGE("flatten: nonzero fd count: %d", count);
+ return BAD_VALUE;
+ }
+
+ // Write the cache header
+ if (size < sizeof(Header)) {
+ LOGE("flatten: not enough room for cache header");
+ return BAD_VALUE;
+ }
+ Header* header = reinterpret_cast<Header*>(buffer);
+ header->mMagicNumber = blobCacheMagic;
+ header->mBlobCacheVersion = blobCacheVersion;
+ header->mDeviceVersion = blobCacheDeviceVersion;
+ header->mNumEntries = mCacheEntries.size();
+
+ // Write cache entries
+ uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
+ off_t byteOffset = align4(sizeof(Header));
+ for (size_t i = 0; i < mCacheEntries.size(); i++) {
+ const CacheEntry& e(mCacheEntries[i]);
+ sp<Blob> keyBlob = e.getKey();
+ sp<Blob> valueBlob = e.getValue();
+ size_t keySize = keyBlob->getSize();
+ size_t valueSize = valueBlob->getSize();
+
+ size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+ if (byteOffset + entrySize > size) {
+ LOGE("flatten: not enough room for cache entries");
+ return BAD_VALUE;
+ }
+
+ EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
+ &byteBuffer[byteOffset]);
+ eheader->mKeySize = keySize;
+ eheader->mValueSize = valueSize;
+
+ memcpy(eheader->mData, keyBlob->getData(), keySize);
+ memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
+
+ byteOffset += align4(entrySize);
+ }
+
+ return OK;
+}
+
+status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[],
+ size_t count) {
+ // All errors should result in the BlobCache being in an empty state.
+ mCacheEntries.clear();
+
+ if (count != 0) {
+ LOGE("unflatten: nonzero fd count: %d", count);
+ return BAD_VALUE;
+ }
+
+ // Read the cache header
+ if (size < sizeof(Header)) {
+ LOGE("unflatten: not enough room for cache header");
+ return BAD_VALUE;
+ }
+ const Header* header = reinterpret_cast<const Header*>(buffer);
+ if (header->mMagicNumber != blobCacheMagic) {
+ LOGE("unflatten: bad magic number: %d", header->mMagicNumber);
+ return BAD_VALUE;
+ }
+ if (header->mBlobCacheVersion != blobCacheVersion ||
+ header->mDeviceVersion != blobCacheDeviceVersion) {
+ // We treat version mismatches as an empty cache.
+ return OK;
+ }
+
+ // Read cache entries
+ const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
+ off_t byteOffset = align4(sizeof(Header));
+ size_t numEntries = header->mNumEntries;
+ for (size_t i = 0; i < numEntries; i++) {
+ if (byteOffset + sizeof(EntryHeader) > size) {
+ mCacheEntries.clear();
+ LOGE("unflatten: not enough room for cache entry headers");
+ return BAD_VALUE;
+ }
+
+ const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
+ &byteBuffer[byteOffset]);
+ size_t keySize = eheader->mKeySize;
+ size_t valueSize = eheader->mValueSize;
+ size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+
+ if (byteOffset + entrySize > size) {
+ mCacheEntries.clear();
+ LOGE("unflatten: not enough room for cache entry headers");
+ return BAD_VALUE;
+ }
+
+ const uint8_t* data = eheader->mData;
+ set(data, keySize, data + keySize, valueSize);
+
+ byteOffset += align4(entrySize);
+ }
+
+ return OK;
+}
+
long int BlobCache::blob_random() {
#ifdef _WIN32
return rand();
@@ -179,7 +313,7 @@
mData(copyData ? malloc(size) : data),
mSize(size),
mOwnsData(copyData) {
- if (copyData) {
+ if (data != NULL && copyData) {
memcpy(const_cast<void*>(mData), data, size);
}
}
diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp
index 653ea5e..b64cc39 100644
--- a/libs/utils/tests/BlobCache_test.cpp
+++ b/libs/utils/tests/BlobCache_test.cpp
@@ -14,9 +14,13 @@
** limitations under the License.
*/
+#include <fcntl.h>
+#include <stdio.h>
+
#include <gtest/gtest.h>
#include <utils/BlobCache.h>
+#include <utils/Errors.h>
namespace android {
@@ -254,4 +258,164 @@
ASSERT_EQ(maxEntries/2 + 1, numCached);
}
+class BlobCacheFlattenTest : public BlobCacheTest {
+protected:
+ virtual void SetUp() {
+ BlobCacheTest::SetUp();
+ mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+ }
+
+ virtual void TearDown() {
+ mBC2.clear();
+ BlobCacheTest::TearDown();
+ }
+
+ void roundTrip() {
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+ delete[] flat;
+ }
+
+ sp<BlobCache> mBC2;
+};
+
+TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ roundTrip();
+ ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
+ ASSERT_EQ('e', buf[0]);
+ ASSERT_EQ('f', buf[1]);
+ ASSERT_EQ('g', buf[2]);
+ ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, &k, 1);
+ }
+
+ roundTrip();
+
+ // Verify the deserialized cache
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ uint8_t v = 0xee;
+ ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
+ ASSERT_EQ(k, v);
+ }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, &k, 1);
+ }
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ delete[] flat;
+
+ // Verify the cache that we just serialized
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ uint8_t v = 0xee;
+ ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
+ ASSERT_EQ(k, v);
+ }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, &k, 1);
+ }
+
+ size_t size = mBC->getFlattenedSize() - 1;
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0));
+ delete[] flat;
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ flat[1] = ~flat[1];
+
+ // Bad magic should cause an error.
+ ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0));
+ delete[] flat;
+
+ // The error should cause the unflatten to result in an empty cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ flat[5] = ~flat[5];
+
+ // Version mismatches shouldn't cause errors, but should not use the
+ // serialized entries
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+ delete[] flat;
+
+ // The version mismatch should cause the unflatten to result in an empty
+ // cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ flat[10] = ~flat[10];
+
+ // Version mismatches shouldn't cause errors, but should not use the
+ // serialized entries
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+ delete[] flat;
+
+ // The version mismatch should cause the unflatten to result in an empty
+ // cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+
+ // A buffer truncation shouldt cause an error
+ ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0));
+ delete[] flat;
+
+ // The error should cause the unflatten to result in an empty cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
} // namespace android
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index b88296f..09152f5 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -56,6 +56,53 @@
return OK;
}
+// stolen from dalvik/vm/checkJni.cpp
+static bool isValidUtf8(const char* bytes) {
+ while (*bytes != '\0') {
+ unsigned char utf8 = *(bytes++);
+ // Switch on the high four bits.
+ switch (utf8 >> 4) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ // Bit pattern 0xxx. No need for any extra bytes.
+ break;
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0f:
+ /*
+ * Bit pattern 10xx or 1111, which are illegal start bytes.
+ * Note: 1111 is valid for normal UTF-8, but not the
+ * modified UTF-8 used here.
+ */
+ return false;
+ case 0x0e:
+ // Bit pattern 1110, so there are two additional bytes.
+ utf8 = *(bytes++);
+ if ((utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ // Fall through to take care of the final byte.
+ case 0x0c:
+ case 0x0d:
+ // Bit pattern 110x, so there is one additional byte.
+ utf8 = *(bytes++);
+ if ((utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
class MyMediaScannerClient : public MediaScannerClient
{
public:
@@ -123,7 +170,22 @@
mEnv->ExceptionClear();
return NO_MEMORY;
}
- if ((valueStr = mEnv->NewStringUTF(value)) == NULL) {
+ char *cleaned = NULL;
+ if (!isValidUtf8(value)) {
+ cleaned = strdup(value);
+ char *chp = cleaned;
+ char ch;
+ while ((ch = *chp)) {
+ if (ch & 0x80) {
+ *chp = '?';
+ }
+ chp++;
+ }
+ value = cleaned;
+ }
+ valueStr = mEnv->NewStringUTF(value);
+ free(cleaned);
+ if (valueStr == NULL) {
mEnv->DeleteLocalRef(nameStr);
mEnv->ExceptionClear();
return NO_MEMORY;
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index efa1c45..5a1e93a 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -49,6 +49,16 @@
}\
}
+
+static inline int16_t clamp16(int32_t sample)
+{
+ // check overflow for both positive and negative values:
+ // all bits above short range must me equal to sign bit
+ if ((sample>>15) ^ (sample>>31))
+ sample = 0x7FFF ^ (sample>>31);
+ return sample;
+}
+
// Namespaces
namespace android {
namespace {
@@ -707,13 +717,6 @@
} /* end LvmBundle_init */
-static inline int16_t clamp16(int32_t sample)
-{
- if ((sample>>15) ^ (sample>>31))
- sample = 0x7FFF ^ (sample>>31);
- return sample;
-}
-
//----------------------------------------------------------------------------
// LvmBundle_process()
//----------------------------------------------------------------------------
@@ -2459,6 +2462,9 @@
LOGV("\tEffect_setEnabled() type %d, enabled %d", pContext->EffectType, enabled);
if (enabled) {
+ // Bass boost or Virtualizer can be temporarily disabled if playing over device speaker due
+ // to their nature.
+ bool tempDisabled = false;
switch (pContext->EffectType) {
case LVM_BASS_BOOST:
if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
@@ -2471,6 +2477,7 @@
pContext->pBundledContext->SamplesToExitCountBb =
(LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
pContext->pBundledContext->bBassEnabled = LVM_TRUE;
+ tempDisabled = pContext->pBundledContext->bBassTempDisabled;
break;
case LVM_EQUALIZER:
if (pContext->pBundledContext->bEqualizerEnabled == LVM_TRUE) {
@@ -2495,6 +2502,7 @@
pContext->pBundledContext->SamplesToExitCountVirt =
(LVM_INT32)(pContext->pBundledContext->SamplesPerSecond*0.1);
pContext->pBundledContext->bVirtualizerEnabled = LVM_TRUE;
+ tempDisabled = pContext->pBundledContext->bVirtualizerTempDisabled;
break;
case LVM_VOLUME:
if (pContext->pBundledContext->bVolumeEnabled == LVM_TRUE) {
@@ -2508,7 +2516,9 @@
LOGV("\tEffect_setEnabled() invalid effect type");
return -EINVAL;
}
- LvmEffect_enable(pContext);
+ if (!tempDisabled) {
+ LvmEffect_enable(pContext);
+ }
} else {
switch (pContext->EffectType) {
case LVM_BASS_BOOST:
@@ -2683,12 +2693,19 @@
LOGV("\tLVM_ERROR : LvmBundle_process returned error %d", lvmStatus);
return lvmStatus;
}
- }else{
+ } else {
//LOGV("\tEffect_process Not Calling process with %d effects enabled, %d called: Effect %d",
//pContext->pBundledContext->NumberEffectsEnabled,
//pContext->pBundledContext->NumberEffectsCalled, pContext->EffectType);
// 2 is for stereo input
- memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount*sizeof(LVM_INT16)*2);
+ if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ for (size_t i=0; i < outBuffer->frameCount*2; i++){
+ outBuffer->s16[i] =
+ clamp16((LVM_INT32)outBuffer->s16[i] + (LVM_INT32)inBuffer->s16[i]);
+ }
+ } else {
+ memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount*sizeof(LVM_INT16)*2);
+ }
}
return status;
@@ -3047,9 +3064,10 @@
LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_DEVICE start");
uint32_t device = *(uint32_t *)pCmdData;
- if(pContext->EffectType == LVM_BASS_BOOST){
- if((device == AUDIO_DEVICE_OUT_SPEAKER)||(device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)||
- (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){
+ if (pContext->EffectType == LVM_BASS_BOOST) {
+ if((device == AUDIO_DEVICE_OUT_SPEAKER) ||
+ (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) ||
+ (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){
LOGV("\tEFFECT_CMD_SET_DEVICE device is invalid for LVM_BASS_BOOST %d",
*(int32_t *)pCmdData);
LOGV("\tEFFECT_CMD_SET_DEVICE temporary disable LVM_BAS_BOOST");
@@ -3058,30 +3076,31 @@
// the effect must still report its original state as this can only be changed
// by the ENABLE/DISABLE command
- if(pContext->pBundledContext->bBassEnabled == LVM_TRUE){
+ if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
LOGV("\tEFFECT_CMD_SET_DEVICE disable LVM_BASS_BOOST %d",
*(int32_t *)pCmdData);
android::LvmEffect_disable(pContext);
- pContext->pBundledContext->bBassTempDisabled = LVM_TRUE;
}
- }else{
+ pContext->pBundledContext->bBassTempDisabled = LVM_TRUE;
+ } else {
LOGV("\tEFFECT_CMD_SET_DEVICE device is valid for LVM_BASS_BOOST %d",
*(int32_t *)pCmdData);
// If a device supports bassboost and the effect has been temporarily disabled
// previously then re-enable it
- if(pContext->pBundledContext->bBassTempDisabled == LVM_TRUE){
+ if (pContext->pBundledContext->bBassEnabled == LVM_TRUE) {
LOGV("\tEFFECT_CMD_SET_DEVICE re-enable LVM_BASS_BOOST %d",
*(int32_t *)pCmdData);
android::LvmEffect_enable(pContext);
- pContext->pBundledContext->bBassTempDisabled = LVM_FALSE;
}
+ pContext->pBundledContext->bBassTempDisabled = LVM_FALSE;
}
}
- if(pContext->EffectType == LVM_VIRTUALIZER){
- if((device == AUDIO_DEVICE_OUT_SPEAKER)||(device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)||
- (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){
+ if (pContext->EffectType == LVM_VIRTUALIZER) {
+ if((device == AUDIO_DEVICE_OUT_SPEAKER)||
+ (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)||
+ (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)){
LOGV("\tEFFECT_CMD_SET_DEVICE device is invalid for LVM_VIRTUALIZER %d",
*(int32_t *)pCmdData);
LOGV("\tEFFECT_CMD_SET_DEVICE temporary disable LVM_VIRTUALIZER");
@@ -3090,25 +3109,25 @@
// the effect must still report its original state as this can only be changed
// by the ENABLE/DISABLE command
- if(pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE){
+ if (pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE) {
LOGV("\tEFFECT_CMD_SET_DEVICE disable LVM_VIRTUALIZER %d",
*(int32_t *)pCmdData);
android::LvmEffect_disable(pContext);
- pContext->pBundledContext->bVirtualizerTempDisabled = LVM_TRUE;
}
- }else{
+ pContext->pBundledContext->bVirtualizerTempDisabled = LVM_TRUE;
+ } else {
LOGV("\tEFFECT_CMD_SET_DEVICE device is valid for LVM_VIRTUALIZER %d",
*(int32_t *)pCmdData);
// If a device supports virtualizer and the effect has been temporarily disabled
// previously then re-enable it
- if(pContext->pBundledContext->bVirtualizerTempDisabled == LVM_TRUE){
+ if(pContext->pBundledContext->bVirtualizerEnabled == LVM_TRUE){
LOGV("\tEFFECT_CMD_SET_DEVICE re-enable LVM_VIRTUALIZER %d",
*(int32_t *)pCmdData);
android::LvmEffect_enable(pContext);
- pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE;
}
+ pContext->pBundledContext->bVirtualizerTempDisabled = LVM_FALSE;
}
}
LOGV("\tEffect_command cmdCode Case: EFFECT_CMD_SET_DEVICE end");
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 7cdb76c..70208f8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -282,7 +282,7 @@
if (err == -EWOULDBLOCK) {
if (mSource->feedMoreTSData() == OK) {
- msg->post();
+ msg->post(10000ll);
}
}
} else if (what == ACodec::kWhatEOS) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
index e3aa8cf..1fa5c0d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
@@ -44,13 +44,7 @@
*/
public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
private String TAG = "MediaBassBoostTest";
- private final static int MIN_ENERGY_RATIO_2 = 3;
private final static short TEST_STRENGTH = 500;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private BassBoost mBassBoost = null;
private int mSession = -1;
@@ -184,85 +178,6 @@
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test actual bass boost influence on sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
-
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setLooping(true);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getBassBoost(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(200);
- // measure reference energy around 1kHz
- int refEnergy200 = probe.capture(200);
- int refEnergy1000 = probe.capture(1000);
- mBassBoost.setStrength((short)1000);
- mBassBoost.setEnabled(true);
- Thread.sleep(4000);
- // measure energy around 1kHz with band level at min
- int energy200 = probe.capture(200);
- int energy1000 = probe.capture(1000);
- // verify that the energy ration between low and high frequencies is at least
- // MIN_ENERGY_RATIO_2 times higher with bassboost on.
- assertTrue(msg + ": bass boost has no effect",
- ((float)energy200/(float)energy1000) >
- (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseBassBoost();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
index ee91bbb..da9089d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
@@ -49,11 +49,6 @@
private final static int MAX_BAND_LEVEL = 1500;
private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
private final static int MIN_NUMBER_OF_PRESETS = 4;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private Equalizer mEqualizer = null;
private int mSession = -1;
@@ -252,80 +247,6 @@
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test that the equalizer actually alters the sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getEqualizer(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(500);
- // measure reference energy around 1kHz
- int refEnergy = probe.capture(1000);
- short band = mEqualizer.getBand(1000000);
- short[] levelRange = mEqualizer.getBandLevelRange();
- mEqualizer.setBandLevel(band, levelRange[0]);
- mEqualizer.setEnabled(true);
- Thread.sleep(500);
- // measure energy around 1kHz with band level at min
- int energy = probe.capture(1000);
- assertTrue(msg + ": equalizer has no effect at 1kHz", energy < refEnergy/4);
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseEqualizer();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
-
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
index b74e525..122545f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
@@ -44,13 +44,7 @@
*/
public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
private String TAG = "MediaVirtualizerTest";
- private final static int MIN_ENERGY_RATIO_2 = 2;
private final static short TEST_STRENGTH = 500;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private Virtualizer mVirtualizer = null;
private int mSession = -1;
@@ -185,89 +179,6 @@
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test actual virtualizer influence on sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
-
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setLooping(true);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getVirtualizer(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(200);
- // measure reference energy around 1kHz
- int refEnergy200 = probe.capture(200);
- int refEnergy1000 = probe.capture(1000);
- mVirtualizer.setStrength((short)1000);
- mVirtualizer.setEnabled(true);
- Thread.sleep(4000);
- // measure energy around 1kHz with band level at min
- int energy200 = probe.capture(200);
- int energy1000 = probe.capture(1000);
- // verify that the energy ration between low and high frequencies is at least
- // MIN_ENERGY_RATIO_2 times higher with virtualizer on.
- // NOTE: this is what is observed with current virtualizer implementation and the test
- // audio file but is not the primary effect of the virtualizer. A better way would
- // be to have a stereo PCM capture and check that a strongly paned input is centered
- // when output. However, we cannot capture stereo with the visualizer.
- assertTrue(msg + ": virtualizer has no effect",
- ((float)energy200/(float)energy1000) >
- (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseVirtualizer();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 3e66a13..5855b63 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -8,6 +8,7 @@
LOCAL_SRC_FILES:= \
EGL/egl_tls.cpp \
+ EGL/egl_cache.cpp \
EGL/egl_display.cpp \
EGL/egl_object.cpp \
EGL/egl.cpp \
@@ -157,4 +158,3 @@
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 1f9ce68..60ac34b 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -858,10 +858,17 @@
return NULL;
}
+ // The EGL_ANDROID_blob_cache extension should not be exposed to
+ // applications. It is used internally by the Android EGL layer.
+ if (!strcmp(procname, "eglSetBlobCacheFuncs")) {
+ return NULL;
+ }
+
__eglMustCastToProperFunctionPointerType addr;
addr = findProcAddress(procname, sExtentionMap, NELEM(sExtentionMap));
if (addr) return addr;
+
// this protects accesses to sGLExtentionMap and sGLExtentionSlot
pthread_mutex_lock(&sExtensionMapMutex);
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
new file mode 100644
index 0000000..1e64302
--- /dev/null
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -0,0 +1,95 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+#include "egl_cache.h"
+#include "egl_display.h"
+#include "egl_impl.h"
+#include "egldefs.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+#define BC_EXT_STR "EGL_ANDROID_blob_cache"
+
+//
+// EGL_ANDROID_blob_cache types and functions
+//
+typedef khronos_ssize_t EGLsizei;
+
+typedef void (*EGLSetBlobFunc) (const void* key, EGLsizei keySize,
+ const void* value, EGLsizei valueSize);
+
+typedef EGLsizei (*EGLGetBlobFunc) (const void* key, EGLsizei keySize,
+ void* value, EGLsizei valueSize);
+
+typedef void (EGLAPIENTRYP PFNEGLSETBLOBCACHEFUNCSPROC) (EGLDisplay dpy,
+ EGLSetBlobFunc set, EGLGetBlobFunc get);
+
+//
+// egl_cache_t definition
+//
+static void setBlob(const void* key, EGLsizei keySize, const void* value,
+ EGLsizei valueSize) {
+}
+
+static EGLsizei getBlob(const void* key, EGLsizei keySize, void* value,
+ EGLsizei valueSize) {
+ return 0;
+}
+
+egl_cache_t* egl_cache_t::get() {
+ static egl_cache_t theCache;
+ return &theCache;
+}
+
+void egl_cache_t::initialize(egl_display_t *display) {
+ for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
+ egl_connection_t* const cnx = &gEGLImpl[i];
+ if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) {
+ const char* exts = display->disp[i].queryString.extensions;
+ size_t bcExtLen = strlen(BC_EXT_STR);
+ size_t extsLen = strlen(exts);
+ bool equal = !strcmp(BC_EXT_STR, exts);
+ bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
+ bool atEnd = (bcExtLen+1) < extsLen &&
+ !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
+ bool inMiddle = strstr(" " BC_EXT_STR " ", exts);
+ if (equal || atStart || atEnd || inMiddle) {
+ PFNEGLSETBLOBCACHEFUNCSPROC eglSetBlobCacheFuncs;
+ eglSetBlobCacheFuncs =
+ reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSPROC>(
+ cnx->egl.eglGetProcAddress("eglSetBlobCacheFuncs"));
+ if (eglSetBlobCacheFuncs == NULL) {
+ LOGE("EGL_ANDROID_blob_cache advertised by display %d, "
+ "but unable to get eglSetBlobCacheFuncs", i);
+ continue;
+ }
+
+ eglSetBlobCacheFuncs(display->disp[i].dpy, setBlob, getBlob);
+ EGLint err = cnx->egl.eglGetError();
+ if (err != EGL_SUCCESS) {
+ LOGE("eglSetBlobCacheFuncs resulted in an error: %#x",
+ err);
+ }
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
new file mode 100644
index 0000000..1fcfacc
--- /dev/null
+++ b/opengl/libs/EGL/egl_cache.h
@@ -0,0 +1,33 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class egl_display_t;
+
+class egl_cache_t {
+public:
+
+ static egl_cache_t* get();
+
+ void initialize(egl_display_t* display);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 83aafa6..0f92864 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,6 +14,7 @@
** limitations under the License.
*/
+#include "egl_cache.h"
#include "egl_display.h"
#include "egl_object.h"
#include "egl_tls.h"
@@ -170,6 +171,8 @@
}
}
+ egl_cache_t::get()->initialize(this);
+
EGLBoolean res = EGL_FALSE;
for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index eb75ebc..2af5103 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -751,10 +751,13 @@
return;
}
ArrayList<AppWidgetId> instances = p.instances;
+ final int callingUid = getCallingUid();
final int N = instances.size();
for (int i=0; i<N; i++) {
AppWidgetId id = instances.get(i);
- updateAppWidgetInstanceLocked(id, views);
+ if (canAccessAppWidgetId(id, callingUid)) {
+ updateAppWidgetInstanceLocked(id, views);
+ }
}
}
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 851cb33..8c42f31 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -171,6 +171,12 @@
private static final int ENABLED = 1;
private static final int DISABLED = 0;
+ private static final boolean ADD = true;
+ private static final boolean REMOVE = false;
+
+ private static final boolean TO_DEFAULT_TABLE = true;
+ private static final boolean TO_SECONDARY_TABLE = false;
+
// Share the event space with NetworkStateTracker (which can't see this
// internal class but sends us events). If you change these, change
// NetworkStateTracker.java too.
@@ -501,7 +507,7 @@
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
- mTethering = new Tethering(mContext, nmService, statsService, mHandler.getLooper());
+ mTethering = new Tethering(mContext, nmService, statsService, this, mHandler.getLooper());
mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
@@ -1146,23 +1152,24 @@
return false;
}
- private boolean addRoute(LinkProperties p, RouteInfo r) {
- return modifyRoute(p.getInterfaceName(), p, r, 0, true);
+ private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable) {
+ return modifyRoute(p.getInterfaceName(), p, r, 0, ADD, toDefaultTable);
}
- private boolean removeRoute(LinkProperties p, RouteInfo r) {
- return modifyRoute(p.getInterfaceName(), p, r, 0, false);
+ private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable) {
+ return modifyRoute(p.getInterfaceName(), p, r, 0, REMOVE, toDefaultTable);
}
private boolean addRouteToAddress(LinkProperties lp, InetAddress addr) {
- return modifyRouteToAddress(lp, addr, true);
+ return modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE);
}
private boolean removeRouteToAddress(LinkProperties lp, InetAddress addr) {
- return modifyRouteToAddress(lp, addr, false);
+ return modifyRouteToAddress(lp, addr, REMOVE, TO_DEFAULT_TABLE);
}
- private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd) {
+ private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd,
+ boolean toDefaultTable) {
RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), addr);
if (bestRoute == null) {
bestRoute = RouteInfo.makeHostRoute(addr);
@@ -1176,15 +1183,15 @@
bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway());
}
}
- return modifyRoute(lp.getInterfaceName(), lp, bestRoute, 0, doAdd);
+ return modifyRoute(lp.getInterfaceName(), lp, bestRoute, 0, doAdd, toDefaultTable);
}
private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
- boolean doAdd) {
+ boolean doAdd, boolean toDefaultTable) {
if ((ifaceName == null) || (lp == null) || (r == null)) return false;
if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
- loge("Error adding route - too much recursion");
+ loge("Error modifying route - too much recursion");
return false;
}
@@ -1199,14 +1206,18 @@
// route to it's gateway
bestRoute = RouteInfo.makeHostRoute(r.getGateway(), bestRoute.getGateway());
}
- modifyRoute(ifaceName, lp, bestRoute, cycleCount+1, doAdd);
+ modifyRoute(ifaceName, lp, bestRoute, cycleCount+1, doAdd, toDefaultTable);
}
}
if (doAdd) {
if (VDBG) log("Adding " + r + " for interface " + ifaceName);
- mAddedRoutes.add(r);
try {
- mNetd.addRoute(ifaceName, r);
+ if (toDefaultTable) {
+ mAddedRoutes.add(r); // only track default table - only one apps can effect
+ mNetd.addRoute(ifaceName, r);
+ } else {
+ mNetd.addSecondaryRoute(ifaceName, r);
+ }
} catch (Exception e) {
// never crash - catch them all
if (VDBG) loge("Exception trying to add a route: " + e);
@@ -1215,18 +1226,29 @@
} else {
// if we remove this one and there are no more like it, then refcount==0 and
// we can remove it from the table
- mAddedRoutes.remove(r);
- if (mAddedRoutes.contains(r) == false) {
+ if (toDefaultTable) {
+ mAddedRoutes.remove(r);
+ if (mAddedRoutes.contains(r) == false) {
+ if (VDBG) log("Removing " + r + " for interface " + ifaceName);
+ try {
+ mNetd.removeRoute(ifaceName, r);
+ } catch (Exception e) {
+ // never crash - catch them all
+ if (VDBG) loge("Exception trying to remove a route: " + e);
+ return false;
+ }
+ } else {
+ if (VDBG) log("not removing " + r + " as it's still in use");
+ }
+ } else {
if (VDBG) log("Removing " + r + " for interface " + ifaceName);
try {
- mNetd.removeRoute(ifaceName, r);
+ mNetd.removeSecondaryRoute(ifaceName, r);
} catch (Exception e) {
// never crash - catch them all
if (VDBG) loge("Exception trying to remove a route: " + e);
return false;
}
- } else {
- if (VDBG) log("not removing " + r + " as it's still in use");
}
}
return true;
@@ -1862,14 +1884,21 @@
for (RouteInfo r : routeDiff.removed) {
if (isLinkDefault || ! r.isDefaultRoute()) {
- removeRoute(curLp, r);
+ removeRoute(curLp, r, TO_DEFAULT_TABLE);
+ }
+ if (isLinkDefault == false) {
+ // remove from a secondary route table
+ removeRoute(curLp, r, TO_SECONDARY_TABLE);
}
}
for (RouteInfo r : routeDiff.added) {
if (isLinkDefault || ! r.isDefaultRoute()) {
- addRoute(newLp, r);
+ addRoute(newLp, r, TO_DEFAULT_TABLE);
} else {
+ // add to a secondary route table
+ addRoute(newLp, r, TO_SECONDARY_TABLE);
+
// many radios add a default route even when we don't want one.
// remove the default route unless somebody else has asked for it
String ifaceName = newLp.getInterfaceName();
@@ -2450,12 +2479,6 @@
int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
boolean tetherEnabledInSettings = (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.TETHER_SUPPORTED, defaultVal) != 0);
- // Short term disabling of Tethering if DUN is required.
- // TODO - fix multi-connection tethering using policy-base routing
- int[] upstreamConnTypes = mTethering.getUpstreamIfaceTypes();
- for (int i : upstreamConnTypes) {
- if (i == ConnectivityManager.TYPE_MOBILE_DUN) return false;
- }
return tetherEnabledInSettings && mTetheringConfigValid;
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 4e4fe4a..6887de3 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -59,7 +59,11 @@
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
@@ -77,6 +81,9 @@
private static final int ADD = 1;
private static final int REMOVE = 2;
+ private static final String DEFAULT = "default";
+ private static final String SECONDARY = "secondary";
+
/**
* Name representing {@link #setGlobalAlert(long)} limit when delivered to
* {@link INetworkManagementEventObserver#limitReached(String, String)}.
@@ -505,15 +512,25 @@
public void addRoute(String interfaceName, RouteInfo route) {
mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
- modifyRoute(interfaceName, ADD, route);
+ modifyRoute(interfaceName, ADD, route, DEFAULT);
}
public void removeRoute(String interfaceName, RouteInfo route) {
mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
- modifyRoute(interfaceName, REMOVE, route);
+ modifyRoute(interfaceName, REMOVE, route, DEFAULT);
}
- private void modifyRoute(String interfaceName, int action, RouteInfo route) {
+ public void addSecondaryRoute(String interfaceName, RouteInfo route) {
+ mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
+ modifyRoute(interfaceName, ADD, route, SECONDARY);
+ }
+
+ public void removeSecondaryRoute(String interfaceName, RouteInfo route) {
+ mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG);
+ modifyRoute(interfaceName, REMOVE, route, SECONDARY);
+ }
+
+ private void modifyRoute(String interfaceName, int action, RouteInfo route, String type) {
ArrayList<String> rsp;
StringBuilder cmd;
@@ -521,12 +538,12 @@
switch (action) {
case ADD:
{
- cmd = new StringBuilder("interface route add " + interfaceName);
+ cmd = new StringBuilder("interface route add " + interfaceName + " " + type);
break;
}
case REMOVE:
{
- cmd = new StringBuilder("interface route remove " + interfaceName);
+ cmd = new StringBuilder("interface route remove " + interfaceName + " " + type);
break;
}
default:
@@ -833,14 +850,33 @@
}
}
+ private void modifyNat(String cmd, String internalInterface, String externalInterface)
+ throws SocketException {
+ cmd = String.format("nat %s %s %s", cmd, internalInterface, externalInterface);
+
+ NetworkInterface internalNetworkInterface =
+ NetworkInterface.getByName(internalInterface);
+ Collection<InterfaceAddress>interfaceAddresses =
+ internalNetworkInterface.getInterfaceAddresses();
+ cmd += " " + interfaceAddresses.size();
+ for (InterfaceAddress ia : interfaceAddresses) {
+ InetAddress addr = NetworkUtils.getNetworkPart(ia.getAddress(),
+ ia.getNetworkPrefixLength());
+ cmd = cmd + " " + addr.getHostAddress() + "/" + ia.getNetworkPrefixLength();
+ }
+
+ mConnector.doCommand(cmd);
+ }
+
public void enableNat(String internalInterface, String externalInterface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ if (DBG) Log.d(TAG, "enableNat(" + internalInterface + ", " + externalInterface + ")");
try {
- mConnector.doCommand(
- String.format("nat enable %s %s", internalInterface, externalInterface));
- } catch (NativeDaemonConnectorException e) {
+ modifyNat("enable", internalInterface, externalInterface);
+ } catch (Exception e) {
+ Log.e(TAG, "enableNat got Exception " + e.toString());
throw new IllegalStateException(
"Unable to communicate to native daemon for enabling NAT interface");
}
@@ -850,10 +886,11 @@
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+ if (DBG) Log.d(TAG, "disableNat(" + internalInterface + ", " + externalInterface + ")");
try {
- mConnector.doCommand(
- String.format("nat disable %s %s", internalInterface, externalInterface));
- } catch (NativeDaemonConnectorException e) {
+ modifyNat("disable", internalInterface, externalInterface);
+ } catch (Exception e) {
+ Log.e(TAG, "disableNat got Exception " + e.toString());
throw new IllegalStateException(
"Unable to communicate to native daemon for disabling NAT interface");
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5bfe6f8..01eade1 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -921,18 +921,14 @@
Slog.d(TAG, "ACTION_SCREEN_ON");
}
mAlarmManager.cancel(mIdleIntent);
- mDeviceIdle = false;
mScreenOff = false;
- // Once the screen is on, we are not keeping WIFI running
- // because of any locks so clear that tracking immediately.
- reportStartWorkSource();
evaluateTrafficStatsPolling();
mWifiStateMachine.enableRssiPolling(true);
if (mBackgroundScanSupported) {
mWifiStateMachine.enableBackgroundScanCommand(false);
}
mWifiStateMachine.enableAllNetworks();
- updateWifiState();
+ setDeviceIdleAndUpdateWifi(false);
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
if (DBG) {
Slog.d(TAG, "ACTION_SCREEN_OFF");
@@ -950,36 +946,17 @@
* or plugged in to AC).
*/
if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
- WifiInfo info = mWifiStateMachine.syncRequestConnectionInfo();
- if (info.getSupplicantState() != SupplicantState.COMPLETED) {
- // we used to go to sleep immediately, but this caused some race conditions
- // we don't have time to track down for this release. Delay instead,
- // but not as long as we would if connected (below)
- // TODO - fix the race conditions and switch back to the immediate turn-off
- long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
- if (DBG) {
- Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
- }
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
- // // do not keep Wifi awake when screen is off if Wifi is not associated
- // mDeviceIdle = true;
- // updateWifiState();
+ //Delayed shutdown if wifi is connected
+ if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
+ if (DBG) Slog.d(TAG, "setting ACTION_DEVICE_IDLE: " + idleMillis + " ms");
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ + idleMillis, mIdleIntent);
} else {
- long triggerTime = System.currentTimeMillis() + idleMillis;
- if (DBG) {
- Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis
- + "ms");
- }
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
+ setDeviceIdleAndUpdateWifi(true);
}
}
} else if (action.equals(ACTION_DEVICE_IDLE)) {
- if (DBG) {
- Slog.d(TAG, "got ACTION_DEVICE_IDLE");
- }
- mDeviceIdle = true;
- reportStartWorkSource();
- updateWifiState();
+ setDeviceIdleAndUpdateWifi(true);
} else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
@@ -1056,6 +1033,12 @@
}
};
+ private void setDeviceIdleAndUpdateWifi(boolean deviceIdle) {
+ mDeviceIdle = deviceIdle;
+ reportStartWorkSource();
+ updateWifiState();
+ }
+
private synchronized void reportStartWorkSource() {
mTmpWorkSource.clear();
if (mDeviceIdle) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0c42926..4fe8119 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -120,6 +120,7 @@
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
@@ -128,8 +129,10 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -857,9 +860,11 @@
static final int SHOW_COMPAT_MODE_DIALOG_MSG = 30;
static final int DISPATCH_FOREGROUND_ACTIVITIES_CHANGED = 31;
static final int DISPATCH_PROCESS_DIED = 32;
+ static final int REPORT_MEM_USAGE = 33;
AlertDialog mUidAlert;
CompatModeDialog mCompatModeDialog;
+ long mLastMemUsageReportTime = 0;
final Handler mHandler = new Handler() {
//public Handler() {
@@ -1199,6 +1204,56 @@
dispatchProcessDied(pid, uid);
break;
}
+ case REPORT_MEM_USAGE: {
+ boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+ if (!isDebuggable) {
+ return;
+ }
+ synchronized (ActivityManagerService.this) {
+ long now = SystemClock.uptimeMillis();
+ if (now < (mLastMemUsageReportTime+10000)) {
+ // Don't report more than every 10 seconds to somewhat
+ // avoid spamming.
+ return;
+ }
+ mLastMemUsageReportTime = now;
+ }
+ Thread thread = new Thread() {
+ @Override public void run() {
+ try {
+ java.lang.Process proc = Runtime.getRuntime().exec(new String[] {
+ "procrank", });
+ final InputStreamReader converter = new InputStreamReader(
+ proc.getInputStream());
+ BufferedReader in = new BufferedReader(converter);
+ String line;
+ while (true) {
+ line = in.readLine();
+ if (line == null) {
+ break;
+ }
+ if (line.length() > 0) {
+ Slog.i(TAG, line);
+ }
+ }
+ converter.close();
+ } catch (IOException e) {
+ }
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ dumpApplicationMemoryUsage(null, pw, " ", new String[] { }, true);
+ Slog.i(TAG, sw.toString());
+ synchronized (ActivityManagerService.this) {
+ long now = SystemClock.uptimeMillis();
+ if (mLastMemUsageReportTime < now) {
+ mLastMemUsageReportTime = now;
+ }
+ }
+ }
+ };
+ thread.start();
+ break;
+ }
}
}
};
@@ -1339,7 +1394,7 @@
return;
}
- mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args);
+ mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false);
}
}
@@ -2768,6 +2823,7 @@
addProcessToGcListLocked(rec);
}
}
+ mHandler.sendEmptyMessage(REPORT_MEM_USAGE);
scheduleAppGcsLocked();
}
}
@@ -9242,7 +9298,7 @@
}
final void dumpApplicationMemoryUsage(FileDescriptor fd,
- PrintWriter pw, String prefix, String[] args) {
+ PrintWriter pw, String prefix, String[] args, boolean brief) {
boolean dumpAll = false;
int opti = 0;
@@ -9382,15 +9438,19 @@
}
}
- pw.println();
- pw.println("Total PSS by process:");
- dumpMemItems(pw, " ", procMems, true);
- pw.println();
+ if (!brief) {
+ pw.println();
+ pw.println("Total PSS by process:");
+ dumpMemItems(pw, " ", procMems, true);
+ pw.println();
+ }
pw.println("Total PSS by OOM adjustment:");
dumpMemItems(pw, " ", oomMems, false);
- pw.println();
- pw.println("Total PSS by category:");
- dumpMemItems(pw, " ", catMems, true);
+ if (!brief) {
+ pw.println();
+ pw.println("Total PSS by category:");
+ dumpMemItems(pw, " ", catMems, true);
+ }
pw.println();
pw.print("Total PSS: "); pw.print(totalPss); pw.println(" Kb");
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 7bd29d9..e49acaf 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -91,6 +91,7 @@
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
+ private final IConnectivityManager mConnService;
private Looper mLooper;
private HandlerThread mThread;
@@ -127,10 +128,11 @@
// when RNDIS is enabled
public Tethering(Context context, INetworkManagementService nmService,
- INetworkStatsService statsService, Looper looper) {
+ INetworkStatsService statsService, IConnectivityManager connService, Looper looper) {
mContext = context;
mNMService = nmService;
mStatsService = statsService;
+ mConnService = connService;
mLooper = looper;
mIfaces = new HashMap<String, TetherInterfaceSM>();
@@ -347,10 +349,8 @@
}
private void sendTetherStateChangedBroadcast() {
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
try {
- if (!cm.isTetheringSupported()) return;
+ if (!mConnService.isTetheringSupported()) return;
} catch (RemoteException e) {
return;
}
@@ -910,6 +910,7 @@
try {
mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
+ Log.e(TAG, "Error Tethering: " + e.toString());
setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
transitionTo(mInitialState);
@@ -987,6 +988,7 @@
try {
mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
} catch (Exception e) {
+ Log.e(TAG, "Exception enabling Nat: " + e.toString());
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
@@ -1150,13 +1152,11 @@
boolean retValue = true;
if (apnType == ConnectivityManager.TYPE_NONE) return false;
if (apnType != mMobileApnReserved) turnOffUpstreamMobileConnection();
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
int result = Phone.APN_REQUEST_FAILED;
String enableString = enableString(apnType);
if (enableString == null) return false;
try {
- result = cm.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ result = mConnService.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
enableString, new Binder());
} catch (Exception e) {
}
@@ -1178,10 +1178,8 @@
}
protected boolean turnOffUpstreamMobileConnection() {
if (mMobileApnReserved != ConnectivityManager.TYPE_NONE) {
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
try {
- cm.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ mConnService.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
enableString(mMobileApnReserved));
} catch (Exception e) {
return false;
@@ -1234,8 +1232,6 @@
}
protected void chooseUpstreamType(boolean tryCell) {
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
int upType = ConnectivityManager.TYPE_NONE;
String iface = null;
@@ -1251,7 +1247,7 @@
for (Integer netType : mUpstreamIfaceTypes) {
NetworkInfo info = null;
try {
- info = cm.getNetworkInfo(netType.intValue());
+ info = mConnService.getNetworkInfo(netType.intValue());
} catch (RemoteException e) { }
if ((info != null) && info.isConnected()) {
upType = netType.intValue();
@@ -1283,7 +1279,7 @@
} else {
LinkProperties linkProperties = null;
try {
- linkProperties = cm.getLinkProperties(upType);
+ linkProperties = mConnService.getLinkProperties(upType);
} catch (RemoteException e) { }
if (linkProperties != null) iface = linkProperties.getInterfaceName();
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 2cd3062..8ed7966 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -8682,7 +8682,8 @@
if (needRelayout) {
requestAnimationLocked(0);
} else if (animating) {
- requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
+ final int refreshTimeUs = (int)(1000 / mDisplay.getRefreshRate());
+ requestAnimationLocked(currentTime + refreshTimeUs - SystemClock.uptimeMillis());
}
// Finally update all input windows now that the window changes have stabilized.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 5b74fb8..d2d2d8b 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -471,14 +471,20 @@
if (mInitCheck != NO_ERROR)
return mInitCheck;
+ SensorInterface* sensor = mSensorMap.valueFor(handle);
+ if (!sensor)
+ return BAD_VALUE;
+
if (ns < 0)
return BAD_VALUE;
+ if (ns == 0) {
+ ns = sensor->getSensor().getMinDelayNs();
+ }
+
if (ns < MINIMUM_EVENTS_PERIOD)
ns = MINIMUM_EVENTS_PERIOD;
- SensorInterface* sensor = mSensorMap.valueFor(handle);
- if (!sensor) return BAD_VALUE;
return sensor->setDelay(connection.get(), handle, ns);
}
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 61a8358..b916bd7 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -30,6 +30,10 @@
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
endif
+ifneq (,$(findstring $(TARGET_DEVICE),tuna toro maguro))
+ LOCAL_CFLAGS += -DREFRESH_RATE=48
+endif
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index f4be168..329c052 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -141,6 +141,17 @@
mDpiY = mNativeWindow->ydpi;
mRefreshRate = fbDev->fps;
+
+/* FIXME: this is a temporary HACK until we are able to report the refresh rate
+ * properly from the HAL. The WindowManagerService now relies on this value.
+ */
+#ifndef REFRESH_RATE
+ mRefreshRate = fbDev->fps;
+#else
+ mRefreshRate = REFRESH_RATE;
+#warning "refresh rate set via makefile to REFRESH_RATE"
+#endif
+
EGLint w, h, dummy;
EGLint numConfigs=0;
EGLSurface surface;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 97e7aa3..5b13603 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -27,12 +27,14 @@
import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.TrafficStats;
import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Settings;
@@ -56,6 +58,7 @@
*/
public abstract class DataConnectionTracker extends Handler {
protected static final boolean DBG = true;
+ protected static final boolean VDBG = false;
/**
* IDLE: ready to start data connection setup, default state
@@ -114,8 +117,8 @@
protected static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14;
protected static final int EVENT_DISCONNECT_DONE = BASE + 15;
protected static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16;
- protected static final int EVENT_START_NETSTAT_POLL = BASE + 17;
- protected static final int EVENT_START_RECOVERY = BASE + 18;
+ protected static final int EVENT_DATA_STALL_ALARM = BASE + 17;
+ protected static final int EVENT_DO_RECOVERY = BASE + 18;
protected static final int EVENT_APN_CHANGED = BASE + 19;
protected static final int EVENT_CDMA_DATA_DETACHED = BASE + 20;
protected static final int EVENT_NV_READY = BASE + 21;
@@ -189,19 +192,16 @@
/**
* After detecting a potential connection problem, this is the max number
- * of subsequent polls before attempting a radio reset. At this point,
- * poll interval is 5 seconds (POLL_NETSTAT_SLOW_MILLIS), so set this to
- * poll for about 2 more minutes.
+ * of subsequent polls before attempting recovery.
*/
protected static final int NO_RECV_POLL_LIMIT = 24;
-
// 1 sec. default polling interval when screen is on.
protected static final int POLL_NETSTAT_MILLIS = 1000;
// 10 min. default polling interval when screen is off.
protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10;
// 2 min for round trip time
protected static final int POLL_LONGEST_RTT = 120 * 1000;
- // 10 for packets without ack
+ // Default sent packets without ack which triggers initial recovery steps
protected static final int NUMBER_SENT_PACKETS_OF_HANG = 10;
// how long to wait before switching back to default APN
protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000;
@@ -210,6 +210,13 @@
// represents an invalid IP address
protected static final String NULL_IP = "0.0.0.0";
+ // Default for the data stall alarm
+ protected static final int DATA_STALL_ALARM_DELAY_IN_MS_DEFAULT = 1000 * 60 * 3;
+ // If attempt is less than this value we're doing first level recovery
+ protected static final int DATA_STALL_NO_RECV_POLL_LIMIT = 1;
+ // Tag for tracking stale alarms
+ protected static final String DATA_STALL_ALARM_TAG_EXTRA = "data.stall.alram.tag";
+
// TODO: See if we can remove INTENT_RECONNECT_ALARM
// having to have different values for GSM and
// CDMA. If so we can then remove the need for
@@ -240,11 +247,19 @@
protected long mTxPkts;
protected long mRxPkts;
- protected long mSentSinceLastRecv;
protected int mNetStatPollPeriod;
- protected int mNoRecvPollCount = 0;
protected boolean mNetStatPollEnabled = false;
+ protected TxRxSum mDataStallTxRxSum = new TxRxSum(0, 0);
+ // Used to track stale data stall alarms.
+ protected int mDataStallAlarmTag = (int) SystemClock.elapsedRealtime();
+ // The current data stall alarm intent
+ protected PendingIntent mDataStallAlarmIntent = null;
+ // Number of packets sent since the last received packet
+ protected long mSentSinceLastRecv;
+ // Controls when a simple recovery attempt it to be tried
+ protected int mNoRecvPollCount = 0;
+
// wifi connection status will be updated by sticky intent
protected boolean mIsWifiConnected = false;
@@ -313,7 +328,8 @@
} else if (action.startsWith(getActionIntentReconnectAlarm())) {
log("Reconnect alarm. Previous state was " + mState);
onActionIntentReconnectAlarm(intent);
-
+ } else if (action.equals(getActionIntentDataStallAlarm())) {
+ onActionIntentDataStallAlarm(intent);
} else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
final android.net.NetworkInfo networkInfo = (NetworkInfo)
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
@@ -363,6 +379,71 @@
}
}
+ /**
+ * Maintian the sum of transmit and receive packets.
+ *
+ * The packet counts are initizlied and reset to -1 and
+ * remain -1 until they can be updated.
+ */
+ public class TxRxSum {
+ public long txPkts;
+ public long rxPkts;
+
+ public TxRxSum() {
+ reset();
+ }
+
+ public TxRxSum(long txPkts, long rxPkts) {
+ this.txPkts = txPkts;
+ this.rxPkts = rxPkts;
+ }
+
+ public TxRxSum(TxRxSum sum) {
+ txPkts = sum.txPkts;
+ rxPkts = sum.rxPkts;
+ }
+
+ public void reset() {
+ txPkts = -1;
+ rxPkts = -1;
+ }
+
+ public String toString() {
+ return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
+ }
+
+ public void updateTxRxSum() {
+ boolean txUpdated = false, rxUpdated = false;
+ long txSum = 0, rxSum = 0;
+ for (ApnContext apnContext : mApnContexts.values()) {
+ if (apnContext.getState() == State.CONNECTED) {
+ DataConnectionAc dcac = apnContext.getDataConnectionAc();
+ if (dcac == null) continue;
+
+ LinkProperties linkProp = dcac.getLinkPropertiesSync();
+ if (linkProp == null) continue;
+
+ String iface = linkProp.getInterfaceName();
+
+ if (iface != null) {
+ long stats = TrafficStats.getTxPackets(iface);
+ if (stats > 0) {
+ txUpdated = true;
+ txSum += stats;
+ }
+ stats = TrafficStats.getRxPackets(iface);
+ if (stats > 0) {
+ rxUpdated = true;
+ rxSum += stats;
+ }
+ }
+ }
+ }
+ if (txUpdated) this.txPkts = txSum;
+ if (rxUpdated) this.rxPkts = rxSum;
+ }
+ }
+
protected boolean isDataSetupCompleteOk(AsyncResult ar) {
if (ar.exception != null) {
if (DBG) log("isDataSetupCompleteOk return false, ar.result=" + ar.result);
@@ -394,6 +475,13 @@
sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
}
+ protected void onActionIntentDataStallAlarm(Intent intent) {
+ if (VDBG) log("onActionIntentDataStallAlarm: action=" + intent.getAction());
+ Message msg = obtainMessage(EVENT_DATA_STALL_ALARM, intent.getAction());
+ msg.arg1 = intent.getIntExtra(DATA_STALL_ALARM_TAG_EXTRA, 0);
+ sendMessage(msg);
+ }
+
/**
* Default constructor
*/
@@ -529,6 +617,7 @@
// abstract methods
protected abstract String getActionIntentReconnectAlarm();
+ protected abstract String getActionIntentDataStallAlarm();
protected abstract void startNetStatPoll();
protected abstract void stopNetStatPoll();
protected abstract void restartRadio();
@@ -553,6 +642,10 @@
protected abstract void onCleanUpAllConnections(String cause);
protected abstract boolean isDataPossible(String apnType);
+ protected void onDataStallAlarm(int tag) {
+ loge("onDataStallAlarm: not impleted tag=" + tag);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -575,6 +668,10 @@
onTrySetupData(reason);
break;
+ case EVENT_DATA_STALL_ALARM:
+ onDataStallAlarm(msg.arg1);
+ break;
+
case EVENT_ROAMING_OFF:
if (getDataOnRoamingEnabled() == false) {
resetAllRetryCounts();
diff --git a/telephony/java/com/android/internal/telephony/EventLogTags.logtags b/telephony/java/com/android/internal/telephony/EventLogTags.logtags
index 9be7b80..427e5da 100644
--- a/telephony/java/com/android/internal/telephony/EventLogTags.logtags
+++ b/telephony/java/com/android/internal/telephony/EventLogTags.logtags
@@ -56,3 +56,18 @@
# Bad IP address
50117 bad_ip_address (ip_address|3)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_GET_DATA_CALL_LIST
+50118 data_stall_recovery_get_data_call_list (out_packet_count|1|1)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_CLEANUP
+50119 data_stall_recovery_cleanup (out_packet_count|1|1)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_REREGISTER
+50120 data_stall_recovery_reregister (out_packet_count|1|1)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_RADIO_RESTART
+50121 data_stall_recovery_radio_restart (out_packet_count|1|1)
+
+# Data Stall Recovery mode DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP
+50122 data_stall_recovery_radio_restart_with_prop (out_packet_count|1|1)
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index f5d05a1..5889372 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -69,6 +69,10 @@
private static final String INTENT_RECONNECT_ALARM =
"com.android.internal.telephony.cdma-reconnect";
+ private static final String INTENT_DATA_STALL_ALARM =
+ "com.android.internal.telephony.cdma-data-stall";
+
+
/**
* Constants for the data connection activity:
* physical link down/up
@@ -149,6 +153,11 @@
}
@Override
+ protected String getActionIntentDataStallAlarm() {
+ return INTENT_DATA_STALL_ALARM;
+ }
+
+ @Override
protected void setState(State s) {
if (DBG) log ("setState: " + s);
if (mState != s) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index aa475e5..865caf6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -96,22 +96,37 @@
private boolean mReregisterOnReconnectFailure = false;
private ContentResolver mResolver;
- // Count of PDP reset attempts; reset when we see incoming,
- // call reRegisterNetwork, or pingTest succeeds.
- private int mPdpResetCount = 0;
-
// Recovery action taken in case of data stall
- enum RecoveryAction {REREGISTER, RADIO_RESTART, RADIO_RESET};
- private RecoveryAction mRecoveryAction = RecoveryAction.REREGISTER;
-
+ class RecoveryAction {
+ public static final int GET_DATA_CALL_LIST = 0;
+ public static final int CLEANUP = 1;
+ public static final int REREGISTER = 2;
+ public static final int RADIO_RESTART = 3;
+ public static final int RADIO_RESTART_WITH_PROP = 4;
+ }
+ public int getRecoveryAction() {
+ int action = Settings.System.getInt(mPhone.getContext().getContentResolver(),
+ "radio.data.stall.recovery.action", RecoveryAction.GET_DATA_CALL_LIST);
+ if (VDBG) log("getRecoveryAction: " + action);
+ return action;
+ }
+ public void putRecoveryAction(int action) {
+ Settings.System.putInt(mPhone.getContext().getContentResolver(),
+ "radio.data.stall.recovery.action", action);
+ if (VDBG) log("putRecoveryAction: " + action);
+ }
//***** Constants
private static final int POLL_PDP_MILLIS = 5 * 1000;
- private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
+ private static final String INTENT_RECONNECT_ALARM =
+ "com.android.internal.telephony.gprs-reconnect";
private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type";
+ private static final String INTENT_DATA_STALL_ALARM =
+ "com.android.internal.telephony.gprs-data-stall";
+
static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
static final String APN_ID = "apn_id";
private boolean canSetPreferApn = false;
@@ -163,6 +178,11 @@
p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
EVENT_PS_RESTRICT_DISABLED, null);
+ // install reconnect intent filter for this data connection.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(INTENT_DATA_STALL_ALARM);
+ p.getContext().registerReceiver(mIntentReceiver, filter, null, p);
+
mDataConnectionTracker = this;
mResolver = mPhone.getContext().getContentResolver();
@@ -241,6 +261,11 @@
return INTENT_RECONNECT_ALARM;
}
+ @Override
+ protected String getActionIntentDataStallAlarm() {
+ return INTENT_DATA_STALL_ALARM;
+ }
+
private ApnContext addApnContext(String type) {
ApnContext apnContext = new ApnContext(type, LOG_TAG);
apnContext.setDependencyMet(false);
@@ -552,6 +577,7 @@
*/
if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
stopNetStatPoll();
+ stopDataStallAlarm();
notifyDataConnection(Phone.REASON_DATA_DETACHED);
}
@@ -560,6 +586,7 @@
if (getOverallState() == State.CONNECTED) {
if (DBG) log("onDataConnectionAttached: start polling notify attached");
startNetStatPoll();
+ startDataStallAlarm();
notifyDataConnection(Phone.REASON_DATA_ATTACHED);
} else {
// update APN availability so that APN can be enabled.
@@ -764,6 +791,8 @@
}
stopNetStatPoll();
+ stopDataStallAlarm();
+
// TODO: Do we need mRequestedApnType?
mRequestedApnType = Phone.APN_TYPE_DEFAULT;
}
@@ -1236,6 +1265,7 @@
// setState(State.CONNECTED);
mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
startNetStatPoll();
+ startDataStallAlarm();
// reset reconnect timer
apnContext.getDataConnection().resetRetryCount();
}
@@ -1250,59 +1280,62 @@
private void resetPollStats() {
mTxPkts = -1;
mRxPkts = -1;
- mSentSinceLastRecv = 0;
mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
- mNoRecvPollCount = 0;
}
private void doRecovery() {
if (getOverallState() == State.CONNECTED) {
- int maxPdpReset = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
- DEFAULT_MAX_PDP_RESET_FAIL);
- if (mPdpResetCount < maxPdpReset) {
- mPdpResetCount++;
- EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
- if (DBG) log("doRecovery() cleanup all connections mPdpResetCount < max");
+ // Go through a series of recovery steps, each action transitions to the next action
+ int recoveryAction = getRecoveryAction();
+ switch (recoveryAction) {
+ case RecoveryAction.GET_DATA_CALL_LIST:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
+ mSentSinceLastRecv);
+ if (DBG) log("doRecovery() get data call list");
+ mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
+ putRecoveryAction(RecoveryAction.CLEANUP);
+ break;
+ case RecoveryAction.CLEANUP:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv);
+ if (DBG) log("doRecovery() cleanup all connections");
cleanUpAllConnections(true, Phone.REASON_PDP_RESET);
- } else {
- mPdpResetCount = 0;
- switch (mRecoveryAction) {
- case REREGISTER:
- EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
- if (DBG) log("doRecovery() re-register getting preferred network type");
- mPhone.getServiceStateTracker().reRegisterNetwork(null);
- mRecoveryAction = RecoveryAction.RADIO_RESTART;
- break;
- case RADIO_RESTART:
- EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
- if (DBG) log("restarting radio");
- mRecoveryAction = RecoveryAction.RADIO_RESET;
- restartRadio();
- break;
- case RADIO_RESET:
- // This is in case radio restart has not recovered the data.
- // It will set an additional "gsm.radioreset" property to tell
- // RIL or system to take further action.
- // The implementation of hard reset recovery action is up to OEM product.
- // Once gsm.radioreset property is consumed, it is expected to set back
- // to false by RIL.
- EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1);
- if (DBG) log("restarting radio with reset indication");
- SystemProperties.set("gsm.radioreset", "true");
- // give 1 sec so property change can be notified.
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {}
- restartRadio();
- break;
- default:
- throw new RuntimeException("doRecovery: Invalid mRecoveryAction " +
- mRecoveryAction);
- }
+ putRecoveryAction(RecoveryAction.REREGISTER);
+ break;
+ case RecoveryAction.REREGISTER:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER,
+ mSentSinceLastRecv);
+ if (DBG) log("doRecovery() re-register");
+ mPhone.getServiceStateTracker().reRegisterNetwork(null);
+ putRecoveryAction(RecoveryAction.RADIO_RESTART);
+ break;
+ case RecoveryAction.RADIO_RESTART:
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
+ mSentSinceLastRecv);
+ if (DBG) log("restarting radio");
+ putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP);
+ restartRadio();
+ break;
+ case RecoveryAction.RADIO_RESTART_WITH_PROP:
+ // This is in case radio restart has not recovered the data.
+ // It will set an additional "gsm.radioreset" property to tell
+ // RIL or system to take further action.
+ // The implementation of hard reset recovery action is up to OEM product.
+ // Once gsm.radioreset property is consumed, it is expected to set back
+ // to false by RIL.
+ EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1);
+ if (DBG) log("restarting radio with gsm.radioreset to true");
+ SystemProperties.set("gsm.radioreset", "true");
+ // give 1 sec so property change can be notified.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {}
+ restartRadio();
+ putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
+ break;
+ default:
+ throw new RuntimeException("doRecovery: Invalid recoveryAction=" +
+ recoveryAction);
}
- } else {
- if (DBG) log("doRecovery(): ignore, we're not connected");
}
}
@@ -1340,119 +1373,130 @@
SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
}
+
+ private void updateDataStallInfo() {
+ long sent, received;
+
+ TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
+ mDataStallTxRxSum.updateTxRxSum();
+
+ if (VDBG) {
+ log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
+ " preTxRxSum=" + preTxRxSum);
+ }
+
+ sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts;
+ received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts;
+
+ if (VDBG) {
+ if (SystemProperties.getBoolean("radio.test.data.stall", false)) {
+ log("updateDataStallInfo: radio.test.data.stall true received = 0;");
+ received = 0;
+ }
+ }
+ if ( sent > 0 && received > 0 ) {
+ if (VDBG) log("updateDataStallInfo: IN/OUT");
+ mSentSinceLastRecv = 0;
+ putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
+ } else if (sent > 0 && received == 0) {
+ if (mPhone.getState() == Phone.State.IDLE) {
+ mSentSinceLastRecv += sent;
+ } else {
+ mSentSinceLastRecv = 0;
+ }
+ if (DBG) {
+ log("updateDataStallInfo: OUT sent=" + sent +
+ " mSentSinceLastRecv=" + mSentSinceLastRecv);
+ }
+ } else if (sent == 0 && received > 0) {
+ if (VDBG) log("updateDataStallInfo: IN");
+ mSentSinceLastRecv = 0;
+ putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
+ } else {
+ if (VDBG) log("updateDataStallInfo: NONE");
+ }
+ }
+
+ @Override
+ protected void onDataStallAlarm(int tag) {
+ if (mDataStallAlarmTag != tag) {
+ if (DBG) {
+ log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag);
+ }
+ return;
+ }
+ updateDataStallInfo();
+
+ int hangWatchdogTrigger = Settings.Secure.getInt(mResolver,
+ Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
+ NUMBER_SENT_PACKETS_OF_HANG);
+
+ if (mSentSinceLastRecv >= hangWatchdogTrigger) {
+ if (DBG) {
+ log("onDataStallAlarm: tag=" + tag + " do recovery action=" + getRecoveryAction());
+ }
+ sendMessage(obtainMessage(EVENT_DO_RECOVERY));
+ } else {
+ if (VDBG) {
+ log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) +
+ " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger);
+ }
+ }
+ startDataStallAlarm();
+ }
+
+
+ private void updateDataActivity() {
+ long sent, received;
+
+ Activity newActivity;
+
+ TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
+ TxRxSum curTxRxSum = new TxRxSum();
+ curTxRxSum.updateTxRxSum();
+ mTxPkts = curTxRxSum.txPkts;
+ mRxPkts = curTxRxSum.rxPkts;
+
+ if (VDBG) {
+ log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum);
+ }
+
+ if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) {
+ sent = mTxPkts - preTxRxSum.txPkts;
+ received = mRxPkts - preTxRxSum.rxPkts;
+
+ if (VDBG) log("updateDataActivity: sent=" + sent + " received=" + received);
+ if ( sent > 0 && received > 0 ) {
+ newActivity = Activity.DATAINANDOUT;
+ } else if (sent > 0 && received == 0) {
+ newActivity = Activity.DATAOUT;
+ } else if (sent == 0 && received > 0) {
+ newActivity = Activity.DATAIN;
+ } else {
+ newActivity = Activity.NONE;
+ }
+
+ if (mActivity != newActivity && mIsScreenOn) {
+ if (VDBG) log("updateDataActivity: newActivity=" + newActivity);
+ mActivity = newActivity;
+ mPhone.notifyDataActivity();
+ }
+ }
+ }
+
private Runnable mPollNetStat = new Runnable()
{
-
+ @Override
public void run() {
- long sent, received;
- long preTxPkts = -1, preRxPkts = -1;
+ updateDataActivity();
- Activity newActivity;
-
- preTxPkts = mTxPkts;
- preRxPkts = mRxPkts;
-
- long txSum = 0, rxSum = 0;
- for (ApnContext apnContext : mApnContexts.values()) {
- if (apnContext.getState() == State.CONNECTED) {
- DataConnectionAc dcac = apnContext.getDataConnectionAc();
- if (dcac == null) continue;
-
- LinkProperties linkProp = dcac.getLinkPropertiesSync();
- if (linkProp == null) continue;
-
- String iface = linkProp.getInterfaceName();
-
- if (iface != null) {
- long stats = TrafficStats.getTxPackets(iface);
- if (stats > 0) txSum += stats;
- stats = TrafficStats.getRxPackets(iface);
- if (stats > 0) rxSum += stats;
- }
- }
- }
-
- mTxPkts = txSum;
- mRxPkts = rxSum;
-
- // log("tx " + mTxPkts + " rx " + mRxPkts);
-
- if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
- sent = mTxPkts - preTxPkts;
- received = mRxPkts - preRxPkts;
-
- if ( sent > 0 && received > 0 ) {
- mSentSinceLastRecv = 0;
- newActivity = Activity.DATAINANDOUT;
- mPdpResetCount = 0;
- mRecoveryAction = RecoveryAction.REREGISTER;
- } else if (sent > 0 && received == 0) {
- if (mPhone.getState() == Phone.State.IDLE) {
- mSentSinceLastRecv += sent;
- } else {
- mSentSinceLastRecv = 0;
- }
- newActivity = Activity.DATAOUT;
- } else if (sent == 0 && received > 0) {
- mSentSinceLastRecv = 0;
- newActivity = Activity.DATAIN;
- mPdpResetCount = 0;
- mRecoveryAction = RecoveryAction.REREGISTER;
- } else if (sent == 0 && received == 0) {
- newActivity = Activity.NONE;
- } else {
- mSentSinceLastRecv = 0;
- newActivity = Activity.NONE;
- }
-
- if (mActivity != newActivity && mIsScreenOn) {
- mActivity = newActivity;
- mPhone.notifyDataActivity();
- }
- }
-
- int watchdogTrigger = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
- NUMBER_SENT_PACKETS_OF_HANG);
-
- if (mSentSinceLastRecv >= watchdogTrigger) {
- // we already have NUMBER_SENT_PACKETS sent without ack
- if (mNoRecvPollCount == 0) {
- EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED,
- mSentSinceLastRecv);
- }
-
- int noRecvPollLimit = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT);
-
- if (mNoRecvPollCount < noRecvPollLimit) {
- // It's possible the PDP context went down and we weren't notified.
- // Start polling the context list in an attempt to recover.
- if (DBG) log("Polling: no DATAIN in a while; polling PDP");
- mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
-
- mNoRecvPollCount++;
-
- // Slow down the poll interval to let things happen
- mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
- POLL_NETSTAT_SLOW_MILLIS);
- } else {
- if (DBG) log("Polling: Sent " + String.valueOf(mSentSinceLastRecv) +
- " pkts since last received start recovery process");
- mNoRecvPollCount = 0;
- sendMessage(obtainMessage(EVENT_START_RECOVERY));
- }
+ if (mIsScreenOn) {
+ mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
+ Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
} else {
- mNoRecvPollCount = 0;
- if (mIsScreenOn) {
- mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
- } else {
- mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
- Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
- POLL_NETSTAT_SCREEN_OFF_MILLIS);
- }
+ mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
+ Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
+ POLL_NETSTAT_SCREEN_OFF_MILLIS);
}
if (mNetStatPollEnabled) {
@@ -1564,6 +1608,41 @@
}
+ private void startDataStallAlarm() {
+ int delayInMs = Settings.Secure.getInt(mResolver,
+ Settings.Secure.DATA_STALL_ALARM_DELAY_IN_MS,
+ DATA_STALL_ALARM_DELAY_IN_MS_DEFAULT);
+ mDataStallAlarmTag += 1;
+ if (DBG) {
+ log("startDataStallAlarm: tag=" + mDataStallAlarmTag +
+ " delay=" + (delayInMs / 1000) + "s");
+ }
+ AlarmManager am =
+ (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+
+ Intent intent = new Intent(INTENT_DATA_STALL_ALARM);
+ intent.putExtra(DATA_STALL_ALARM_TAG_EXTRA, mDataStallAlarmTag);
+ mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent);
+ }
+
+ private void stopDataStallAlarm() {
+ AlarmManager am =
+ (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+
+ if (DBG) {
+ log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag +
+ " mDataStallAlarmIntent=" + mDataStallAlarmIntent);
+ }
+ mDataStallAlarmTag += 1;
+ if (mDataStallAlarmIntent != null) {
+ am.cancel(mDataStallAlarmIntent);
+ mDataStallAlarmIntent = null;
+ }
+ }
+
private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
ApnContext apnContext) {
if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
@@ -1928,6 +2007,7 @@
if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
if (DBG) log("onVoiceCallStarted stop polling");
stopNetStatPoll();
+ stopDataStallAlarm();
notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
}
}
@@ -1938,6 +2018,7 @@
if (isConnected()) {
if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
startNetStatPoll();
+ startDataStallAlarm();
notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
} else {
// clean slate after call end.
@@ -2249,11 +2330,7 @@
onPollPdp();
break;
- case EVENT_START_NETSTAT_POLL:
- startNetStatPoll();
- break;
-
- case EVENT_START_RECOVERY:
+ case EVENT_DO_RECOVERY:
doRecovery();
break;
@@ -2270,6 +2347,7 @@
*/
if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
stopNetStatPoll();
+ stopDataStallAlarm();
mIsPsRestricted = true;
break;
@@ -2282,6 +2360,7 @@
mIsPsRestricted = false;
if (isConnected()) {
startNetStatPoll();
+ startDataStallAlarm();
} else {
// TODO: Should all PDN states be checked to fail?
if (mState == State.FAILED) {
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index e981da7..00fae2e 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -211,7 +211,7 @@
static final int CMD_STOP_SUPPLICANT = BASE + 12;
/* Start the driver */
static final int CMD_START_DRIVER = BASE + 13;
- /* Start the driver */
+ /* Stop the driver */
static final int CMD_STOP_DRIVER = BASE + 14;
/* Indicates Static IP succeded */
static final int CMD_STATIC_IP_SUCCESS = BASE + 15;
@@ -219,6 +219,9 @@
static final int CMD_STATIC_IP_FAILURE = BASE + 16;
/* Indicates supplicant stop failed */
static final int CMD_STOP_SUPPLICANT_FAILED = BASE + 17;
+ /* Delayed stop to avoid shutting down driver too quick*/
+ static final int CMD_DELAYED_STOP_DRIVER = BASE + 18;
+
/* Start the soft access point */
static final int CMD_START_AP = BASE + 21;
@@ -389,6 +392,13 @@
private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
private long mLastEnableAllNetworksTime;
+ /**
+ * Starting and shutting down driver too quick causes problems leading to driver
+ * being in a bad state. Delay driver stop.
+ */
+ private static final int DELAYED_DRIVER_STOP_MS = 2 * 60 * 1000; /* 2 minutes */
+ private int mDelayedStopCounter;
+ private boolean mInDelayedStop = false;
private static final int MIN_RSSI = -200;
private static final int MAX_RSSI = 256;
@@ -1651,6 +1661,7 @@
mDhcpInfoInternal = dhcpInfoInternal;
}
mLastSignalLevel = -1; // force update of signal strength
+ mReconnectCount = 0; //Reset IP failure tracking
WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
InetAddress addr = NetworkUtils.numericToInetAddress(dhcpInfoInternal.ipAddress);
mWifiInfo.setInetAddress(addr);
@@ -1779,6 +1790,7 @@
case CMD_STOP_SUPPLICANT_FAILED:
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
+ case CMD_DELAYED_STOP_DRIVER:
case CMD_START_AP:
case CMD_START_AP_SUCCESS:
case CMD_START_AP_FAILURE:
@@ -2441,6 +2453,7 @@
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
mIsRunning = true;
+ mInDelayedStop = false;
updateBatteryWorkSource(null);
/**
@@ -2520,6 +2533,30 @@
WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive);
break;
case CMD_STOP_DRIVER:
+ /* Already doing a delayed stop */
+ if (mInDelayedStop) {
+ if (DBG) log("Already in delayed stop");
+ break;
+ }
+ mInDelayedStop = true;
+ mDelayedStopCounter++;
+ if (DBG) log("Delayed stop message " + mDelayedStopCounter);
+ sendMessageDelayed(obtainMessage(CMD_DELAYED_STOP_DRIVER, mDelayedStopCounter,
+ 0), DELAYED_DRIVER_STOP_MS);
+ break;
+ case CMD_START_DRIVER:
+ if (mInDelayedStop) {
+ mInDelayedStop = false;
+ mDelayedStopCounter++;
+ if (DBG) log("Delayed stop ignored due to start");
+ }
+ break;
+ case CMD_DELAYED_STOP_DRIVER:
+ if (message.arg1 != mDelayedStopCounter) break;
+ if (getCurrentState() != mDisconnectedState) {
+ WifiNative.disconnectCommand();
+ handleNetworkDisconnect();
+ }
mWakeLock.acquire();
WifiNative.stopDriverCommand();
transitionTo(mDriverStoppingState);
@@ -2878,10 +2915,6 @@
/* Ignore */
case WifiMonitor.NETWORK_CONNECTION_EVENT:
break;
- case CMD_STOP_DRIVER:
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- break;
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
sendMessage(CMD_DISCONNECT);
@@ -2936,10 +2969,6 @@
WifiNative.disconnectCommand();
transitionTo(mDisconnectingState);
break;
- case CMD_STOP_DRIVER:
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- break;
case CMD_REQUEST_CM_WAKELOCK:
checkAndSetConnectivityInstance();
mCm.requestNetworkTransitionWakelock(TAG);
@@ -3035,9 +3064,6 @@
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
switch (message.what) {
- case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
- deferMessage(message);
- break;
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
deferMessage(message);