Merge "If we defer processing the touch events, pass the original view coordinate with TouchEventData and use them when we reprocess the events. We can't depend on contentToView() to convert the doc point to the view point. When we originally convert the view to doc, the scroll position in UI doesn't change. When we convert the doc back to view, we are chasing a moving target. That is why we saw bigger delta." into froyo
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/provider/Settings.java b/core/java/android/provider/Settings.java
index e640005..e8c09b0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3306,13 +3306,13 @@
          * The bandwidth throttle threshold (long)
          * @hide
          */
-        public static final String THROTTLE_THRESHOLD = "throttle_threshold";
+        public static final String THROTTLE_THRESHOLD_BYTES = "throttle_threshold_bytes";
 
         /**
          * The bandwidth throttle value (kbps)
          * @hide
          */
-        public static final String THROTTLE_VALUE = "throttle_value";
+        public static final String THROTTLE_VALUE_KBITSPS = "throttle_value_kbitsps";
 
         /**
          * The bandwidth throttle reset calendar day (1-28)
@@ -3327,12 +3327,6 @@
         public static final String THROTTLE_NOTIFICATION_TYPE = "throttle_notification_type";
 
         /**
-         * The interface we throttle
-         * @hide
-         */
-        public static final String THROTTLE_IFACE = "throttle_iface";
-
-        /**
          * Help URI for data throttling policy
          * @hide
          */
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7ebbab0..64f05fe 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -290,4 +290,21 @@
     <!-- Boolean indicating if current platform supports bluetooth SCO for off call
     use cases -->
     <bool name="config_bluetooth_sco_off_call">true</bool>
+
+    <!-- The default data-use polling period. -->
+    <integer name="config_datause_polling_period_sec">600</integer>
+
+    <!-- The default data-use threshold in bytes. 0 disables-->
+    <integer name="config_datause_threshold_bytes">0</integer>
+
+    <!-- The default reduced-datarate value in kilobits per sec -->
+    <integer name="config_datause_throttle_kbitsps">300</integer>
+
+    <!-- The default iface on which to monitor data use -->
+    <string name="config_datause_iface">rmnet0</string>
+
+    <!-- The default reduced-datarate notification mask -->
+    <!-- 2 means give warning -->
+    <integer name="config_datause_notification_type">2</integer>
+
 </resources>
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 0e21d08..323f145 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1642,6 +1642,8 @@
     }
 
     if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
+        || !memcmp(header, "ftyp3gr6", 8) || !memcmp(header, "ftyp3gs6", 8)
+        || !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8)
         || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
         || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
         || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 3908389..78f7b33 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -615,10 +615,27 @@
                     }
                 } break;
                 case UPDATED_MEDIA_STATUS: {
-                    try {
-                        PackageHelper.getMountService().finishMediaUpdate();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "MountService not running?");
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS");
+                    boolean reportStatus = msg.arg1 == 1;
+                    boolean doGc = msg.arg2 == 1;
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "reportStatus=" + reportStatus + ", doGc = " + doGc);
+                    if (doGc) {
+                        // Force a gc to clear up stale containers.
+                        Runtime.getRuntime().gc();
+                    }
+                    if (msg.obj != null) {
+                        Set<SdInstallArgs> args = (Set<SdInstallArgs>) msg.obj;
+                        if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading all containers");
+                        // Unload containers
+                        unloadAllContainers(args);
+                    }
+                    if (reportStatus) {
+                        try {
+                            if (DEBUG_SD_INSTALL) Log.i(TAG, "Invoking MountService call back");
+                            PackageHelper.getMountService().finishMediaUpdate();
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "MountService not running?");
+                        }
                     }
                 } break;
                 case WRITE_SETTINGS: {
@@ -9429,9 +9446,9 @@
            if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
                    mediaStatus+", mMediaMounted=" + mMediaMounted);
            if (mediaStatus == mMediaMounted) {
-               if (reportStatus) {
-                   mHandler.sendEmptyMessage(UPDATED_MEDIA_STATUS);
-               }
+               Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
+                       reportStatus ? 1 : 0, -1);
+               mHandler.sendMessage(msg);
                return;
            }
            mMediaMounted = mediaStatus;
@@ -9440,38 +9457,42 @@
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
-               try {
-                   updateExternalMediaStatusInner(mediaStatus);
-               } finally {
-                   if (reportStatus) {
-                       mHandler.sendEmptyMessage(UPDATED_MEDIA_STATUS);
-                   }
-               }
+               updateExternalMediaStatusInner(mediaStatus, reportStatus);
            }
        });
    }
 
