Merge "Include showmap output in bug report." into ics-mr1
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/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 22e86c7..754d6e9 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2416,7 +2416,11 @@
         if (mIsRestored) {
             mInitialViewState.mIsRestored = true;
             mInitialViewState.mViewScale = mRestoredScale;
-            mInitialViewState.mTextWrapScale = mRestoredTextWrapScale;
+            if (mRestoredTextWrapScale > 0) {
+                mInitialViewState.mTextWrapScale = mRestoredTextWrapScale;
+            } else {
+                mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale;
+            }
         } else {
             if (mViewportInitialScale > 0) {
                 mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
@@ -2535,9 +2539,11 @@
     // called by JNI
     private void restoreScale(float scale, float textWrapScale) {
         if (mBrowserFrame.firstLayoutDone() == false) {
-            mIsRestored = true;
+            mIsRestored = scale > 0;
             mRestoredScale = scale;
-            mRestoredTextWrapScale = textWrapScale;
+            if (mSettings.getUseWideViewPort()) {
+                mRestoredTextWrapScale = textWrapScale;
+            }
         }
     }
 
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index e829571..f599dba 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1114,7 +1114,7 @@
             float scale;
             if (mInitialScale > 0) {
                 scale = mInitialScale;
-            } else if (viewState.mIsRestored) {
+            } else if (viewState.mIsRestored || viewState.mViewScale > 0) {
                 scale = (viewState.mViewScale > 0)
                     ? viewState.mViewScale : overviewScale;
                 mTextWrapScale = (viewState.mTextWrapScale > 0)
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>&lt;activity android:name=&quot;.app.DeviceAdminSample$Controller&quot;
-          android:label=&quot;&#64;string/activity_sample_device_admin&quot;&gt;
-    &lt;intent-filter&gt;
-        &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
-        &lt;category android:name=&quot;android.intent.category.SAMPLE_CODE&quot; /&gt;
-    &lt;/intent-filter&gt;
+<pre>&lt;activity android:name=&quot;.app.DeviceAdminSample&quot;
+            android:label=&quot;&#64;string/activity_sample_device_admin&quot;&gt;
+    &lt;intent-filter&gt;
+        &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
+        &lt;category android:name=&quot;android.intent.category.SAMPLE_CODE&quot; /&gt;
+    &lt;/intent-filter&gt;
 &lt;/activity&gt;
-
-&lt;receiver android:name=&quot;.app.DeviceAdminSample&quot;
-          android:label=&quot;&#64;string/sample_device_admin&quot;
-          android:description=&quot;&#64;string/sample_device_admin_description&quot;
-          android:permission=&quot;android.permission.BIND_DEVICE_ADMIN&quot;&gt;
-    &lt;meta-data android:name=&quot;android.app.device_admin&quot;
-               android:resource=&quot;&#64;xml/device_admin_sample&quot; /&gt;
-    &lt;intent-filter&gt;
-        &lt;action android:name=&quot;android.app.action.DEVICE_ADMIN_ENABLED&quot; /&gt;
-    &lt;/intent-filter&gt;
+&lt;receiver android:name=&quot;.app.DeviceAdminSample$DeviceAdminSampleReceiver&quot;
+        android:label=&quot;&#64;string/sample_device_admin&quot;
+        android:description=&quot;&#64;string/sample_device_admin_description&quot;
+        android:permission=&quot;android.permission.BIND_DEVICE_ADMIN&quot;&gt;
+    &lt;meta-data android:name=&quot;android.app.device_admin&quot;
+            android:resource=&quot;&#64;xml/device_admin_sample&quot; /&gt;
+    &lt;intent-filter&gt;
+        &lt;action android:name=&quot;android.app.action.DEVICE_ADMIN_ENABLED&quot; /&gt;
+    &lt;/intent-filter&gt;
 &lt;/receiver&gt;</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>&quot;.app.DeviceAdminSample$Controller&quot;</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=&quot;@string/activity_sample_device_admin&quot;</code> refers to the
+<li><code>android:label=&quot;&#64;string/activity_sample_device_admin&quot;</code> refers to the
 user-readable label for the activity.</li>
 
-<li><code>android:label=&quot;@string/sample_device_admin&quot;</code> refers to the
+<li><code>android:label=&quot;&#64;string/sample_device_admin&quot;</code> refers to the
 user-readable label for the permission.</li>
 
-<li><code>android:description=&quot;@string/sample_device_admin_description&quot;</code> refers to
+<li><code>android:description=&quot;&#64;string/sample_device_admin_description&quot;</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 @@
     &lt;reset-password /&gt;
     &lt;force-lock /&gt;
     &lt;wipe-data /&gt;
+    &lt;expire-password /&gt;
+    &lt;encrypted-storage /&gt;
+    &lt;disable-camera /&gt;
   &lt;/uses-policies&gt;
 &lt;/device-admin&gt;
 </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();
+    }
+
     &#64;Override
-    public void onEnabled(Context context, Intent intent) {
-        showToast(context, &quot;Sample Device Admin: enabled&quot;);
-    }
+    public void onEnabled(Context context, Intent intent) {
+        showToast(context, context.getString(R.string.admin_receiver_status_enabled));
+    }
 
-    &#64;Override
-    public CharSequence onDisableRequested(Context context, Intent intent) {
-        return &quot;This is an optional message to warn the user about disabling.&quot;;
-    }
+    &#64;Override
+    public CharSequence onDisableRequested(Context context, Intent intent) {
+        return context.getString(R.string.admin_receiver_status_disable_warning);
+    }
 
-    &#64;Override
-    public void onDisabled(Context context, Intent intent) {
-        showToast(context, &quot;Sample Device Admin: disabled&quot;);
-    }
+    &#64;Override
+    public void onDisabled(Context context, Intent intent) {
+        showToast(context, context.getString(R.string.admin_receiver_status_disabled));
+    }
 
-    &#64;Override
-    public void onPasswordChanged(Context context, Intent intent) {
-        showToast(context, &quot;Sample Device Admin: pw changed&quot;);
-    }
-
-    void showToast(Context context, CharSequence msg) {
-        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
+    &#64;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,
-               &quot;Additional text explaining why this needs to be added.&quot;);
-        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.
-&#64;Override
-protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-    switch (requestCode) {
-        case RESULT_ENABLE:
-            if (resultCode == Activity.RESULT_OK) {
-                Log.i(&quot;DeviceAdminSample&quot;, &quot;Administration enabled!&quot;);
-            } else {
-                Log.i(&quot;DeviceAdminSample&quot;, &quot;Administration enable FAILED!&quot;);
-            }
-            return;
-    }
-    super.onActivityResult(requestCode, resultCode, data);
-}</pre>
+<pre>&#64;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 &gt;= 0) {
-            statusText = &quot;Expiration in &quot; + countdownString(mSecUntilExpiration);
-        } else {
-            statusText = &quot;Expired &quot; + countdownString(-mSecUntilExpiration) + &quot; ago&quot;;
-        }
-
-        // expirationTimeout is the cycle time between required password refresh
-        long expirationTimeout = mDPM.getPasswordExpirationTimeout(mDeviceAdminSample);
-        statusText += &quot; / timeout period &quot; + countdownString(expirationTimeout);
-
-        // Now report the aggregate (global) expiration time
-        statusText += &quot; / Aggregate &quot;;
-        expirationDate = mDPM.getPasswordExpiration(null);
-        mSecUntilExpiration = expirationDate - now;
-        if (mSecUntilExpiration &gt;= 0) {
-            statusText += &quot;expiration in &quot; + countdownString(mSecUntilExpiration);
-        } else {
-            statusText += &quot;expired &quot; + countdownString(-mSecUntilExpiration) + &quot; ago&quot;;
-        }
-    } else {
-        statusText = &quot;&lt;inactive&gt;&quot;;
-    }
-    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/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/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..a46771b 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1651,6 +1651,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);