Merge "Fix crashes related to unmounting when app on sdcard has an active notification in status bar. When unmounting sdcard, all the secure containers have to be unmounted first before invoking the MountService call back. We send a broadcast about disabled packages. Force a gc to clean up any stale asset references  and then unmount the containers before invoking the MountService call back." into froyo
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 52f767e..34648b5 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -5,7 +5,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=       \
-	stagefright.cpp
+	stagefright.cpp \
+	SineSource.cpp
 
 LOCAL_SHARED_LIBRARIES := \
 	libstagefright libmedia libutils libbinder
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index fec9e1a..4405da6 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -20,9 +20,12 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "SineSource.h"
+
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <media/IMediaPlayerService.h>
+#include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/CachingDataSource.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/HTTPDataSource.h>
@@ -42,6 +45,7 @@
 static long gMaxNumFrames;  // 0 means decode all available.
 static long gReproduceBug;  // if not -1.
 static bool gPreferSoftwareCodec;
+static bool gPlaybackAudio;
 
 static int64_t getNowUs() {
     struct timeval tv;
@@ -73,7 +77,20 @@
 
     rawSource->start();
 
-    if (gReproduceBug >= 3 && gReproduceBug <= 5) {
+    if (gPlaybackAudio) {
+        AudioPlayer *player = new AudioPlayer(NULL);
+        player->setSource(rawSource);
+
+        player->start(true /* sourceAlreadyStarted */);
+
+        status_t finalStatus;
+        while (!player->reachedEOS(&finalStatus)) {
+            usleep(100000ll);
+        }
+
+        delete player;
+        player = NULL;
+    } else if (gReproduceBug >= 3 && gReproduceBug <= 5) {
         int64_t durationUs;
         CHECK(meta->findInt64(kKeyDuration, &durationUs));
 
@@ -245,6 +262,7 @@
     fprintf(stderr, "       -p(rofiles) dump decoder profiles supported\n");
     fprintf(stderr, "       -t(humbnail) extract video thumbnail or album art\n");
     fprintf(stderr, "       -s(oftware) prefer software codec\n");
+    fprintf(stderr, "       -o playback audio\n");
 }
 
 int main(int argc, char **argv) {
@@ -258,9 +276,10 @@
     gMaxNumFrames = 0;
     gReproduceBug = -1;
     gPreferSoftwareCodec = false;
+    gPlaybackAudio = false;
 
     int res;
-    while ((res = getopt(argc, argv, "han:lm:b:pts")) >= 0) {
+    while ((res = getopt(argc, argv, "han:lm:b:ptso")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -314,6 +333,12 @@
                 break;
             }
 
+            case 'o':
+            {
+                gPlaybackAudio = true;
+                break;
+            }
+
             case '?':
             case 'h':
             default:
@@ -325,6 +350,11 @@
         }
     }
 
+    if (gPlaybackAudio && !audioOnly) {
+        // This doesn't make any sense if we're decoding the video track.
+        gPlaybackAudio = false;
+    }
+
     argc -= optind;
     argv += optind;
 
@@ -456,6 +486,11 @@
             dataSource = new FileSource(filename);
         }
 
+        if (dataSource == NULL) {
+            fprintf(stderr, "Unable to create data source.\n");
+            return 1;
+        }
+
         bool isJPEG = false;
 
         size_t len = strlen(filename);
@@ -467,10 +502,18 @@
 
         if (isJPEG) {
             mediaSource = new JPEGSource(dataSource);
+        } else if (!strncasecmp("sine:", filename, 5)) {
+            char *end;
+            long sampleRate = strtol(filename + 5, &end, 10);
+
+            if (end == filename + 5) {
+                sampleRate = 44100;
+            }
+            mediaSource = new SineSource(sampleRate, 1);
         } else {
             sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
             if (extractor == NULL) {
-                fprintf(stderr, "could not create data source\n");
+                fprintf(stderr, "could not create extractor.\n");
                 return -1;
             }
 
@@ -492,6 +535,17 @@
                 if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
                     break;
                 }
+
+                meta = NULL;
+            }
+
+            if (meta == NULL) {
+                fprintf(stderr,
+                        "No suitable %s track found. The '-a' option will "
+                        "target audio tracks only, the default is to target "
+                        "video tracks only.\n",
+                        audioOnly ? "audio" : "video");
+                return -1;
             }
 
             int64_t thumbTimeUs;
diff --git a/core/java/android/app/backup/BackupAgentHelper.java b/core/java/android/app/backup/BackupAgentHelper.java
index 788b1b5..6d73090 100644
--- a/core/java/android/app/backup/BackupAgentHelper.java
+++ b/core/java/android/app/backup/BackupAgentHelper.java
@@ -21,16 +21,28 @@
 import java.io.IOException;
 
 /**
- * A convenient BackupAgent wrapper class that automatically manages
+ * A convenient {@link BackupAgent} wrapper class that automatically manages
  * heterogeneous data sets within the backup data, each identified by a unique
- * key prefix. An application will typically extend this class in their own
- * backup agent. Then, within the agent's onBackup() and onRestore() methods, it
- * will call {@link #addHelper(String, BackupHelper)} one or more times to
- * specify the data sets, then invoke super.onBackup() or super.onRestore() to
- * have the BackupAgentHelper implementation process the data.
+ * key prefix.  When processing a backup or restore operation, the BackupAgentHelper
+ * dispatches to one or more installed {@link BackupHelper helpers} objects, each
+ * of which is responsible for a defined subset of the data being processed.
  * <p>
- * STOPSHIP: document!
+ * An application will typically extend this class in their own
+ * backup agent. Then, within the agent's {@link BackupAgent#onCreate() onCreate()}
+ * method, it will call {@link #addHelper(String, BackupHelper)} one or more times to
+ * install the handlers for each kind of data it wishes to manage within its backups.
+ * <p>
+ * The Android framework currently provides two predefined {@link BackupHelper} classes:
+ * {@link FileBackupHelper}, which manages the backup and restore of entire files
+ * within an application's data directory hierarchy; and {@link SharedPreferencesBackupHelper},
+ * which manages the backup and restore of an application's
+ * {@link android.content.SharedPreferences} data.
+ * <p>
+ * An application can also implement its own helper classes to work within the
+ * {@link BackupAgentHelper} framework.  See the {@link BackupHelper} interface
+ * documentation for details.
  *
+ * @see BackupHelper
  * @see FileBackupHelper
  * @see SharedPreferencesBackupHelper
  */
diff --git a/core/java/android/app/backup/BackupDataInputStream.java b/core/java/android/app/backup/BackupDataInputStream.java
index a7f4ba6..465b3b6 100644
--- a/core/java/android/app/backup/BackupDataInputStream.java
+++ b/core/java/android/app/backup/BackupDataInputStream.java
@@ -20,7 +20,21 @@
 import java.io.IOException;
 
 /**
- * STOPSHIP: document */
+ * Used by {@link BackupHelper} classes within the {@link BackupAgentHelper} mechanism,
+ * this class provides an {@link java.io.InputStream}-like interface for accessing an
+ * entity's data during a restore operation.
+ * <p>
+ * When {@link BackupHelper#restoreEntity(BackupDataInputStream) BackupHelper.restoreEntity(BackupDataInputStream)}
+ * is called, the current entity's header has already been read from the underlying
+ * {@link BackupDataInput}.  The entity's key string and total data size are available
+ * through this class's {@link #getKey()} and {@link #size()} methods, respectively.
+ * <p class="note">
+ * <em>Note:</em> The caller should take care not to seek or close the underlying data
+ * source, or to read more than {@link #size()} bytes total from the stream.</p>
+ *
+ * @see BackupAgentHelper
+ * @see BackupHelper
+ */
 public class BackupDataInputStream extends InputStream {
 
     String key;
@@ -34,6 +48,13 @@
         mData = data;
     }
 
+    /**
+     * Read one byte of entity data from the stream, returning it as
+     * an integer value.  If more than {@link #size()} bytes of data
+     * are read from the stream, the output of this method is undefined.
+     *
+     * @return The byte read, or undefined if the end of the stream has been reached.
+     */
     public int read() throws IOException {
         byte[] one = mOneByte;
         if (mOneByte == null) {
@@ -43,18 +64,52 @@
         return one[0];
     }
 
+    /**
+     * Read up to {@code size} bytes of data into a byte array, beginning at position
+     * {@code offset} within the array.
+     *
+     * @param b Byte array into which the data will be read
+     * @param offset The data will be stored in {@code b} beginning at this index
+     *   within the array.
+     * @param size The number of bytes to read in this operation.  If insufficient
+     *   data exists within the entity to fulfill this request, only as much data
+     *   will be read as is available.
+     * @return The number of bytes of data read, or zero if all of the entity's
+     *   data has already been read.
+     */
     public int read(byte[] b, int offset, int size) throws IOException {
         return mData.readEntityData(b, offset, size);
     }
 
+    /**
+     * Read enough entity data into a byte array to fill the array.
+     *
+     * @param b Byte array to fill with data from the stream.  If the stream does not
+     *   have sufficient data to fill the array, then the contents of the remainder of
+     *   the array will be undefined.
+     * @return The number of bytes of data read, or zero if all of the entity's
+     *   data has already been read.
+     */
     public int read(byte[] b) throws IOException {
         return mData.readEntityData(b, 0, b.length);
     }
 
+    /**
+     * Report the key string associated with this entity within the backup data set.
+     *
+     * @return The key string for this entity, equivalent to calling
+     *   {@link BackupDataInput#getKey()} on the underlying {@link BackupDataInput}.
+     */
     public String getKey() {
         return this.key;
     }
-    
+
+    /**
+     * Report the total number of bytes of data available for the current entity.
+     *
+     * @return The number of data bytes available, equivalent to calling
+     *   {@link BackupDataInput#getDataSize()} on the underlying {@link BackupDataInput}.
+     */
     public int size() {
         return this.dataSize;
     }
diff --git a/core/java/android/app/backup/BackupHelper.java b/core/java/android/app/backup/BackupHelper.java
index 3f41ed2..87b581b 100644
--- a/core/java/android/app/backup/BackupHelper.java
+++ b/core/java/android/app/backup/BackupHelper.java
@@ -19,11 +19,21 @@
 import android.os.ParcelFileDescriptor;
 
 /**
- * A convenient interface to be used with the
- * {@link android.app.backup.BackupAgentHelper} class to implement backup and restore of
- * arbitrary data types.
+ * This interface defines the calling interface that {@link BackupAgentHelper} uses
+ * when dispatching backup and restore operations to the installed helpers.
+ * Applications can define and install their own helpers as well as using those
+ * provided as part of the Android framework.
  * <p>
- * STOPSHIP: document!
+ * Although multiple helper objects may be installed simultaneously, each helper
+ * is responsible only for handling its own data, and will not see entities
+ * created by other components within the backup system.  Invocations of multiple
+ * helpers are performed sequentially by the {@link BackupAgentHelper}, with each
+ * helper given a chance to access its own saved state from within the state record
+ * produced during the previous backup operation.
+ *
+ * @see BackupAgentHelper
+ * @see FileBackupHelper
+ * @see SharedPreferencesBackupHelper
  */
 public interface BackupHelper {
     /**
@@ -31,24 +41,46 @@
      * application's data directory need to be backed up, write them to
      * <code>data</code>, and fill in <code>newState</code> with the state as it
      * exists now.
+     * <p>
+     * Implementing this method is much like implementing
+     * {@link BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
+     * &mdash; the method parameters are the same.  When this method is invoked the
+     * {@code oldState} descriptor points to the beginning of the state data
+     * written during this helper's previous backup operation, and the {@code newState}
+     * descriptor points to the file location at which the helper should write its
+     * new state after performing the backup operation.
+     * <p class="note">
+     * <em>Note:</em> The helper should not close or seek either the {@code oldState} or
+     * the {@code newState} file descriptors.</p>
      */
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState);
 
     /**
      * Called by {@link android.app.backup.BackupAgentHelper BackupAgentHelper}
-     * to restore one entity from the restore dataset.
-     * <p class=note>
-     * Do not close the <code>data</code> stream.  Do not read more than
-     * <code>data.size()</code> bytes from <code>data</code>.
+     * to restore a single entity from the restore data set.  This method will be
+     * called for each entity in the data set that belongs to this handler.
+     * <p class="note">
+     * <em>Note:</em> Do not close the <code>data</code> stream.  Do not read more than
+     * <code>data.size()</code> bytes from <code>data</code>.</p>
      */
     public void restoreEntity(BackupDataInputStream data);
 
     /**
      * Called by {@link android.app.backup.BackupAgentHelper BackupAgentHelper}
      * after a restore operation to write the backup state file corresponding to
-     * the data as processed by the helper.
+     * the data as processed by the helper.  The data written here will be
+     * available to the helper during the next call to its
+     * {@link #performBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
+     * method.
+     * <p>
+     * Note that this method will be called even if the handler's
+     * {@link #restoreEntity(BackupDataInputStream)} method was never invoked during
+     * the restore operation.
+     * <p class="note">
+     * <em>Note:</em> The helper should not close or seek the {@code newState}
+     * file descriptor.</p>
      */
-    public void writeNewStateDescription(ParcelFileDescriptor fd);
+    public void writeNewStateDescription(ParcelFileDescriptor newState);
 }
 
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 08cd2f0..893db2e 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -487,12 +487,6 @@
         if (state != prevState) {
             if (state == BluetoothA2dp.STATE_DISCONNECTED ||
                     state == BluetoothA2dp.STATE_DISCONNECTING) {
-                if (prevState == BluetoothA2dp.STATE_CONNECTED ||
-                        prevState == BluetoothA2dp.STATE_PLAYING) {
-                   // disconnecting or disconnected
-                   Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
-                   mContext.sendBroadcast(intent);
-                }
                 mSinkCount--;
             } else if (state == BluetoothA2dp.STATE_CONNECTED) {
                 mSinkCount ++;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4f2a67b..06a7a6f4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2150,11 +2150,15 @@
                 mScrollX = pinLocX(Math.round(sx));
                 mScrollY = pinLocY(Math.round(sy));
 
+                // update webkit
                 if (oldX != mScrollX || oldY != mScrollY) {
                     onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+                } else {
+                    // the scroll position is adjusted at the beginning of the
+                    // zoom animation. But we want to update the WebKit at the
+                    // end of the zoom animation. See comments in onScaleEnd().
+                    sendOurVisibleRect();
                 }
-
-                // update webkit
                 sendViewSizeZoom();
             }
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 87ccaaf..9ab02f0 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -102,6 +102,9 @@
     private static final int MSG_MEDIA_SERVER_DIED = 5;
     private static final int MSG_MEDIA_SERVER_STARTED = 6;
     private static final int MSG_PLAY_SOUND_EFFECT = 7;
+    private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
+
+    private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
 
     /** @see AudioSystemThread */
     private AudioSystemThread mAudioSystemThread;
@@ -1651,6 +1654,11 @@
                 case MSG_PLAY_SOUND_EFFECT:
                     playSoundEffect(msg.arg1, msg.arg2);
                     break;
+
+                case MSG_BTA2DP_DOCK_TIMEOUT:
+                    // msg.obj  == address of BTA2DP device
+                    makeA2dpDeviceUnavailableNow( (String) msg.obj );
+                    break;
             }
         }
     }
@@ -1705,6 +1713,38 @@
         }
     }
 