-   private void updateExternalMediaStatusInner(boolean mediaStatus) {
-       // If we are up here that means there are packages to be
-       // enabled or disabled.
+   /*
+    * Collect information of applications on external media, map them
+    * against existing containers and update information based on current
+    * mount status. Please note that we always have to report status
+    * if reportStatus has been set to true especially when unloading packages.
+    */
+   private void updateExternalMediaStatusInner(boolean mediaStatus,
+           boolean reportStatus) {
+       // Collection of uids
+       int uidArr[] = null;
+       // Collection of stale containers
+       HashSet<String> removeCids = new HashSet<String>();
+       // Collection of packages on external media with valid containers.
+       HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
+       // Get list of secure containers.
        final String list[] = PackageHelper.getSecureContainerList();
        if (list == null || list.length == 0) {
            Log.i(TAG, "No secure containers on sdcard");
-           return;
-       }
-
-       int uidList[] = new int[list.length];
-       int num = 0;
-       HashSet<String> removeCids = new HashSet<String>();
-       HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
-       synchronized (mPackages) {
-           for (String cid : list) {
-               SdInstallArgs args = new SdInstallArgs(cid);
-               if (DEBUG_SD_INSTALL) Log.i(TAG, "Processing container " + cid);
-               boolean failed = true;
-               try {
+       } else {
+           // Process list of secure containers and categorize them
+           // as active or stale based on their package internal state.
+           int uidList[] = new int[list.length];
+           int num = 0;
+           synchronized (mPackages) {
+               for (String cid : list) {
+                   SdInstallArgs args = new SdInstallArgs(cid);
+                   if (DEBUG_SD_INSTALL) Log.i(TAG, "Processing container " + cid);
                    String pkgName = args.getPackageName();
                    if (pkgName == null) {
+                       if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale");
+                       removeCids.add(cid);
                        continue;
                    }
                    if (DEBUG_SD_INSTALL) Log.i(TAG, "Looking for pkg : " + pkgName);
@@ -9483,33 +9504,29 @@
                                " at code path: " + ps.codePathString);
                        // We do have a valid package installed on sdcard
                        processCids.put(args, ps.codePathString);
-                       failed = false;
                        int uid = ps.userId;
                        if (uid != -1) {
                            uidList[num++] = uid;
                        }
-                   }
-               } finally {
-                   if (failed) {
+                   } else {
                        // Stale container on sdcard. Just delete
                        if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale");
                        removeCids.add(cid);
                    }
                }
            }
-       }
-       // Organize uids
-       int uidArr[] = null;
-       if (num > 0) {
-           // Sort uid list
-           Arrays.sort(uidList, 0, num);
-           // Throw away duplicates
-           uidArr = new int[num];
-           uidArr[0] = uidList[0];
-           int di = 0;
-           for (int i = 1; i < num; i++) {
-               if (uidList[i-1] != uidList[i]) {
-                   uidArr[di++] = uidList[i];
+
+           if (num > 0) {
+               // Sort uid list
+               Arrays.sort(uidList, 0, num);
+               // Throw away duplicates
+               uidArr = new int[num];
+               uidArr[0] = uidList[0];
+               int di = 0;
+               for (int i = 1; i < num; i++) {
+                   if (uidList[i-1] != uidList[i]) {
+                       uidArr[di++] = uidList[i];
+                   }
                }
            }
        }
@@ -9520,7 +9537,7 @@
            startCleaningPackages();
        } else {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
-           unloadMediaPackages(processCids, uidArr);
+           unloadMediaPackages(processCids, uidArr, reportStatus);
        }
    }
 
@@ -9545,10 +9562,7 @@
     * Look at potentially valid container ids from processCids
     * If package information doesn't match the one on record
     * or package scanning fails, the cid is added to list of
-    * removeCids and cleaned up. Since cleaning up containers
-    * involves destroying them, we do not want any parse
-    * references to such stale containers. So force gc's
-    * to avoid unnecessary crashes.
+    * removeCids. We currently don't delete stale containers.
     */
    private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
            int uidArr[], HashSet<String> removeCids) {
@@ -9626,6 +9640,7 @@
        if (pkgList.size() > 0) {
            sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
        }
+       // Force gc to avoid any stale parser references that we might have.
        if (doGc) {
            Runtime.getRuntime().gc();
        }
@@ -9637,12 +9652,33 @@
        }
    }
 
+   /*
+    * Utility method to unload a list of specified containers
+    */
+   private void unloadAllContainers(Set<SdInstallArgs> cidArgs) {
+       // Just unmount all valid containers.
+       for (SdInstallArgs arg : cidArgs) {
+           synchronized (mInstallLock) {
+               arg.doPostDeleteLI(false);
+           }
+       }
+   }
+
+   /*
+    * Unload packages mounted on external media. This involves deleting
+    * package data from internal structures, sending broadcasts about
+    * diabled packages, gc'ing to free up references, unmounting all
+    * secure containers corresponding to packages on external media, and
+    * posting a UPDATED_MEDIA_STATUS message if status has been requested.
+    * Please note that we always have to post this message if status has
+    * been requested no matter what.
+    */
    private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids,
