Merge "Fix null ptr exception in getReadingLevelScale()" into ics-mr0
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 62bb965..54a89ad 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -197,7 +197,10 @@
                 String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
                 mNetworkInfo.setRoaming(intent.getBooleanExtra(Phone.DATA_NETWORK_ROAMING_KEY,
                         false));
-
+                if (VDBG) {
+                    log(mApnType + " setting isAvailable to " +
+                            intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY,false));
+                }
                 mNetworkInfo.setIsAvailable(!intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY,
                         false));
 
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 1735592..7df0193 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -132,6 +132,9 @@
      * Returns an indication of whether this network is available for
      * connections. A value of {@code false} means that some quasi-permanent
      * condition prevents connectivity to this network.
+     *
+     * NOTE that this is broken on multi-connection devices.  Should be fixed in J release
+     * TODO - fix on multi-pdp devices
      */
     public boolean isAvailable();
 
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index b96099e..2ecf307 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -35,6 +35,28 @@
  * SpellCheckerService provides an abstract base class for a spell checker.
  * This class combines a service to the system with the spell checker service interface that
  * spell checker must implement.
+ *
+ * <p>In addition to the normal Service lifecycle methods, this class
+ * introduces a new specific callback that subclasses should override
+ * {@link #createSession()} to provide a spell checker session that is corresponding
+ * to requested language and so on. The spell checker session returned by this method
+ * should extend {@link SpellCheckerService.Session}.
+ * </p>
+ *
+ * <h3>Returning spell check results</h3>
+ *
+ * <p>{@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
+ * should return spell check results.
+ * It receives {@link android.view.textservice.TextInfo} and returns
+ * {@link android.view.textservice.SuggestionsInfo} for the input.
+ * You may want to override
+ * {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} for
+ * better performance and quality.
+ * </p>
+ *
+ * <p>Please note that {@link SpellCheckerService.Session#getLocale()} does not return a valid
+ * locale before {@link SpellCheckerService.Session#onCreate()} </p>
+ *
  */
 public abstract class SpellCheckerService extends Service {
     private static final String TAG = SpellCheckerService.class.getSimpleName();
@@ -89,7 +111,7 @@
          * but will be called in series on another thread.
          * @param textInfo the text metadata
          * @param suggestionsLimit the number of limit of suggestions returned
-         * @return SuggestionInfo which contains suggestions for textInfo
+         * @return SuggestionsInfo which contains suggestions for textInfo
          */
         public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit);
 
@@ -101,7 +123,7 @@
          * @param textInfos an array of the text metadata
          * @param suggestionsLimit the number of limit of suggestions returned
          * @param sequentialWords true if textInfos can be treated as sequential words.
-         * @return an array of SuggestionInfo of onGetSuggestions
+         * @return an array of SuggestionsInfo of onGetSuggestions
          */
         public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
                 int suggestionsLimit, boolean sequentialWords) {
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 34c9c29..93caabe 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -35,6 +35,13 @@
  * Subtype can describe locale (e.g. en_US, fr_FR...) and mode (e.g. voice, keyboard...), and is
  * used for IME switch and settings. The input method subtype allows the system to bring up the
  * specified subtype of the designated input method directly.
+ *
+ * <p>It should be defined in an XML resource file of the input method
+ * with the <code>&lt;subtype></code> element.
+ * For more information, see the guide to
+ * <a href="{@docRoot}resources/articles/creating-input-method.html">
+ * Creating an Input Method</a>.</p>
+ *
  */
 public final class InputMethodSubtype implements Parcelable {
     private static final String TAG = InputMethodSubtype.class.getSimpleName();
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index b940b80..793f514 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -34,6 +34,51 @@
 
 /**
  * The SpellCheckerSession interface provides the per client functionality of SpellCheckerService.
+ *
+ *
+ * <a name="Applications"></a>
+ * <h3>Applications</h3>
+ *
+ * <p>In most cases, applications that are using the standard
+ * {@link android.widget.TextView} or its subclasses will have little they need
+ * to do to work well with spell checker services.  The main things you need to
+ * be aware of are:</p>
+ *
+ * <ul>
+ * <li> Properly set the {@link android.R.attr#inputType} in your editable
+ * text views, so that the spell checker will have enough context to help the
+ * user in editing text in them.
+ * </ul>
+ *
+ * <p>For the rare people amongst us writing client applications that use the spell checker service
+ * directly, you will need to use {@link #getSuggestions(TextInfo, int)} or
+ * {@link #getSuggestions(TextInfo[], int, boolean)} for obtaining results from the spell checker
+ * service by yourself.</p>
+ *
+ * <h3>Security</h3>
+ *
+ * <p>There are a lot of security issues associated with spell checkers,
+ * since they could monitor all the text being sent to them
+ * through, for instance, {@link android.widget.TextView}.
+ * The Android spell checker framework also allows
+ * arbitrary third party spell checkers, so care must be taken to restrict their
+ * selection and interactions.</p>
+ *
+ * <p>Here are some key points about the security architecture behind the
+ * spell checker framework:</p>
+ *
+ * <ul>
+ * <li>Only the system is allowed to directly access a spell checker framework's
+ * {@link android.service.textservice.SpellCheckerService} interface, via the
+ * {@link android.Manifest.permission#BIND_TEXT_SERVICE} permission.  This is
+ * enforced in the system by not binding to a spell checker service that does
+ * not require this permission.
+ *
+ * <li>The user must explicitly enable a new spell checker in settings before
+ * they can be enabled, to confirm with the system that they know about it
+ * and want to make it available for use.
+ * </ul>
+ *
  */
 public class SpellCheckerSession {
     private static final String TAG = SpellCheckerSession.class.getSimpleName();
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index b06c112..69f88a5 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -35,6 +35,31 @@
  *
  * The user can change the current text services in Settings. And also applications can specify
  * the target text services.
+ *
+ * <h3>Architecture Overview</h3>
+ *
+ * <p>There are three primary parties involved in the text services
+ * framework (TSF) architecture:</p>
+ *
+ * <ul>
+ * <li> The <strong>text services manager</strong> as expressed by this class
+ * is the central point of the system that manages interaction between all
+ * other parts.  It is expressed as the client-side API here which exists
+ * in each application context and communicates with a global system service
+ * that manages the interaction across all processes.
+ * <li> A <strong>text service</strong> implements a particular
+ * interaction model allowing the client application to retrieve information of text.
+ * The system binds to the current text service that is in use, causing it to be created and run.
+ * <li> Multiple <strong>client applications</strong> arbitrate with the text service
+ * manager for connections to text services.
+ * </ul>
+ *
+ * <h3>Text services sessions</h3>
+ * <ul>
+ * <li>The <strong>spell checker session</strong> is one of the text services.
+ * {@link android.view.textservice.SpellCheckerSession}</li>
+ * </ul>
+ *
  */
 public final class TextServicesManager {
     private static final String TAG = TextServicesManager.class.getSimpleName();
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
index 66edb7a..8d92b05 100755
--- a/data/sounds/AudioPackage7.mk
+++ b/data/sounds/AudioPackage7.mk
@@ -62,6 +62,6 @@
 	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
 	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
 	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
 	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
 	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/ringtones/ogg/Themos.ogg b/data/sounds/ringtones/ogg/Themos.ogg
new file mode 100644
index 0000000..bc850b8
--- /dev/null
+++ b/data/sounds/ringtones/ogg/Themos.ogg
Binary files differ
diff --git a/data/sounds/ringtones/wav/Themos.wav b/data/sounds/ringtones/wav/Themos.wav
new file mode 100644
index 0000000..d4d5c6e
--- /dev/null
+++ b/data/sounds/ringtones/wav/Themos.wav
Binary files differ
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index e0c2b3b..1d20e248 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -517,8 +517,8 @@
         ensureValidDirection(direction);
         ensureValidStreamType(streamType);
 
-
-        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
+        int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType];
+        VolumeStreamState streamState = mStreamStates[streamTypeAlias];
         final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
         boolean adjustVolume = true;
 
@@ -527,14 +527,14 @@
         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
              (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL &&
                streamType != AudioSystem.STREAM_BLUETOOTH_SCO) ||
-                (mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
+                (mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_RING)) {
             //  do not vibrate if already in silent mode
             if (mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
                 flags &= ~AudioManager.FLAG_VIBRATE;
             }
             // Check if the ringer mode changes with this volume adjustment. If
             // it does, it will handle adjusting the volume, so we won't below
-            adjustVolume = checkForRingerModeChange(oldIndex, direction);
+            adjustVolume = checkForRingerModeChange(oldIndex, direction, streamTypeAlias);
         }
 
         // If stream is muted, adjust last audible index only
@@ -551,7 +551,7 @@
             if (adjustVolume && streamState.adjustIndex(direction)) {
                 // Post message to set system volume (it in turn will post a message
                 // to persist). Do not change volume if stream is muted.
-                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
+                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamTypeAlias, SENDMSG_NOOP, 0, 0,
                         streamState, 0);
             }
             index = streamState.mIndex;
@@ -567,6 +567,23 @@
 
         final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
 
+        // setting ring or notifications volume to 0 on voice capable devices enters silent mode
+        if (mVoiceCapable && (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
+                (STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING))) {
+            int newRingerMode = mRingerMode;
+            if (index == 0) {
+                newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
+                    ? AudioManager.RINGER_MODE_VIBRATE
+                    : AudioManager.RINGER_MODE_SILENT;
+                setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
+            } else {
+                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
+            }
+            if (newRingerMode != mRingerMode) {
+                setRingerMode(newRingerMode);
+            }
+        }
+
         index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
         setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
 
@@ -692,6 +709,13 @@
             if (isStreamMutedByRingerMode(streamType)) {
                 if (!isStreamAffectedByRingerMode(streamType) ||
                     mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+                    // ring and notifications volume should never be 0 when not silenced
+                    // on voice capable devices
+                    if (mVoiceCapable &&
+                            STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING &&
+                            mStreamStates[streamType].mLastAudibleIndex == 0) {
+                        mStreamStates[streamType].mLastAudibleIndex = 10;
+                    }
                     mStreamStates[streamType].mute(null, false);
                     mRingerModeMutedStreams &= ~(1 << streamType);
                 }
@@ -1593,7 +1617,7 @@
      * adjusting volume. If so, this will set the proper ringer mode and volume
      * indices on the stream states.
      */
-    private boolean checkForRingerModeChange(int oldIndex, int direction) {
+    private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) {
         boolean adjustVolumeIndex = true;
         int newRingerMode = mRingerMode;
         int uiIndex = (oldIndex + 5) / 10;
@@ -1608,7 +1632,8 @@
                         ? AudioManager.RINGER_MODE_VIBRATE
                         : AudioManager.RINGER_MODE_SILENT;
                 }
-                if (uiIndex == 0) {
+                if (uiIndex == 0 || (mPrevVolDirection == AudioManager.ADJUST_LOWER &&
+                        mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
                     adjustVolumeIndex = false;
                 }
             }
@@ -1616,13 +1641,8 @@
             if (direction == AudioManager.ADJUST_RAISE) {
                 // exiting silent mode
                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
-                if (uiIndex != 0) {
-                    adjustVolumeIndex = false;
-                }
-            } else {
-                // prevent last audible index to reach 0
-                adjustVolumeIndex = false;
             }
+            adjustVolumeIndex = false;
         }
 
         if (newRingerMode != mRingerMode) {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 4f183f5..1e1de04 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -279,6 +279,8 @@
 void NuCachedSource2::fetchInternal() {
     LOGV("fetchInternal");
 
+    bool reconnect = false;
+
     {
         Mutex::Autolock autoLock(mLock);
         CHECK(mFinalStatus == OK || mNumRetriesLeft > 0);
@@ -286,18 +288,24 @@
         if (mFinalStatus != OK) {
             --mNumRetriesLeft;
 
-            status_t err =
-                mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize());
+            reconnect = true;
+        }
+    }
 
-            if (err == ERROR_UNSUPPORTED) {
-                mNumRetriesLeft = 0;
-                return;
-            } else if (err != OK) {
-                LOGI("The attempt to reconnect failed, %d retries remaining",
-                     mNumRetriesLeft);
+    if (reconnect) {
+        status_t err =
+            mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize());
 
-                return;
-            }
+        Mutex::Autolock autoLock(mLock);
+
+        if (err == ERROR_UNSUPPORTED) {
+            mNumRetriesLeft = 0;
+            return;
+        } else if (err != OK) {
+            LOGI("The attempt to reconnect failed, %d retries remaining",
+                 mNumRetriesLeft);
+
+            return;
         }
     }
 
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index f306e4a..171710a 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -97,6 +97,16 @@
             setCameraFree(i);
         }
     }
+
+    // Read the system property to determine if we have to use the
+    // AUDIO_STREAM_ENFORCED_AUDIBLE type.
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.camera.sound.forced", value, "0");
+    if (strcmp(value, "0") != 0) {
+        mAudioStreamType = AUDIO_STREAM_ENFORCED_AUDIBLE;
+    } else {
+        mAudioStreamType = AUDIO_STREAM_MUSIC;
+    }
 }
 
 CameraService::~CameraService() {
@@ -282,21 +292,10 @@
 // A reference count is kept to determine when we will actually release the
 // media players.
 
-static MediaPlayer* newMediaPlayer(const char *file) {
-    // Read the system property to determine if we have need to use the
-    // AUDIO_STREAM_ENFORCED_AUDIBLE type.
-    char value[PROPERTY_VALUE_MAX];
-    property_get("ro.camera.sound.forced", value, "0");
-    int audioStreamType;
-    if (strcmp(value, "0") != 0) {
-        audioStreamType = AUDIO_STREAM_ENFORCED_AUDIBLE;
-    } else {
-        audioStreamType = AUDIO_STREAM_MUSIC;
-    }
-
+MediaPlayer* CameraService::newMediaPlayer(const char *file) {
     MediaPlayer* mp = new MediaPlayer();
     if (mp->setDataSource(file, NULL) == NO_ERROR) {
-        mp->setAudioStreamType(audioStreamType);
+        mp->setAudioStreamType(mAudioStreamType);
         mp->prepare();
     } else {
         LOGE("Failed to load CameraService sounds: %s", file);
@@ -335,7 +334,7 @@
         // do not play the sound if stream volume is 0
         // (typically because ringer mode is silent).
         int index;
-        AudioSystem::getStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, &index);
+        AudioSystem::getStreamVolumeIndex(mAudioStreamType, &index);
         if (index != 0) {
             player->seekTo(0);
             player->start();
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 57abf83..cdfbc56 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -76,6 +76,9 @@
     void                setCameraFree(int cameraId);
 
     // sounds
+    audio_stream_type_t mAudioStreamType;
+    MediaPlayer*        newMediaPlayer(const char *file);
+
     Mutex               mSoundLock;
     sp<MediaPlayer>     mSoundPlayer[NUM_SOUNDS];
     int                 mSoundRef;  // reference count (release all MediaPlayer when 0)
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index ae152fb..759a84b 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -254,9 +254,6 @@
     /** CID of active data connection */
     protected int mCidActive;
 
-    /** indication of our availability (preconditions to trysetupData are met) **/
-    protected boolean mAvailability = false;
-
     // When false we will not auto attach and manually attaching is required.
     protected boolean mAutoAttachOnCreation = false;
 
@@ -755,7 +752,7 @@
                 mPhone.notifyDataConnection(reason, apnIdToType(id));
             }
         }
-        notifyDataAvailability(reason);
+        notifyOffApnsOfAvailability(reason);
     }
 
     // a new APN has gone active and needs to send events to catch up with the
@@ -783,15 +780,8 @@
     }
 
     // disabled apn's still need avail/unavail notificiations - send them out
-    protected void notifyOffApnsOfAvailability(String reason, boolean availability) {
-        if (mAvailability == availability) {
-            if (DBG) {
-                log("notifyOffApnsOfAvailability: no change in availability, " +
-                     "not nofitying about reason='" + reason + "' availability=" + availability);
-            }
-            return;
-        }
-        mAvailability = availability;
+    protected void notifyOffApnsOfAvailability(String reason) {
+        if (DBG) log("notifyOffApnsOfAvailability - reason= " + reason);
         for (int id = 0; id < APN_NUM_TYPES; id++) {
             if (!isApnIdEnabled(id)) {
                 notifyApnIdDisconnected(reason, id);
@@ -799,13 +789,6 @@
         }
     }
 
-    // we had an availability change - tell the listeners
-    protected void notifyDataAvailability(String reason) {
-        // note that we either just turned all off because we lost availability
-        // or all were off and could now go on, so only have off apns to worry about
-        notifyOffApnsOfAvailability(reason, isDataPossible(Phone.APN_TYPE_DEFAULT));
-    }
-
     public boolean isApnTypeEnabled(String apnType) {
         if (apnType == null) {
             return false;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 4dace82..3c7ff05 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -237,7 +237,7 @@
             // FIXME  this can be improved
             setState(State.CONNECTED);
             notifyDataConnection(reason);
-            notifyOffApnsOfAvailability(reason, true);
+            notifyOffApnsOfAvailability(reason);
 
             log("(fix?) We're on the simulator; assuming data is connected");
             return true;
@@ -250,10 +250,10 @@
         if ((mState == State.IDLE || mState == State.SCANNING) &&
                 isDataAllowed() && getAnyDataEnabled()) {
             boolean retValue = setupData(reason);
-            notifyOffApnsOfAvailability(reason, retValue);
+            notifyOffApnsOfAvailability(reason);
             return retValue;
         } else {
-            notifyOffApnsOfAvailability(reason, false);
+            notifyOffApnsOfAvailability(reason);
             return false;
         }
     }
@@ -276,7 +276,7 @@
         }
 
         setState(State.DISCONNECTING);
-        notifyDataAvailability(reason);
+        notifyOffApnsOfAvailability(reason);
 
         boolean notificationDeferred = false;
         for (DataConnection conn : mDataConnections.values()) {
@@ -536,7 +536,7 @@
 
     private void notifyNoData(FailCause lastFailCauseCode) {
         setState(State.FAILED);
-        notifyDataAvailability(null);
+        notifyOffApnsOfAvailability(null);
     }
 
     protected void gotoIdleAndNotifyDataConnection(String reason) {
@@ -583,7 +583,7 @@
     @Override
     protected void onRoamingOff() {
         if (getDataOnRoamingEnabled() == false) {
-            notifyDataAvailability(Phone.REASON_ROAMING_OFF);
+            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
             trySetupData(Phone.REASON_ROAMING_OFF);
         } else {
             notifyDataConnection(Phone.REASON_ROAMING_OFF);
@@ -601,7 +601,7 @@
         } else {
             if (DBG) log("Tear down data connection on roaming.");
             cleanUpAllConnections(null);
-            notifyDataAvailability(Phone.REASON_ROAMING_ON);
+            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
         }
     }
 
@@ -619,7 +619,7 @@
             log("We're on the simulator; assuming data is connected");
         }
 
-        notifyDataAvailability(null);
+        notifyOffApnsOfAvailability(null);
 
         if (mState != State.IDLE) {
             cleanUpAllConnections(null);
@@ -710,7 +710,7 @@
         if (mState == State.CONNECTED && !mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) {
             stopNetStatPoll();
             notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
-            notifyDataAvailability(Phone.REASON_VOICE_CALL_STARTED);
+            notifyOffApnsOfAvailability(Phone.REASON_VOICE_CALL_STARTED);
         }
     }
 
@@ -727,7 +727,7 @@
                 // clean slate after call end.
                 resetPollStats();
             }
-            notifyDataAvailability(Phone.REASON_VOICE_CALL_ENDED);
+            notifyOffApnsOfAvailability(Phone.REASON_VOICE_CALL_ENDED);
         } else {
             mDataConnections.get(0).resetRetryCount();
             // in case data setup was attempted when we were on a voice call
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 78ba7dd..5497b7f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -556,7 +556,7 @@
             notifyDataConnection(Phone.REASON_DATA_ATTACHED);
         } else {
             // update APN availability so that APN can be enabled.
-            notifyDataAvailability(Phone.REASON_DATA_ATTACHED);
+            notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
         }
 
         setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED);
@@ -693,7 +693,7 @@
                 if (waitingApns.isEmpty()) {
                     if (DBG) log("trySetupData: No APN found");
                     notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext);
-                    notifyOffApnsOfAvailability(apnContext.getReason(), false);
+                    notifyOffApnsOfAvailability(apnContext.getReason());
                     return false;
                 } else {
                     apnContext.setWaitingApns(waitingApns);
@@ -708,7 +708,7 @@
             }
             // apnContext.setReason(apnContext.getReason());
             boolean retValue = setupData(apnContext);
-            notifyOffApnsOfAvailability(apnContext.getReason(), retValue);
+            notifyOffApnsOfAvailability(apnContext.getReason());
             return retValue;
         } else {
             // TODO: check the condition.
@@ -716,23 +716,25 @@
                 && (apnContext.getState() == State.IDLE
                     || apnContext.getState() == State.SCANNING))
                 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
-            notifyOffApnsOfAvailability(apnContext.getReason(), false);
+            notifyOffApnsOfAvailability(apnContext.getReason());
             return false;
         }
     }
 
     @Override
     // Disabled apn's still need avail/unavail notificiations - send them out