+    private void makeA2dpDeviceAvailable(String address) {
+        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                AudioSystem.DEVICE_STATE_AVAILABLE,
+                address);
+        // Reset A2DP suspend state each time a new sink is connected
+        AudioSystem.setParameters("A2dpSuspended=false");
+        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
+                address);
+    }
+
+    private void makeA2dpDeviceUnavailableNow(String address) {
+        Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+        mContext.sendBroadcast(noisyIntent);
+        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                AudioSystem.DEVICE_STATE_UNAVAILABLE,
+                address);
+        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+    }
+
+    private void makeA2dpDeviceUnavailableLater(String address) {
+        // the device will be made unavailable later, so consider it disconnected right away
+        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+        // send the delayed message to make the device unavailable later
+        Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
+        mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
+
+    }
+
+    private void cancelA2dpDeviceTimeout(String address) {
+        mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
+    }
+
     /**
      * Receiver for misc intent broadcasts the Phone app cares about.
      */
@@ -1739,20 +1779,25 @@
 
                 if (isConnected &&
                     state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
-                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
-                            address);
-                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+                    if (btDevice.isBluetoothDock()) {
+                        if (state == BluetoothA2dp.STATE_DISCONNECTED) {
+                            // introduction of a delay for transient disconnections of docks when
+                            // power is rapidly turned off/on, this message will be canceled if
+                            // we reconnect the dock under a preset delay
+                            makeA2dpDeviceUnavailableLater(address);
+                            // the next time isConnected is evaluated, it will be false for the dock
+                        }
+                    } else {
+                        makeA2dpDeviceUnavailableNow(address);
+                    }
                 } else if (!isConnected &&
                              (state == BluetoothA2dp.STATE_CONNECTED ||
                               state == BluetoothA2dp.STATE_PLAYING)) {
-                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
-                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
-                                                         address);
-                    // Reset A2DP suspend state each time a new sink is connected
-                    AudioSystem.setParameters("A2dpSuspended=false");
-                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
-                            address);
+                    if (btDevice.isBluetoothDock()) {
+                        // this could be a reconnection after a transient disconnection
+                        cancelA2dpDeviceTimeout(address);
+                    }
+                    makeA2dpDeviceAvailable(address);
                 }
             } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
                 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 005c64a..bcf2463 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -102,7 +102,7 @@
                 (numChannels == 2)
                     ? AudioSystem::CHANNEL_OUT_STEREO
                     : AudioSystem::CHANNEL_OUT_MONO,