-           int uidArr[]) {
+           int uidArr[], final boolean reportStatus) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
        ArrayList<String> pkgList = new ArrayList<String>();
        ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
-       Set<SdInstallArgs> keys = processCids.keySet();
+       final Set<SdInstallArgs> keys = processCids.keySet();
        for (SdInstallArgs args : keys) {
            String cid = args.cid;
            String pkgName = args.getPackageName();
@@ -9660,22 +9696,23 @@
                }
            }
        }
-       // Send broadcasts
+       // We have to absolutely send UPDATED_MEDIA_STATUS only
+       // after confirming that all the receivers processed the ordered
+       // broadcast when packages get disabled, force a gc to clean things up.
+       // and unload all the containers.
        if (pkgList.size() > 0) {
            sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
                public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
                        boolean ordered, boolean sticky) throws RemoteException {
-                   // Force gc now that everyone is done cleaning up, to release
-                   // references on assets.
-                   Runtime.getRuntime().gc();
+                   Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
+                           reportStatus ? 1 : 0, 1, keys);
+                   mHandler.sendMessage(msg);
                }
            });
-       }
-       // Just unmount all valid containers.
-       for (SdInstallArgs args : keys) {
-           synchronized (mInstallLock) {
-               args.doPostDeleteLI(false);
-           }
+       } else {
+           Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
+                   reportStatus ? 1 : 0, -1, keys);
+           mHandler.sendMessage(msg);
        }
    }
 
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index 9d4e226..71557f5 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -66,23 +66,17 @@
 
     private Context mContext;
 
-    private int mPolicyPollPeriodSec;
-    private static final int DEFAULT_POLLING_PERIOD_SEC = 60 * 10;
     private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
-
     private static final int TESTING_RESET_PERIOD_SEC = 60 * 3;
+    private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
 
     private static final int PERIOD_COUNT = 6;
 
+    private int mPolicyPollPeriodSec;
     private long mPolicyThreshold;
-    // TODO - remove testing stuff?
-    private static final long DEFAULT_TESTING_THRESHOLD = 1 * 1024 * 1024;
-    private static final long DEFAULT_THRESHOLD = 0; // off by default
-
     private int mPolicyThrottleValue;
-    private static final int DEFAULT_THROTTLE_VALUE = 100; // 100 Kbps
-
     private int mPolicyResetDay; // 1-28
+    private int mPolicyNotificationsAllowedMask;
 
     private long mLastRead; // read byte count from last poll
     private long mLastWrite; // write byte count from last poll
@@ -100,11 +94,10 @@
 
     private DataRecorder mRecorder;
 
-    private String mPolicyIface;
+    private String mIface;
 
     private static final int NOTIFICATION_WARNING   = 2;
     private static final int NOTIFICATION_ALL       = 0xFFFFFFFF;
-    private int mPolicyNotificationsAllowedMask;
 
     private Notification mThrottlingNotification;
     private boolean mWarningNotificationSent = false;
@@ -146,16 +139,15 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.THROTTLE_POLLING_SEC), false, this);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.THROTTLE_THRESHOLD), false, this);
+                    Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.THROTTLE_VALUE), false, this);
+                    Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.THROTTLE_RESET_DAY), false, this);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this);
             resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.THROTTLE_IFACE), false, this);
-                    // TODO - add help url
+                    Settings.Secure.THROTTLE_HELP_URI), false, this);
         }
 
         @Override