-    protected void notifyOffApnsOfAvailability(String reason, boolean availability) {
-        if (mAvailability == availability) return;
-        mAvailability = availability;
-
+    protected void notifyOffApnsOfAvailability(String reason) {
         for (ApnContext apnContext : mApnContexts.values()) {
             if (!apnContext.isReady()) {
                 if (DBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
                 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
                                             apnContext.getApnType(),
                                             Phone.DataState.DISCONNECTED);
+            } else {
+                if (DBG) {
+                    log("notifyOffApnsOfAvailability skipped apn due to isReady==false: " +
+                            apnContext.toString());
+                }
             }
         }
     }
@@ -1572,7 +1574,7 @@
         createAllApnList();
         if (mPhone.mCM.getRadioState().isOn()) {
             if (DBG) log("onRecordsLoaded: notifying data availability");
-            notifyDataAvailability(Phone.REASON_SIM_LOADED);
+            notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
         }
         setupDataOnReadyApns(Phone.REASON_SIM_LOADED);
     }
@@ -1681,7 +1683,7 @@
         if (DBG) log("onRoamingOff");
 
         if (getDataOnRoamingEnabled() == false) {
-            notifyDataAvailability(Phone.REASON_ROAMING_OFF);
+            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
             setupDataOnReadyApns(Phone.REASON_ROAMING_OFF);
         } else {
             notifyDataConnection(Phone.REASON_ROAMING_OFF);
@@ -1697,7 +1699,7 @@
         } else {
             if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
             cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
-            notifyDataAvailability(Phone.REASON_ROAMING_ON);
+            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
         }
     }
 