-                8192, 0, &AudioCallback, this, 0);
+                0, 0, &AudioCallback, this, 0);
 
         if ((err = mAudioTrack->initCheck()) != OK) {
             delete mAudioTrack;
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 2bc8139..b14a03c 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1239,7 +1239,19 @@
     Mutex::Autolock autoLock(mLock);
 
     if (mSuspensionState != NULL) {
-        return INVALID_OPERATION;
+        if (mLastVideoBuffer == NULL) {
+            //go into here if video is suspended again
+            //after resuming without being played between
+            //them
+            SuspensionState *state = mSuspensionState;
+            mSuspensionState = NULL;
+            reset_l();
+            mSuspensionState = state;
+            return OK;
+        }
+
+        delete mSuspensionState;
+        mSuspensionState = NULL;
     }
 
     if (mFlags & PREPARING) {
@@ -1344,7 +1356,7 @@
         play_l();
     }
 
-    delete state;
+    mSuspensionState = state;
     state = NULL;
 
     return OK;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index b9d6fbc..873c2aa 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -3040,7 +3040,7 @@
             continue;
         }
 
-        OMXCodec::setComponentRole(omx, node, queryDecoders, mime);
+        OMXCodec::setComponentRole(omx, node, !queryDecoders, mime);
 
         results->push();
         CodecCapabilities *caps = &results->editItemAt(results->size() - 1);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 0c0bf93..2b4714d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -789,7 +789,8 @@
                         + " VALUES(?,?);");
     
                 // Set the timeout to 30 minutes in milliseconds
-                loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT, 30 * 60 * 1000);
+                loadSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
+                        Integer.toString(30 * 60 * 1000));
             } finally {
                 if (stmt != null) stmt.close();
             }
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index 9d4e226..08b003d 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -463,7 +463,7 @@
         private void postNotification(int titleInt, int messageInt, int icon, int flags) {
             Intent intent = new Intent();
             // TODO - fix up intent
-            intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
+            intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
             intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
 
             PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);