@@ -172,18 +164,26 @@
 
     public synchronized long getResetTime(String iface) {
         enforceAccessPermission();
-        if (iface.equals(mPolicyIface) && (mRecorder != null)) mRecorder.getPeriodEnd();
+        if ((iface != null) &&
+                iface.equals(mIface) &&
+                (mRecorder != null)) {
+            mRecorder.getPeriodEnd();
+        }
         return 0;
     }
     public synchronized long getPeriodStartTime(String iface) {
         enforceAccessPermission();
-        if (iface.equals(mPolicyIface) && (mRecorder != null)) mRecorder.getPeriodStart();
+        if ((iface != null) &&
+                iface.equals(mIface) &&
+                (mRecorder != null)) {
+            mRecorder.getPeriodStart();
+        }
         return 0;
     }
     //TODO - a better name?  getCliffByteCountThreshold?
     public synchronized long getCliffThreshold(String iface, int cliff) {
         enforceAccessPermission();
-        if ((cliff == 1) && iface.equals(mPolicyIface)) {
+        if ((iface != null) && (cliff == 1) && iface.equals(mIface)) {
             return mPolicyThreshold;
         }
         return 0;
@@ -191,7 +191,7 @@
     // TODO - a better name? getThrottleRate?
     public synchronized int getCliffLevel(String iface, int cliff) {
         enforceAccessPermission();
-        if ((cliff == 1) && iface.equals(mPolicyIface)) {
+        if ((iface != null) && (cliff == 1) && iface.equals(mIface)) {
             return mPolicyThrottleValue;
         }
         return 0;
@@ -205,7 +205,8 @@
 
     public synchronized long getByteCount(String iface, int dir, int period, int ago) {
         enforceAccessPermission();
-        if (iface.equals(mPolicyIface) &&
+        if ((iface != null) &&
+                iface.equals(mIface) &&
                 (period == ThrottleManager.PERIOD_CYCLE) &&
                 (mRecorder != null)) {
             if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
@@ -217,7 +218,7 @@
     // TODO - a better name - getCurrentThrottleRate?
     public synchronized int getThrottle(String iface) {
         enforceAccessPermission();
-        if (iface.equals(mPolicyIface) && (mThrottleIndex == 1)) {
+        if ((iface != null) && iface.equals(mIface) && (mThrottleIndex == 1)) {
             return mPolicyThrottleValue;
         }
         return 0;
@@ -302,20 +303,27 @@
         private void onPolicyChanged() {
             boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
 
-            int pollingPeriod = DEFAULT_POLLING_PERIOD_SEC;
-            if (testing) pollingPeriod = TESTING_POLLING_PERIOD_SEC;
+            int pollingPeriod = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_datause_polling_period_sec);
             mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
 
             // TODO - remove testing stuff?
-            long defaultThreshold = DEFAULT_THRESHOLD;
-            if (testing) defaultThreshold = DEFAULT_TESTING_THRESHOLD;
+            long defaultThreshold = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_datause_threshold_bytes);
+            int defaultValue = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_datause_throttle_kbitsps);
             synchronized (ThrottleService.this) {
                 mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(),
-                        Settings.Secure.THROTTLE_THRESHOLD, defaultThreshold);
+                        Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
                 mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(),
-                        Settings.Secure.THROTTLE_VALUE, DEFAULT_THROTTLE_VALUE);
+                        Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
+                if (testing) {
+                    mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
+                    mPolicyThreshold = TESTING_THRESHOLD;
+                }
             }
+
             mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.THROTTLE_RESET_DAY, -1);
             if (mPolicyResetDay == -1 ||
@@ -325,15 +333,18 @@
                 Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
             }
+            mIface = mContext.getResources().getString(
+                    com.android.internal.R.string.config_datause_iface);
             synchronized (ThrottleService.this) {
-                mPolicyIface = Settings.Secure.getString(mContext.getContentResolver(),
-                        Settings.Secure.THROTTLE_IFACE);
-                // TODO - read default from resource so it's device-specific
-                if (mPolicyIface == null) mPolicyIface = "rmnet0";
+                if (mIface == null) {
+                    mPolicyThreshold = 0;
+                }
             }
 
+            int defaultNotificationType = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_datause_notification_type);
             mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.THROTTLE_NOTIFICATION_TYPE, NOTIFICATION_ALL);
+                    Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
 
             Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + mPolicyPollPeriodSec +
                     ", threshold=" + mPolicyThreshold + ", value=" + mPolicyThrottleValue +
@@ -352,8 +363,8 @@
             long incRead = 0;
             long incWrite = 0;
             try {
-                incRead = mNMService.getInterfaceRxCounter(mPolicyIface) - mLastRead;
-                incWrite = mNMService.getInterfaceTxCounter(mPolicyIface) - mLastWrite;
+                incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
+                incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
             } catch (RemoteException e) {
                 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
             }
@@ -383,11 +394,12 @@
             broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, mRecorder.getPeriodEnd());
             mContext.sendStickyBroadcast(broadcast);
 
+            mAlarmManager.cancel(mPendingPollIntent);
             mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent);
         }
 
         private void checkThrottleAndPostNotification(long currentTotal) {
-            // are we even doing this?
+            // is throttling enabled?
             if (mPolicyThreshold == 0)
                 return;
 
@@ -399,7 +411,7 @@
                     }
                     if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!");
                     try {
-                        mNMService.setInterfaceThrottle(mPolicyIface,
+                        mNMService.setInterfaceThrottle(mIface,
                                 mPolicyThrottleValue, mPolicyThrottleValue);
                     } catch (Exception e) {
                         Slog.e(TAG, "error setting Throttle: " + e);
@@ -463,7 +475,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);
@@ -492,7 +504,7 @@
                     mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED;
                 }
                 try {
-                    mNMService.setInterfaceThrottle(mPolicyIface, -1, -1);
+                    mNMService.setInterfaceThrottle(mIface, -1, -1);
                 } catch (Exception e) {
                     Slog.e(TAG, "error clearing Throttle: " + e);
                 }