@@ -1714,7 +1716,7 @@
         }
 
         if (mPhone.mIccRecords.getRecordsLoaded()) {
-            notifyDataAvailability(null);
+            notifyOffApnsOfAvailability(null);
         }
 
         if (getOverallState() != State.IDLE) {
@@ -1740,7 +1742,7 @@
             if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
             cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
         }
-        notifyDataAvailability(null);
+        notifyOffApnsOfAvailability(null);
     }
 
     @Override
@@ -1985,7 +1987,7 @@
                         apnContext.getApnType());
             }
         }
-        notifyDataAvailability(reason);
+        notifyOffApnsOfAvailability(reason);
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 55e9587..b76f8b9 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -2698,6 +2698,19 @@
                             handleNetworkDisconnect();
                         }
                         transitionTo(mDriverStoppedState);
+                        break;
+                    }
+
+                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
+                    // when authentication times out after a successful connection,
+                    // we can figure this from the supplicant state. If supplicant
+                    // state is DISCONNECTED, but the mNetworkInfo says we are not
+                    // disconnected, we need to handle a disconnection
+                    if (state == SupplicantState.DISCONNECTED &&
+                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
+                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
+                        handleNetworkDisconnect();
+                        transitionTo(mDisconnectedState);
                     }
                     break;
                     /* Do a redundant disconnect without transition */