Merge "Fix issue #5367164: memory leak in LayoutTransition"
diff --git a/api/14.txt b/api/14.txt
index e26311f..92969f6 100644
--- a/api/14.txt
+++ b/api/14.txt
@@ -11541,7 +11541,6 @@
 
   public class ConnectivityManager {
     method public android.net.NetworkInfo getActiveNetworkInfo();
-    method public android.net.NetworkQuotaInfo getActiveNetworkQuotaInfo();
     method public android.net.NetworkInfo[] getAllNetworkInfo();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.NetworkInfo getNetworkInfo(int);
@@ -11701,16 +11700,6 @@
     enum_constant public static final android.net.NetworkInfo.State UNKNOWN;
   }
 
-  public class NetworkQuotaInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getEstimatedBytes();
-    method public long getHardLimitBytes();
-    method public long getSoftLimitBytes();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final long NO_LIMIT = -1L; // 0xffffffffffffffffL
-  }
-
   public class ParseException extends java.lang.RuntimeException {
     field public java.lang.String response;
   }
diff --git a/api/current.txt b/api/current.txt
index e26311f..92969f6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11541,7 +11541,6 @@
 
   public class ConnectivityManager {
     method public android.net.NetworkInfo getActiveNetworkInfo();
-    method public android.net.NetworkQuotaInfo getActiveNetworkQuotaInfo();
     method public android.net.NetworkInfo[] getAllNetworkInfo();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.NetworkInfo getNetworkInfo(int);
@@ -11701,16 +11700,6 @@
     enum_constant public static final android.net.NetworkInfo.State UNKNOWN;
   }
 
-  public class NetworkQuotaInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getEstimatedBytes();
-    method public long getHardLimitBytes();
-    method public long getSoftLimitBytes();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final long NO_LIMIT = -1L; // 0xffffffffffffffffL
-  }
-
   public class ParseException extends java.lang.RuntimeException {
     field public java.lang.String response;
   }
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 93a6ad3..d23873d 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -66,6 +66,7 @@
  */
 public class SQLiteDatabase extends SQLiteClosable {
     private static final String TAG = "SQLiteDatabase";
+    private static final boolean ENABLE_DB_SAMPLE = false; // true to enable stats in event log
     private static final int EVENT_DB_OPERATION = 52000;
     private static final int EVENT_DB_CORRUPT = 75004;
 
@@ -440,7 +441,9 @@
             }
         }
         if (sql != null) {
-            logTimeStat(sql, timeStart, GET_LOCK_LOG_PREFIX);
+            if (ENABLE_DB_SAMPLE)  {
+                logTimeStat(sql, timeStart, GET_LOCK_LOG_PREFIX);
+            }
         }
     }
     private static class DatabaseReentrantLock extends ReentrantLock {
@@ -726,7 +729,9 @@
                     }
                 }
                 // log the transaction time to the Eventlog.
-                logTimeStat(getLastSqlStatement(), mTransStartTime, COMMIT_SQL);
+                if (ENABLE_DB_SAMPLE) {
+                    logTimeStat(getLastSqlStatement(), mTransStartTime, COMMIT_SQL);
+                }
             } else {
                 try {
                     execSQL("ROLLBACK;");
@@ -2036,7 +2041,9 @@
     }
 
     /* package */ void logTimeStat(String sql, long beginMillis) {
-        logTimeStat(sql, beginMillis, null);
+        if (ENABLE_DB_SAMPLE) {
+            logTimeStat(sql, beginMillis, null);
+        }
     }
 
     private void logTimeStat(String sql, long beginMillis, String prefix) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 530122c..e2d5af0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -548,6 +548,8 @@
      * Return quota status for the current active network, or {@code null} if no
      * network is active. Quota status can change rapidly, so these values
      * shouldn't be cached.
+     *
+     * @hide
      */
     public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
         try {
diff --git a/core/java/android/net/NetworkQuotaInfo.java b/core/java/android/net/NetworkQuotaInfo.java
index b85f925..6535256 100644
--- a/core/java/android/net/NetworkQuotaInfo.java
+++ b/core/java/android/net/NetworkQuotaInfo.java
@@ -21,6 +21,8 @@
 
 /**
  * Information about quota status on a specific network.
+ *
+ * @hide
  */
 public class NetworkQuotaInfo implements Parcelable {
     private final long mEstimatedBytes;
diff --git a/core/java/android/net/http/SslError.java b/core/java/android/net/http/SslError.java
index 5998f5f..863304c 100644
--- a/core/java/android/net/http/SslError.java
+++ b/core/java/android/net/http/SslError.java
@@ -198,7 +198,8 @@
 
     /**
      * Gets the most severe SSL error in this object's set of errors.
-     * @return The most severe SSL error.
+     * Returns -1 if the set is empty.
+     * @return The most severe SSL error, or -1 if the set is empty.
      */
     public int getPrimaryError() {
         if (mErrors != 0) {
@@ -208,9 +209,11 @@
                     return error;
                 }
             }
+            // mErrors should never be set to an invalid value.
+            assert false;
         }
 
-        return 0;
+        return -1;
     }
 
     /**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 237a892..79995d0 100755
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -666,6 +666,7 @@
             public static SmsMessage[] getMessagesFromIntent(
                     Intent intent) {
                 Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
+                String format = intent.getStringExtra("format");
                 byte[][] pduObjs = new byte[messages.length][];
 
                 for (int i = 0; i < messages.length; i++) {
@@ -676,7 +677,7 @@
                 SmsMessage[] msgs = new SmsMessage[pduCount];
                 for (int i = 0; i < pduCount; i++) {
                     pdus[i] = pduObjs[i];
-                    msgs[i] = SmsMessage.createFromPdu(pdus[i]);
+                    msgs[i] = SmsMessage.createFromPdu(pdus[i], format);
                 }
                 return msgs;
             }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 23d1b0f..b86d21d 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -184,6 +184,22 @@
     abstract void setup(int width, int height);
 
     /**
+     * Gets the current width of the surface. This is the width that the surface
+     * was last set to in a call to {@link #setup(int, int)}.
+     *
+     * @return the current width of the surface
+     */
+    abstract int getWidth();
+
+    /**
+     * Gets the current height of the surface. This is the height that the surface
+     * was last set to in a call to {@link #setup(int, int)}.
+     *
+     * @return the current width of the surface
+     */
+    abstract int getHeight();
+
+    /**
      * Interface used to receive callbacks whenever a view is drawn by
      * a hardware renderer instance.
      */
@@ -362,6 +378,7 @@
         static EGLDisplay sEglDisplay;
         static EGLConfig sEglConfig;
         static final Object[] sEglLock = new Object[0];
+        int mWidth = -1, mHeight = -1;
 
         static final ThreadLocal<EGLContext> sEglContextStorage = new ThreadLocal<EGLContext>();
 
@@ -714,9 +731,21 @@
         void setup(int width, int height) {
             if (validate()) {
                 mCanvas.setViewport(width, height);
+                mWidth = width;
+                mHeight = height;
             }
         }
 
+        @Override
+        int getWidth() {
+            return mWidth;
+        }
+
+        @Override
+        int getHeight() {
+            return mHeight;
+        }
+
         boolean canDraw() {
             return mGl != null && mCanvas != null;
         }        
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e7c91f9..615a5f6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -860,7 +860,6 @@
         CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
         if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
             params = lp;
-            windowAttributesChanges |= WindowManager.LayoutParams.BUFFER_CHANGED;
             fullRedrawNeeded = true;
             mLayoutRequested = true;
             if (mLastInCompatMode) {
@@ -1078,7 +1077,6 @@
                             ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
                             resizeMode;
                     params = lp;
-                    windowAttributesChanges |= WindowManager.LayoutParams.BUFFER_CHANGED;
                 }
             }
         }
@@ -1375,13 +1373,15 @@
                 }
             }
 
-            if (hwInitialized || ((windowShouldResize || (params != null &&
-                    (windowAttributesChanges & WindowManager.LayoutParams.BUFFER_CHANGED) != 0)) &&
-                    mAttachInfo.mHardwareRenderer != null &&
-                    mAttachInfo.mHardwareRenderer.isEnabled())) {
-                mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight);
-                if (!hwInitialized && mAttachInfo.mHardwareRenderer.isEnabled()) {
-                    mAttachInfo.mHardwareRenderer.invalidate(mHolder);
+            if (mAttachInfo.mHardwareRenderer != null &&
+                    mAttachInfo.mHardwareRenderer.isEnabled()) {
+                if (hwInitialized || windowShouldResize ||
+                        mWidth != mAttachInfo.mHardwareRenderer.getWidth() ||
+                        mHeight != mAttachInfo.mHardwareRenderer.getHeight()) {
+                    mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight);
+                    if (!hwInitialized) {
+                        mAttachInfo.mHardwareRenderer.invalidate(mHolder);
+                    }
                 }
             }
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 17a516c..99acb3f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1260,8 +1260,6 @@
         /** {@hide} */
         public static final int PRIVATE_FLAGS_CHANGED = 1<<16;
         /** {@hide} */
-        public static final int BUFFER_CHANGED = 1<<17;
-        /** {@hide} */
         public static final int EVERYTHING_CHANGED = 0xffffffff;
 
         // internal buffer to backup/restore parameters under compatibility mode.
@@ -1272,11 +1270,11 @@
     
             if (width != o.width) {
                 width = o.width;
-                changes |= LAYOUT_CHANGED | BUFFER_CHANGED;
+                changes |= LAYOUT_CHANGED;
             }
             if (height != o.height) {
                 height = o.height;
-                changes |= LAYOUT_CHANGED | BUFFER_CHANGED;
+                changes |= LAYOUT_CHANGED;
             }
             if (x != o.x) {
                 x = o.x;
@@ -1288,19 +1286,19 @@
             }
             if (horizontalWeight != o.horizontalWeight) {
                 horizontalWeight = o.horizontalWeight;
-                changes |= LAYOUT_CHANGED | BUFFER_CHANGED;
+                changes |= LAYOUT_CHANGED;
             }
             if (verticalWeight != o.verticalWeight) {
                 verticalWeight = o.verticalWeight;
-                changes |= LAYOUT_CHANGED | BUFFER_CHANGED;
+                changes |= LAYOUT_CHANGED;
             }
             if (horizontalMargin != o.horizontalMargin) {
                 horizontalMargin = o.horizontalMargin;
-                changes |= LAYOUT_CHANGED | BUFFER_CHANGED;
+                changes |= LAYOUT_CHANGED;
             }
             if (verticalMargin != o.verticalMargin) {
                 verticalMargin = o.verticalMargin;
-                changes |= LAYOUT_CHANGED | BUFFER_CHANGED;
+                changes |= LAYOUT_CHANGED;
             }
             if (type != o.type) {
                 type = o.type;
@@ -1308,7 +1306,7 @@
             }
             if (flags != o.flags) {
                 flags = o.flags;
-                changes |= FLAGS_CHANGED | BUFFER_CHANGED;
+                changes |= FLAGS_CHANGED;
             }
             if (privateFlags != o.privateFlags) {
                 privateFlags = o.privateFlags;
@@ -1320,11 +1318,11 @@
             }
             if (gravity != o.gravity) {
                 gravity = o.gravity;
-                changes |= LAYOUT_CHANGED | BUFFER_CHANGED;
+                changes |= LAYOUT_CHANGED;
             }
             if (format != o.format) {
                 format = o.format;
-                changes |= FORMAT_CHANGED | BUFFER_CHANGED;
+                changes |= FORMAT_CHANGED;
             }
             if (windowAnimations != o.windowAnimations) {
                 windowAnimations = o.windowAnimations;
@@ -1363,7 +1361,7 @@
     
             if (screenOrientation != o.screenOrientation) {
                 screenOrientation = o.screenOrientation;
-                changes |= SCREEN_ORIENTATION_CHANGED | BUFFER_CHANGED;
+                changes |= SCREEN_ORIENTATION_CHANGED;
             }
 
             if (systemUiVisibility != o.systemUiVisibility
diff --git a/core/java/android/webkit/SslErrorHandlerImpl.java b/core/java/android/webkit/SslErrorHandlerImpl.java
index 82cd3e8..b2e4b13 100644
--- a/core/java/android/webkit/SslErrorHandlerImpl.java
+++ b/core/java/android/webkit/SslErrorHandlerImpl.java
@@ -159,7 +159,7 @@
 
         if (DebugFlags.SSL_ERROR_HANDLER) {
             assert host != null;
-            assert primary != 0;
+            assert primary != -1;
         }
 
         if (mSslPrefTable.containsKey(host) && primary <= mSslPrefTable.getInt(host)) {
@@ -260,7 +260,7 @@
 
                 if (DebugFlags.SSL_ERROR_HANDLER) {
                     assert host != null;
-                    assert primary != 0;
+                    assert primary != -1;
                 }
                 boolean hasKey = mSslPrefTable.containsKey(host);
                 if (!hasKey || primary > mSslPrefTable.getInt(host)) {
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 33f84a5..66371f5 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -832,7 +832,7 @@
             TextView tv =
                     (TextView) super.getView(position, convertView, parent);
             if (tv != null && mTextView != null) {
-                tv.setTextSize(mTextView.getTextSize());
+                tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextView.getTextSize());
             }
             return tv;
         }
@@ -896,7 +896,10 @@
      *          WebTextView represents.
      */
     /* package */ void setNodePointer(int ptr) {
-        mNodePointer = ptr;
+        if (ptr != mNodePointer) {
+            mNodePointer = ptr;
+            setAdapterCustom(null);
+        }
     }
 
     /**
@@ -1051,11 +1054,12 @@
         }
         setHint(null);
         setThreshold(1);
+        boolean autoComplete = false;
         if (single) {
             mWebView.requestLabel(mWebView.nativeFocusCandidateFramePointer(),
                     mNodePointer);
             maxLength = mWebView.nativeFocusCandidateMaxLength();
-            boolean autoComplete = mWebView.nativeFocusCandidateIsAutoComplete();
+            autoComplete = mWebView.nativeFocusCandidateIsAutoComplete();
             if (type != PASSWORD && (mAutoFillable || autoComplete)) {
                 String name = mWebView.nativeFocusCandidateName();
                 if (name != null && name.length() > 0) {
@@ -1070,8 +1074,9 @@
         setInputType(inputType);
         setImeOptions(imeOptions);
         setVisibility(VISIBLE);
-        AutoCompleteAdapter adapter = null;
-        setAdapterCustom(adapter);
+        if (!autoComplete) {
+            setAdapterCustom(null);
+        }
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 89f21d7..5111969 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3246,8 +3246,7 @@
     public void clearFormData() {
         checkThread();
         if (inEditingMode()) {
-            AutoCompleteAdapter adapter = null;
-            mWebTextView.setAdapterCustom(adapter);
+            mWebTextView.setAdapterCustom(null);
         }
     }
 
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 440ee79..ea29ad1 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1662,24 +1662,15 @@
                 mDrawIsScheduled = false;
             }
             if (mMessages != null) {
-                Log.w(LOGTAG, "Not supported in this case.");
+                Throwable throwable = new Throwable(
+                        "EventHub.removeMessages(int what = " + what + ") is not supported " +
+                        "before the WebViewCore is set up.");
+                Log.w(LOGTAG, Log.getStackTraceString(throwable));
             } else {
                 mHandler.removeMessages(what);
             }
         }
 
-        private synchronized boolean hasMessages(int what) {
-            if (mBlockMessages) {
-                return false;
-            }
-            if (mMessages != null) {
-                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
-                return false;
-            } else {
-                return mHandler.hasMessages(what);
-            }
-        }
-
         private synchronized void sendMessageDelayed(Message msg, long delay) {
             if (mBlockMessages) {
                 return;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ae39760..bde2c72 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -182,6 +182,28 @@
     <!-- Regex of wired ethernet ifaces -->
     <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
 
+    <!-- If the mobile hotspot feature requires provisioning, an intent string can be provided
+        to the launch a supported application that provisions the devices.
+
+        Example Usage:
+
+        Intent intent = new Intent(R.string.config_mobile_hotspot_provision_intent);
+        startActivityForResult(intent, 0);
+
+        public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+            super.onActivityResult(requestCode, resultCode, intent);
+            if (requestCode == 0) {
+                if (resultCode == Activity.RESULT_OK) {
+                    //Mobile hotspot provisioning successful
+                } else {
+                    //Mobile hotspot provisioning failed
+                }
+            }
+
+        See src/com/android/settings/TetherSettings.java for more details.
+        -->
+    <string translatable="false" name="config_mobile_hotspot_provision_intent"></string>
+
     <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
     <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
     <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
diff --git a/docs/html/guide/developing/building/building-cmdline.jd b/docs/html/guide/developing/building/building-cmdline.jd
index ec918dc..d78a4f5 100644
--- a/docs/html/guide/developing/building/building-cmdline.jd
+++ b/docs/html/guide/developing/building/building-cmdline.jd
@@ -18,6 +18,7 @@
         <li><a href="#RunningOnEmulator">Running on the Emulator</a></li>
         <li><a href="#RunningOnDevice">Running on a Device</a></li>
         <li><a href="#Signing">Application Signing</a></li>
+        <li><a href="#AntReference">Ant Command Reference</a></li>
       </ol>
   <h2>See also</h2>
   <ol>
@@ -58,11 +59,11 @@
 
   <p class="note"><strong>Note:</strong> When installing JDK on Windows, the default is to install
   in the "Program Files" directory. This location will cause <code>ant</code> to fail, because of
-  the space. To fix the problem, you can specify the JAVA_HOME variable like this: 
+  the space. To fix the problem, you can specify the JAVA_HOME variable like this:
   <pre>set JAVA_HOME=c:\Progra~1\Java\&lt;jdkdir&gt;</pre>
-  
+
   <p>The easiest solution, however, is to install JDK in a non-space directory, for example:</p>
-  
+
   <pre>c:\java\jdk1.6.0_02</pre>
 
   <h2 id="DebugMode">Building in Debug Mode</h2>
@@ -141,7 +142,7 @@
 
   <p>If you would like, you can configure the Android build script to automatically sign and align
   your application package. To do so, you must provide the path to your keystore and the name of
-  your key alias in your project's {@code build.properties} file. With this information provided,
+  your key alias in your project's {@code ant.properties} file. With this information provided,
   the build script will prompt you for your keystore and alias password when you build in release
   mode and produce your final application package, which will be ready for distribution.</p>
 
@@ -152,7 +153,7 @@
   procedure manually, <a href="#ManualReleaseMode">build unsigned</a> and then continue with
   <a href="{@docRoot}guide/publishing/app-signing.html">Signing Your Applications</a>.</p>
 
-  <p>To specify your keystore and alias, open the project {@code build.properties} file (found in
+  <p>To specify your keystore and alias, open the project {@code ant.properties} file (found in
   the root of the project directory) and add entries for {@code key.store} and {@code key.alias}.
   For example:</p>
   <pre>
@@ -180,16 +181,16 @@
 
   <p>This creates your Android application .apk file inside the project <code>bin/</code>
   directory, named <code><em>&lt;your_project_name&gt;</em>-release.apk</code>. This .apk file has
-  been signed with the private key specified in {@code build.properties} and aligned with {@code
+  been signed with the private key specified in {@code ant.properties} and aligned with {@code
   zipalign}. It's ready for installation and distribution.</p>
 
   <h3 id="OnceBuilt">Once built and signed in release mode</h3>
 
   <p>Once you have signed your application with a private key, you can install and run it on an
-  <a href="#RunningOnEmulator">emulator</a> or <a href="#RunningOnDevice">device</a>. You can 
-  also try installing it onto a device from a web server. Simply upload the signed .apk to a web 
-  site, then load the .apk URL in your Android web browser to download the application and begin 
-  installation. (On your device, be sure you have enabled 
+  <a href="#RunningOnEmulator">emulator</a> or <a href="#RunningOnDevice">device</a>. You can
+  also try installing it onto a device from a web server. Simply upload the signed .apk to a web
+  site, then load the .apk URL in your Android web browser to download the application and begin
+  installation. (On your device, be sure you have enabled
   <em>Settings &gt; Applications &gt; Unknown sources</em>.)</p>
 
   <h2 id="RunningOnEmulator">Running on the Emulator</h2>
@@ -260,10 +261,6 @@
   device:</p>
 
   <ul>
-    <li>Ensure that your application is debuggable by setting the
-    <code>android:debuggable</code> attribute of the <code>&lt;application&gt;</code>
-    element to <code>true</code>. As of ADT 8.0, this is done by default when you build in debug mode.</li>
-
     <li>Enable USB Debugging on your device. You can find the setting on most Android devices by
     going to <strong>Settings > Applications > Development > USB debugging</strong>.</li>
 
@@ -276,7 +273,7 @@
   <p>Once your device is set up and connected via USB, navigate to your SDK's <code>platform-tools/</code>
   directory and install the <code>.apk</code> on the device:</p>
   <pre>
-adb -d install <em>path/to/your/app</em>.apk 
+adb -d install <em>path/to/your/app</em>.apk
 </pre>
 
   <p>The {@code -d} flag specifies that you want to use the attached device (in case you also have
@@ -315,4 +312,60 @@
   <p>Please read <a href="{@docRoot}guide/publishing/app-signing.html">Signing Your
   Applications</a>, which provides a thorough guide to application signing on Android and what it
   means to you as an Android application developer. The document also includes a guide to exporting
-  and signing your application with the ADT's Export Wizard.</p>
\ No newline at end of file
+  and signing your application with the ADT's Export Wizard.</p>
+
+  <h2 id="AntReference">Ant Command Reference</h2>
+  <dt><code>ant clean</code></dt>
+  <dd>Cleans the project. If you include the <code>all</code> target before <code>clean</code>
+(<code>ant all clean</code>), other projects are also cleaned. For instance if you clean a
+test project, the tested project is also cleaned.</dd>
+
+  <dt><code>ant debug</code></dt>
+  <dd>Builds a debug package. Works on application, library, and test projects and compiles
+  dependencies as  needed.</dd>
+
+  <dt id="emma"><code>ant emma debug</code></dt>
+  <dd>Builds a test project while building the tested project with instrumentation turned on.
+  This is used to run tests with code coverage enabled.</dd>
+
+  <dt><code>ant release</code></dt>
+  <dd>Builds a release package.</dd>
+
+  <dt><code>ant instrument</code>
+  </dt>
+  <dd>Builds an instrumented debug package. This is generally called automatically when building a
+  test project with code coverage enabled (with the <code>emma</code>
+  target)</dd>
+
+  <dt><code>ant &lt;build_target&gt; install</code></dt>
+  <dd>Builds and installs a package. Using <code>install</code> by itself fails.</dd>
+
+  <dt><code>ant installd</code></dt>
+  <dd>Installs an already compiled debug package. This fails if the <code>.apk</code> is not
+  already built.</dd>
+
+  <dt><code>ant installr</code></dt>
+  <dd>Installs an already compiled release package. This fails if the <code>.apk</code> is not
+  already built.</dd>
+
+  <dt><code>ant installt</code></dt>
+  <dd>Installs an already compiled test package. Also installs the <code>.apk</code> of the
+  tested application. This fails if the <code>.apk</code> is not already built.</dd>
+
+  <dt><code>ant installi</code></dt>
+  <dd>Installs an already compiled instrumented package. This is generally not used manually as
+  it's called when installing a test package. This fails if the <code>.apk</code> is not already
+  built.</dd>
+
+   <dt><code>ant test</code></dt>
+   <dd>Runs the tests (for test projects). The tested and test <code>.apk</code> files must be
+   previously installed.</dd>
+
+  <dt><code>ant debug installt test</code></dt>
+  <dd>Builds a test project and the tested project, installs both <code>.apk</code> files, and
+  runs the tests.</dd>
+
+  <dt><code>ant emma debug installt test</code></dt>
+  <dd>Builds a test project and the tested project, installs both <code>.apk</code> files, and
+  runs the tests with code coverage enabled.</dd>
+
diff --git a/docs/html/guide/developing/projects/index.jd b/docs/html/guide/developing/projects/index.jd
index 273a405..ac8a1a5 100644
--- a/docs/html/guide/developing/projects/index.jd
+++ b/docs/html/guide/developing/projects/index.jd
@@ -15,7 +15,7 @@
         </li>
 
         <li><a href="#TestProjects">Test Projects</a></li>
-        
+
         <li><a href="#testing">Testing a Library Project</a></li>
       </ol>
     </div>
@@ -43,7 +43,7 @@
     <dt><strong>Library Projects</strong></dt>
 
     <dd>These projects contain shareable Android source code and resources that you can reference
-    in Android projects. This is useful when you have common code that you want to reuse. 
+    in Android projects. This is useful when you have common code that you want to reuse.
     Library projects cannot be installed onto a device, however, they are
     pulled into the <code>.apk</code> file at build time.</dd>
   </dl>
@@ -125,7 +125,7 @@
 
         <dt><code>menu/</code></dt>
 
-        <dd>For XML files that define application menus. 
+        <dd>For XML files that define application menus.
         See the <a href="{@docRoot}guide/topics/resources/menu-resource.html">Menus</a>
         resource type.</dd>
 
@@ -168,23 +168,33 @@
     <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>
     documentation for more information</dd>
 
-    <dt><code>build.properties</code></dt>
+    <dt><code>project.properties</code></dt>
+
+    <dd>This file contains project settings, such as the build target. This file is integral to
+    the project, so maintain it in a source revision control system. To edit project
+    properties in Eclipse, right-click the project folder and select
+    <strong>Properties</strong>.</dd>
+
+    <dt><code>local.properties</code></dt>
+
+    <dd>Customizable computer-specific properties for the build system. If you use Ant to build
+    the project, this contains the path to the SDK installation. Because the content of the file
+    is specific to the local installation of the SDK, maintained it in a source
+    revision control system. If you use Eclipse, this file is not used.</dd>
+
+    <dt><code>ant.properties</code></dt>
 
     <dd>Customizable properties for the build system. You can edit this file to override default
-    build settings used by Ant and provide a pointer to your keystore and key alias so that the
-    build tools can sign your application when built in release mode. If you use Eclipse, this file
-    is not used.</dd>
+    build settings used by Ant and also provide the location of your keystore and key alias so that
+    the build tools can sign your application when building in release mode. This file is integral
+    to the project, so maintain it in a source revision control system. If you use Eclipse, this
+    file is not used.</dd>
 
     <dt><code>build.xml</code></dt>
 
     <dd>The Ant build file for your project. This is only applicable for projects that
-    you create on the command line.</dd>
+    you build with Ant.</dd>
 
-    <dt><code>default.properties</code></dt>
-
-    <dd>This file contains project settings, such as the build target. This files is integral to
-    the project, as such, it should be maintained in a Source Revision Control system. Do not edit
-    the file manually.</dd>
   </dl>
 
   <h2 id="LibraryProjects">Library Projects</h2>
@@ -199,7 +209,7 @@
 
       <p>To download the sample applications and run them as projects in
       your environment, use the <em>Android SDK and AVD Manager</em> to download the "Samples for
-      SDK API 8" component into your SDK.</p>
+      SDK API 8" (or later) component into your SDK.</p>
 
       <p>For more information and to browse the code of the samples, see
       the <a href="{@docRoot}resources/samples/TicTacToeMain/index.html">TicTacToeMain
@@ -212,9 +222,10 @@
     and, at build time, include its compiled sources in their <code>.apk</code> files. Multiple
     application projects can reference the same library project and any single application project
     can reference multiple library projects.</p>
-        
-    <p class="note"><strong>Note:</strong> You need SDK Tools r8 or newer to fully support library projects
-    for all Android platform versions. You can download the tools and platforms using the
+
+    <p class="note"><strong>Note:</strong> You need SDK Tools r14 or newer to use the new library
+    project feature that generates each library project into its own JAR file.
+    You can download the tools and platforms using the
     <em>Android SDK and AVD Manager</em>, as described in
     <a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.</p>
 
@@ -246,12 +257,12 @@
     library in the dependent application and building that application.</p>
 
     <p>When you build an application that depends on a library project, the SDK tools compile the
-    library and merge its sources with those in the main project, then use the result to generate
-    the <code>.apk</code>. In cases where a resource ID is defined in both the application and the
-    library, the tools ensure that the resource declared in the application gets priority and that
-    the resource in the library project is not compiled into the application <code>.apk</code>.
-    This gives your application the flexibility to either use or redefine any resource behaviors or
-    values that are defined in any library.</p>
+    library into a temporary JAR file and uses it in the main project, then uses the
+    result to generate the <code>.apk</code>. In cases where a resource ID is defined in both the
+    application and the library, the tools ensure that the resource declared in the application gets
+    priority and that the resource in the library project is not compiled into the application
+    <code>.apk</code>. This gives your application the flexibility to either use or redefine any
+    resource behaviors or values that are defined in any library.</p>
 
     <p>To organize your code further, your application can add references to multiple library
     projects, then specify the relative priority of the resources in each library. This lets you
@@ -259,15 +270,13 @@
     libraries referenced from an application define the same resource ID, the tools select the
     resource from the library with higher priority and discard the other.</p>
 
-    <p>Once you have added references to library projects to your Android project, 
+    <p>Once you have added references to library projects to your Android project,
     you can set their relative priority. At build time, the
     libraries are merged with the application one at a time, starting from the lowest priority to
     the highest.</p>
 
-    <p>Note that a library project cannot itself reference another library project and that, at
-    build time, library projects are <em>not</em> merged with each other before being merged with
-    the application. However, note that a library can import an external library (JAR) in the
-    normal way.</p>
+    <p>Library projects can reference other library projects and can import an external library
+    (JAR) in the  normal way.</p>
 
   <h3 id="considerations">Development considerations</h3>
 
@@ -283,22 +292,23 @@
   defined in more than one project and will be merged, with the resource from the application or
   highest-priority library taking precedence.</p>
   </li>
-  
+
   <li><p><strong>Use prefixes to avoid resource conflicts</strong></p>
 
   <p>To avoid resource conflicts for common resource IDs, consider using a prefix or other
   consistent naming scheme that is unique to the project (or is unique across all projects).</p></li>
-  
+
   <li><p><strong>You cannot export a library project to a JAR file</strong></p>
 
-  <p>A library cannot be distributed as a binary file (such as a jar file). This is because the
-  library project is compiled by the main project to use the correct resource IDs.</p></li>
+  <p>A library cannot be distributed as a binary file (such as a JAR file). This will
+be added in a future
+  version of the SDK Tools.</p></li>
 
   <li><p><strong>A library project can include a JAR library</strong></p>
 
   <p>You can develop a library project that itself includes a JAR library, however you need to
   manually edit the dependent application project's build path and add a path to the JAR file.</p></li>
-  
+
   <li><p><strong>A library project can depend on an external JAR library</strong></p>
 
   <p>You can develop a library project that depends on an external library (for example, the Maps
@@ -316,7 +326,7 @@
   used by an application must be stored in the <code>assets/</code> directory of the application
   project itself. However, resource files saved in the
   <code>res/</code> directory are supported.</p></li>
-  
+
   <li><p><strong>Platform version must be lower than or equal to the Android project</strong></p>
 
   <p>A library is compiled as part of the dependent application project, so the API used in the
@@ -327,12 +337,12 @@
   higher than that of the application, the application project will not compile. It is
   perfectly acceptable to have a library that uses the Android 1.5 API (API level 3) and that is
   used in an Android 1.6 (API level 4) or Android 2.1 (API level 7) project, for instance.</p></li>
-  
+
   <li> <p><strong>No restriction on library package names</strong></p>
 
   <p>There is no requirement for the package name of a library to be the same as that of
   applications that use it.</p></li>
-  
+
   <li><p><strong>Each library project creates its own R class </strong></p>
 
   <p>When you build the dependent application project, library projects are compiled and
@@ -340,7 +350,7 @@
   to the library's package name. The <code>R</code> class generated from main
   project and the library project is created in all the packages that are needed including the main
   project's package and the libraries' packages.</p></li>
-  
+
   <li><p><strong>Library project storage location</strong></p>
 
   <p>There are no specific requirements on where you should store a library project, relative to a
@@ -351,7 +361,7 @@
 
   <h2 id="TestProjects">Test Projects</h2>
 
-  <p>Test projects contain Android applications that you write using the 
+  <p>Test projects contain Android applications that you write using the
   <a href="{@docRoot}guide/topics/testing/index.html">Testing and
   Instrumentation framework</a>. The framework is an extension of the JUnit test framework and adds
   access to Android system objects. The file structure of a test project is the same as an
@@ -387,24 +397,36 @@
     <code>&lt;instrumentation&gt;</code></a>
     element that connects the test project with the application project.</dd>
 
-    <dt><code>build.properties</code></dt>
+    <dt><code>project.properties</code></dt>
+
+    <dd>This file contains project settings, such as the build target and links to the project being
+tested. This file is integral to the project, so maintain it in a source
+revision control system. To edit project properties in Eclipse, right-click the project folder
+and select <strong>Properties</strong>.</dd>
+
+    <dt><code>local.properties</code></dt>
+
+    <dd>Customizable computer-specific properties for the build system. If you use Ant to build
+    the project, this contains the path to the SDK installation. Because the content of the file
+    is specific to the local installation of the SDK, it should not be maintained in a Source
+    Revision Control system. If you use Eclipse, this file is not used.</dd>
+
+    <dt><code>ant.properties</code></dt>
 
     <dd>Customizable properties for the build system. You can edit this file to override default
-    build settings used by Ant and provide a pointer to your keystore and key alias so that the
-    build tools can sign your application when built in release mode.</dd>
+    build settings used by Ant and provide the location to your keystore and key alias, so that the
+    build tools can sign your application when building in release mode. This file is integral to
+    the project, so maintain it in a source revision control system.
+    If you use Eclipse, this file is not used.</dd>
 
     <dt><code>build.xml</code></dt>
 
-    <dd>The Ant build file for your project.</dd>
+    <dd>The Ant build file for your project. This is only applicable for projects that
+    you build with Ant.</dd>
+  </dl>
 
-    <dt><code>default.properties</code></dt>
-
-    <dd>This file contains project settings, such as the build target. This files is integral to
-    the project, as such, it should be maintained in a Source Revision Control system. It should
-    never be edited manually &mdash; to edit project properties, right-click the project folder and
-    select "Properties".</dd>
-  </dl>For more information, see the <a href=
-  "{@docRoot}guide/developing/testing/index.html">Testing</a> section.
+  <p>For more information, see the <a href=
+  "{@docRoot}guide/developing/testing/index.html">Testing</a> section.</p>
 
 
   <h2 id="testing">Testing a Library Project</h2>
diff --git a/docs/html/guide/developing/projects/projects-cmdline.jd b/docs/html/guide/developing/projects/projects-cmdline.jd
index 90f88fb..08e0d9a 100644
--- a/docs/html/guide/developing/projects/projects-cmdline.jd
+++ b/docs/html/guide/developing/projects/projects-cmdline.jd
@@ -37,8 +37,8 @@
 
   <p>The <code>android</code> tool provides you with commands to create all three types of
   projects. An Android project contains all of the files and resources that are needed to build a
-  project into an .apk file for installation. 
-  
+  project into an .apk file for installation.
+
   <ul>
     <li>An Android project contains all of the files and resources that are needed to build a project into
   an .apk file for installation. You need to create an Android project for any application that you
@@ -107,13 +107,14 @@
   "{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a> (adb) &mdash; located in the
   SDK <code>platform-tools/</code> directory &mdash; to send your application to the emulator (discussed
   later). So you need access between your project solution and the <code>platform-tools/</code> folder.</p>
- 
+
   <p class="note"><strong>Tip:</strong> Add the <code>platform-tools/</code> as well as the <code>tools/</code> directory
   to your <code>PATH</code> environment variable.</p>
-  
+
   <p class="caution"><strong>Caution:</strong> You should refrain from moving the location of the
-  SDK directory, because this will break the build scripts. (They will need to be manually updated
-  to reflect the new SDK location before they will work again.)</p>
+  SDK directory, because this will break the SDK location property located in <code>local.properties</code>.
+  If you need to update the SDK location, use the <code>android update project</code> command.
+  See <a href="UpdatingAProject">Updating a Project</a> for more information.</p>
 
   <h2 id="UpdatingAProject">Updating a Project</h2>
 
@@ -166,7 +167,7 @@
 
   <p>The <code>create lib-project</code> command creates a standard project structure that includes
   preset property that indicates to the build system that the project is a library. It does this by
-  adding this line to the project's <code>default.properties</code> file:</p>
+  adding this line to the project's <code>project.properties</code> file:</p>
   <pre class="no-pretty-print">
 android.library=true
 </pre>
@@ -176,7 +177,7 @@
 
   <p>If you want to convert an existing application project to a library project, so that other
   applications can use it, you can do so by adding a the <code>android.library=true</code> property
-  to the application's <code>default.properties</code> file.</p>
+  to the application's <code>project.properties</code> file.</p>
 
   <h3 id="CreatingManifestFile">Creating the manifest file</h3>
 
@@ -225,13 +226,13 @@
 
   <p>This command updates the application project's build properties to include a reference to the
   library project. Specifically, it adds an <code>android.library.reference.<em>n</em></code>
-  property to the project's <code>default.properties</code> file. For example:</p>
+  property to the project's <code>project.properties</code> file. For example:</p>
   <pre class="no-pretty-print">
 android.library.reference.1=path/to/library_projectA
 </pre>
 
   <p>If you are adding references to multiple libraries, note that you can set their relative
-  priority (and merge order) by manually editing the <code>default.properties</code> file and
+  priority (and merge order) by manually editing the <code>project.properties</code> file and
   adjusting the each reference's <code>.<em>n</em></code> index as appropriate. For example, assume
   these references:</p>
   <pre class="no-pretty-print">
diff --git a/docs/html/guide/developing/tools/proguard.jd b/docs/html/guide/developing/tools/proguard.jd
index b97babe..eca262a 100644
--- a/docs/html/guide/developing/tools/proguard.jd
+++ b/docs/html/guide/developing/tools/proguard.jd
@@ -59,7 +59,7 @@
   customizing the ProGuard configuration file.</p>
 
   <p>To enable ProGuard so that it runs as part of an Ant or Eclipse build, set the
-  <code>proguard.config</code> property in the <code>&lt;project_root&gt;/default.properties</code>
+  <code>proguard.config</code> property in the <code>&lt;project_root&gt;/project.properties</code>
   file. The path can be an absolute path or a path relative to the project's root.</p>
 <p>If you left the <code>proguard.cfg</code> file in its default location (the project's root directory),
 you can specify its location like this:</p>
diff --git a/docs/html/guide/developing/tools/zipalign.jd b/docs/html/guide/developing/tools/zipalign.jd
index ebf177b..3216080 100644
--- a/docs/html/guide/developing/tools/zipalign.jd
+++ b/docs/html/guide/developing/tools/zipalign.jd
@@ -21,7 +21,7 @@
 The build scripts used
 when compiling your application with Ant will also zipalign your .apk,
 as long as you have provided the path to your keystore and the key alias in
-your project {@code build.properties} file, so that the build tools 
+your project {@code ant.properties} file, so that the build tools 
 can sign the package first.</p>
 
 <p class="caution"><strong>Caution:</strong> zipalign must only be performed
diff --git a/docs/html/guide/publishing/app-signing.jd b/docs/html/guide/publishing/app-signing.jd
index df240e2..193c0fd 100644
--- a/docs/html/guide/publishing/app-signing.jd
+++ b/docs/html/guide/publishing/app-signing.jd
@@ -434,13 +434,13 @@
 
 <p>However, the Ant build script can also perform the signing
 and aligning for you, if you have provided the path to your keystore and the name of
-your key alias in the project's {@code build.properties} file. With this information provided,
+your key alias in the project's {@code ant.properties} file. With this information provided,
 the build script will prompt you for your keystore and alias password when you perform
 <code>ant release</code>, it will sign the package and then align it. The final output
 file in {@code bin/} will instead be 
 <code><em>&lt;your_project_name></em>-release.apk</code>. With these steps
 automated for you, you're able to skip the manual procedures below (steps 3 and 4).
-To learn how to specify your keystore and alias in the {@code build.properties} file,
+To learn how to specify your keystore and alias in the {@code ant.properties} file,
 see <a href="{@docRoot}guide/developing/building/building-cmdline.html#ReleaseMode">
 Building and Running Apps on the Command Line</a>.</p>
 
diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd
index d89a8ca..fc0de9d 100644
--- a/docs/html/guide/publishing/licensing.jd
+++ b/docs/html/guide/publishing/licensing.jd
@@ -662,7 +662,7 @@
 </ol>
 
 <p> When created, the project is
-predefined as a library project in its <code>default.properties</code> file, so
+predefined as a library project in its <code>project.properties</code> file, so
 no further configuration is needed. </p>
 
 <p>For more information about how to create an application project or work with
@@ -712,7 +712,7 @@
 
 <p>If you are developing using the SDK command-line tools, navigate to the
 directory containing your application project and open the
-<code>default.properties</code> file. Add a line to the file that specifies the
+<code>project.properties</code> file. Add a line to the file that specifies the
 <code>android.library.reference.&lt;n&gt;</code> key and the path to the
 library. For example: </p>
 
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index 912b6fd..ed9f990 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -641,6 +641,7 @@
         android:drawable="@[package:]drawable/<em>drawable_resource</em>"
         android:state_pressed=["true" | "false"]
         android:state_focused=["true" | "false"]
+        android:state_hovered=["true" | "false"]
         android:state_selected=["true" | "false"]
         android:state_checkable=["true" | "false"]
         android:state_checked=["true" | "false"]
@@ -692,6 +693,11 @@
           <dd><em>Boolean</em>. "true" if this item should be used when the object is focused (such as when a button
 is highlighted using the trackball/d-pad); "false" if this item should be used in the default,
 non-focused state.</dd>
+        <dt><code>android:state_hovered</code></dt>
+          <dd><em>Boolean</em>. "true" if this item should be used when the object is being hovered
+by a cursor; "false" if this item should be used in the default, non-hovered state. Often, this
+drawable may be the same drawable used for the "focused" state.
+          <p>Introduced in API level 14.</p></dd>
         <dt><code>android:state_selected</code></dt>
           <dd><em>Boolean</em>. "true" if this item should be used when the object is selected (such as when a
 tab is opened); "false" if this item should be used when the object is not selected.</dd>
@@ -729,6 +735,8 @@
           android:drawable="@drawable/button_pressed" /> &lt;!-- pressed --&gt;
     &lt;item android:state_focused="true"
           android:drawable="@drawable/button_focused" /> &lt;!-- focused --&gt;
+    &lt;item android:state_hovered="true"
+          android:drawable="@drawable/button_focused" /> &lt;!-- hovered --&gt;
     &lt;item android:drawable="@drawable/button_normal" /> &lt;!-- default --&gt;
 &lt;/selector>
 </pre>
diff --git a/docs/html/guide/topics/resources/menu-resource.jd b/docs/html/guide/topics/resources/menu-resource.jd
index 64cdf21..fb7612e 100644
--- a/docs/html/guide/topics/resources/menu-resource.jd
+++ b/docs/html/guide/topics/resources/menu-resource.jd
@@ -43,9 +43,10 @@
           android:titleCondensed="<em>string</em>"
           android:icon="@[package:]drawable/<em>drawable_resource_name</em>"
           android:onClick="<em>method name</em>"
-          android:showAsAction=["ifRoom" | "never" | "withText" | "always"]
+          android:showAsAction=["ifRoom" | "never" | "withText" | "always" | "collapseActionView"]
           android:actionLayout="@[package:]layout/<em>layout_resource_name</em>"
           android:actionViewClass="<em>class name</em>"
+          android:actionProviderClass="<em>class name</em>"
           android:alphabeticShortcut="<em>string</em>"
           android:numericShortcut="<em>string</em>"
           android:checkable=["true" | "false"]
@@ -131,6 +132,9 @@
 Avoid using this unless it's critical that the item always appear in the action
 bar. Setting multiple items to always appear as action items can result in them overlapping
 with other UI in the action bar.</td></tr>
+            <tr><td><code>collapseActionView</code></td><td>The action view associated
+with this action item (as declared by <code>android:actionViewLayout</code>) is
+collapsible.<br/>Introduced in API Level 14.</td></tr>
           </table>
           <p>See the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> developer 
 guide for more information.</p>
@@ -143,9 +147,10 @@
 guide for more information.</p>
           <p>Introduced in API Level 11.</p></dd>
 
-        <dt><code>android:actionViewClassName</code></dt>
+        <dt><code>android:actionViewClass</code></dt>
           <dd><em>Class name</em>. A fully-qualified class name for the {@link android.view.View}
-to use as the action view.
+to use as the action view. For example,
+{@code "android.widget.SearchView"} to use {@link android.widget.SearchView} as an action view.
           <p>See the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> developer 
 guide for more information.</p>
           <p class="warning"><strong>Warning:</strong> If you obfuscate your code using <a
@@ -154,6 +159,17 @@
 functionality.</p>
           <p>Introduced in API Level 11.</p></dd>
 
+        <dt><code>android:actionProviderClass</code></dt>
+          <dd><em>Class name</em>. A fully-qualified class name for the {@link
+android.view.ActionProvider} to use in place of the action item. For example,
+{@code "android.widget.ShareActionProvider"} to use {@link android.widget.ShareActionProvider}.
+          <p>See the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> developer
+guide for more information.</p>
+          <p class="warning"><strong>Warning:</strong> If you obfuscate your code using <a
+href="{@docRoot}guide/developing/tools/proguard.html">ProGuard</a> (or a similar tool),
+be sure to exclude the class you specify in this attribute from renaming, because it can break the
+functionality.</p>
+          <p>Introduced in API Level 14.</p></dd>
 
         <dt><code>android:alphabeticShortcut</code></dt>
           <dd><em>Char</em>. A character for the alphabetic shortcut key.</dd>
diff --git a/docs/html/intl/ja/guide/developing/eclipse-adt.jd b/docs/html/intl/ja/guide/developing/eclipse-adt.jd
index e7accd8..397c006 100644
--- a/docs/html/intl/ja/guide/developing/eclipse-adt.jd
+++ b/docs/html/intl/ja/guide/developing/eclipse-adt.jd
@@ -77,7 +77,7 @@
   <dd>アプリケーションのリソース用(描画ファイル、レイアウト ファイル、文字列値など)のフォルダです。<a href="{@docRoot}guide/topics/resources/index.html">Resources and Assets</a>をご覧ください。</dd>
     <dt><code>AndroidManifest.xml</code></dt>
   <dd>このプロジェクトの Android マニフェストです。<a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml File</a>をご覧ください。</dd>
-    <dt><code>default.properties</code></dt>
+    <dt><code>project.properties</code></dt>
   <dd>このファイルには、ビルド ターゲットのようなプロジェクトの設定が含まれます。このファイルはプロジェクトに不可欠なので、ソース リビジョン管理システムで管理する必要があります。このファイルを手動で編集しないでください。プロジェクトのプロパティを編集するには、プロジェクト フォルダを右クリックして、[プロパティ(Properties)] を選択します。</dd>
   </dl>
 
diff --git a/docs/html/intl/ja/guide/developing/other-ide.jd b/docs/html/intl/ja/guide/developing/other-ide.jd
index 2983da2..ba82c18 100644
--- a/docs/html/intl/ja/guide/developing/other-ide.jd
+++ b/docs/html/intl/ja/guide/developing/other-ide.jd
@@ -98,8 +98,8 @@
 <ul>
   <li><code>AndroidManifest.xml</code> - アプリケーションのマニフェスト ファイル。指定したプロジェクトの Activity クラスと同期されます。</li>
   <li><code>build.xml</code> - Ant 用のビルド ファイルです。</li>
-  <li><code>default.properties</code> - ビルド システム用のプロパティです。このファイルを変更しないでください。<em></em></li>
-  <li><code>build.properties</code> - ビルド システム用のカスタマイズ可能なプロパティです。このファイルを編集して、Ant が使用するデフォルトのビルド設定をオーバーライドできます。</li>
+  <li><code>project.properties</code> - ビルド システム用のプロパティです。このファイルを変更しないでください。<em></em></li>
+  <li><code>ant.properties</code> - ビルド システム用のカスタマイズ可能なプロパティです。このファイルを編集して、Ant が使用するデフォルトのビルド設定をオーバーライドできます。</li>
   <li><code>src<em>/your/package/namespace/ActivityName</em>.java</code> - プロジェクトの作成時に指定した Activity クラスです。</li>
   <li><code>bin/</code> - ビルド スクリプト用の出力ディレクトリです。</li>
   <li><code>gen/</code> - <code>Ant</code> が生成するファイル(<code>R.java</code> など)が含まれます。 </li>
diff --git a/docs/html/resources/articles/zipalign.jd b/docs/html/resources/articles/zipalign.jd
index 9e767aa..d3c68a6 100644
--- a/docs/html/resources/articles/zipalign.jd
+++ b/docs/html/resources/articles/zipalign.jd
@@ -67,7 +67,7 @@
 information to sign the packages, since aligning has to happen after signing. In
 order to be able to sign packages, and therefore to align them, <em>Ant</em>
 needs to know the location of the keystore and the name of the key in
-<code>build.properties</code>. The name of the properties are
+<code>ant.properties</code>. The name of the properties are
 <code>key.store</code> and <code>key.alias</code> respectively. If those
 properties are present, the signing tool will prompt to enter the store/key
 passwords during the build, and the script will sign and then align the apk
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index 9267373..c0a2284 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -578,13 +578,13 @@
     }
   },
   {
-    tags: ['sample', 'media' ],
+    tags: ['sample', 'media', 'updated'],
     path: 'samples/RandomMusicPlayer/index.html',
     title: {
       en: 'Random Music Player'
     },
     description: {
-      en: 'Demonstrates how to write a multimedia application that plays music from the device and from URLs. It manages media playback from a service and can play music in the background, respecting audio focus changes.'
+      en: 'Demonstrates how to write a multimedia application that plays music from the device and from URLs. It manages media playback from a service and can play music in the background, respecting audio focus changes. Also shows how to use the new Remote Control APIs in API level 14.'
     }
   },
   {
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index bbf7cbe..333efa2 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -1,8 +1,8 @@
 page.title=ADT Plugin for Eclipse
-adt.zip.version=12.0.0
-adt.zip.download=ADT-12.0.0.zip
-adt.zip.bytes=5651973
-adt.zip.checksum=8ad85d0f3da4a2b8dadfddcc2d66dbcb
+adt.zip.version=14.0.0
+adt.zip.download=ADT-14.0.0.zip
+adt.zip.bytes=6745584
+adt.zip.checksum=a645330d90fd9dae6187662bb1c3c644
 
 @jd:body
 
@@ -22,7 +22,7 @@
     </li>
     <li><a href="#updating">Updating the ADT Plugin</a></li>
   </ol>
-  
+
   <h2>See also</h2>
   <ol>
     <li><a href="{@docRoot}guide/developing/tools/adt.html">Android Developer Tools</a></li>
@@ -110,6 +110,124 @@
   <a href="#" onclick="return toggleDiv(this)">
         <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px"
 width="9px" />
+ADT 14.0.0</a> <em>(October 2011)</em>
+  <div class="toggleme">
+<dl>
+
+<dt>Dependencies:</dt>
+
+<dd>ADT 14.0.0 is designed for use with <a href="{@docRoot}sdk/tools-notes.html">SDK Tools r14</a>.
+If you haven't already installed SDK Tools r14 into your SDK, use the Android SDK and AVD Manager to
+do so.</dd>
+
+<dt>Build system</dt>
+<dd>
+  <ul>
+    <li>Changed <code>default.properties</code> to <code>project.properties</code> and
+    <code>build.properties</code> to <code>ant.properties</code>. ADT automatically
+    renames these files, if necessary, when you open a project in Eclipse.</li>
+    <li>Changed how library projects are built in Eclipse.</a></li>
+    <li>Changed output of <code>javac</code> from <code>bin/</code> to <code>bin/classes</code>
+    in Eclipse.</li>
+    <li>Improved incremental builds so that resource compilation runs less frequently. Builds no
+    longer run when you edit strings or layouts (unless you add a new <code>id</code>) and no longer
+    run once for each library project.</li>
+    <li>Introduced a "PNG crunch cache" that only runs on modified PNG files, instead of
+    crunching all existing PNG files, all the time.</li>
+    <li>Modified resource compilation so it no longer happens for normal save operations. It only
+    happens when running or debugging (the build option that lets you disable the packaging
+    step, which was introduced in ADT 12, is now on by default.)</li>
+  </ul>
+<p>For a complete overview of the build system changes and what you need to do to support them,
+see the <a href="http://tools.android.com/recent/buildchangesinrevision14">Android Tools Project
+site</a>.</p>
+</dd>
+
+<dt>General improvements</dt>
+<dd>
+  <ul>
+
+
+<li>Added a Welcome Wizard to help with the initial setup of the Android
+development environment (<a href="http://tools.android.com/recent/welcomewizard">more
+info</a>).</li>
+<li>Integrated the Android Asset Studio, which helps you create icons for things
+like the launcher, menus, and tabs. (<a
+href="http://tools.android.com/recent/assetstudiointegration">more
+info</a>).</li>
+<li>Revamped the Logcat view and added support to display and filter logs by
+   application names as well as PIDs (<a
+   href="http://tools.android.com/recent/updatedlogcatviewer">more info</a>).</li>
+<li>Revamped the SDK Manager UI (<a href="http://tools.android.com/recent/newsdkmanager">more
+info</a>).</li>
+<li>Revamped the New Project and the New XML File wizards to have
+multiple pages. Sample projects are now copied into the workspace such that they can be modified
+and deleted without affecting the master copy
+(<a href="http://tools.android.com/recent/revampedwizards">more info</a>).</li>
+<li>Removed the dependency on Eclipse GEF.</li>
+</ul>
+</dd>
+
+<dt>XML and Java editors</dt>
+<dd>
+  <ul>
+    <li>Added a new XML formatter that formats all XML files according to the
+   standard Android coding style. The formatter can also reorder
+   attributes to follow a recommended order and processes any changes made in the Layout editor.
+(<a href="http://tools.android.com/recent/xmlformatter">more info</a>).</li>
+  <li>Added the "Go to Matching" (Ctrl-Shift-P) feature, which lets you jump
+between opening and closing tags in XML files.</li>
+  <li>Added support for the "Select Enclosing Element" feature on Mac.</li>
+  <li>Added a Quickfix for extracting Strings when the caret is inside a String (<a href="">see
+more</a>).</li>
+  <li>Improved "smart indent", which allows automatic indentation and un-indentation
+   when pressing the Return key in XML editors (<a
+href="http://tools.android.com/recent/xmleditingimprovements">more info</a>).</li>
+
+  </ul>
+</dd>
+
+<dt>Layout editor</dt>
+<dd>
+  <ul>
+    <li>Added tooltip feedback for dragging and resizing operations. For
+    example, when dragging in a relative layout, the proposed
+    constraints are shown. When resizing, the new dimensions are
+    shown (<a href="http://tools.android.com/recent/layouteditorfeedbacktooltips">more
+info</a>).</li>
+    <li>Added the ability to suppress rendering fidelity warnings (<a
+href="http://tools.android.com/recent/suppressrenderwarnings">more info</a>).</li>
+    <li>Added "Remove Container" visual refactoring that removes the
+    children of a container up to the top level and transfers
+    namespace and layout attributes if necessary (<a
+href="http://tools.android.com/recent/removecontainervisualrefactoring">more info</a>).</li>
+    <li>Added pull-right menus to the context menu for accessing
+    properties of the parents, which is useful when the children fully
+    cover the parent and make it hard to select on their own.</li>
+     <li>Improved access to properties in the context menu. The most
+    frequently set attributes for each view are listed at the top of
+    the menu. The Properties menu offers access to the most
+    recently set attributes, attributes organized by their defining
+    view, and layout attributes only or all attributes alphabetically (<a
+href="http://tools.android.com/recent/layouteditorcontextmenuimprovements">more info</a>).</li>
+  </ul>
+</dd>
+
+<dt>Bug fixes</dt>
+<dd>Fixed many bugs and added <a
+href="http://tools.android.com/recent/miscellaneousrecentfixes">minor improvements</a>, in
+particular some <a href="http://tools.android.com/recent/linuxfixes">critical bug fixes on
+Linux</a>.</dd>
+
+</div>
+</div>
+
+
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
+width="9px" />
 ADT 12.0.0</a> <em>(July 2011)</em>
   <div class="toggleme">
 <dl>
@@ -141,7 +259,7 @@
 <dt>Build system</dt>
 <dd>
 <ul>
-  <li>A new option lets you disable the packaging step in the automatic
+  <li id="build-option">A new option lets you disable the packaging step in the automatic
   builders. This improves performance when saving files by not
   performing a full build, which can take a long time for large projects.
   If the option is enabled, the APK is packaged when the
@@ -225,7 +343,7 @@
 href="http://tools.android.com/recent/customviewsinthepalette">more info</a>).</li>
     <li>Fragments are available in the palette for placement in your layout. In the tool, you can
 choose which layout to show rendered for a given fragment tag. Go to declaration works for fragment
-classes (<a href="http://tools.android.com/recent/fragmentsupport">more info</a>).</li> 
+classes (<a href="http://tools.android.com/recent/fragmentsupport">more info</a>).</li>
     <li>The layout editor automatically applies a "zoom to fit" for newly
     opened files as well as on device size and orientation changes to
     ensure that large layouts are always fully visible unless you
@@ -253,7 +371,7 @@
 
 <dt>XML editors:</dt>
 <dd>
-<ul>  
+<ul>
   <li>Code completion has been significantly improved. It now works
   with {@code &lt;style&gt;} elements, completes dimensional units,
   sorts resource paths in values based on the attribute name, and more. There are also many fixes to
@@ -270,7 +388,7 @@
 
 <dt>DDMS:</dt>
 <dd>
-<ul>  
+<ul>
   <li>"New Folder" action in the File Explorer.</li>
   <li>The screenshot dialog will add timestamps to the filenames and preserve the orientation on
 snapshot refresh.</li>
@@ -367,7 +485,7 @@
         (<a href="http://tools.android.com/recent/improvedsupportformergetags">details</a>).</li>
         <li>Improved rendering error diagnostics.</li>
       </ul>
-    </li>    
+    </li>
   </ul>
 </dd>
 </dl>
@@ -391,14 +509,14 @@
 
 <dt>General notes:</dt>
 <dd>
-  <ul>    
+  <ul>
     <li>"Go To Declaration" hyperlink support: You can jump directly from code references (such as
     <code>R.id.main</code>) to the corresponding XML declaration, or from XML attributes (such as
     <code>@string</code>) to the corresponding resource definition, or from manifest XML
     registrations to activities and services.</li>
     <li>Improvements were made to name refactoring.</li>
     <li>AVDs now automatically save their state, so they can restart almost instantly. You can enable this feature when
-    creating an AVD or by editing an AVD with the AVD Manager.</li> 
+    creating an AVD or by editing an AVD with the AVD Manager.</li>
     <li>Improvements to the Visual Layout Editor:
       <ul>
         <li>Support for rendering targets: You can now choose an arbitrary Android platform to
@@ -436,10 +554,10 @@
         into an {@link android.widget.AdapterView}.</li>
         <li>Outline reordering: Reordering your views in the Outline tab is much easier
         (<a href="http://tools.android.com/recent/outlineimprovements">Details</a>).</li>
-        <li>Fix for keybinding bug where keyboard shortcuts did not work (Issues 
-        <a href="http://code.google.com/p/android/issues/detail?id=13231">13231</a> and 
+        <li>Fix for keybinding bug where keyboard shortcuts did not work (Issues
+        <a href="http://code.google.com/p/android/issues/detail?id=13231">13231</a> and
         <a href="http://code.google.com/p/android/issues/detail?id=13134">13134</a>).</li>
-        <li>Fix for problems with Custom layout attribute menu (Issue 
+        <li>Fix for problems with Custom layout attribute menu (Issue
         <a href="http://code.google.com/p/android/issues/detail?id=13134">13134</a>).</li>
         <li>Automatic configuration for various view types: Certain views have properties configured
         by default. For example, the width of an {@link android.widget.EditText} object is set to
@@ -455,7 +573,7 @@
         and <a href="http://code.google.com/p/android/issues/detail?id=13092">13092</a>).</li>
         <li>Included layouts can be rendered and edited in the context of the layouts that include
         them. From a layout using an <a href="{@docRoot}guide/topics/resources/layout-resource.html#include-element">
-        <code>&lt;include&gt;</code></a> tag, double-clicking on the 
+        <code>&lt;include&gt;</code></a> tag, double-clicking on the
         <a href="{@docRoot}guide/topics/resources/layout-resource.html#include-element">
         <code>&lt;include&gt;</code></a> element edits the referenced layout in the context of the
         current layout. Additionally, when editing a layout that is included by other layouts,
@@ -466,12 +584,12 @@
     <li>This release fixes many other bugs, but the most important ones are listed below:
   <ul>
    <li>Fixed issue that prevented launching debug builds on productions devices when
-    <code>debuggable=true</code> was not set in the Android manifest.</li>    
+    <code>debuggable=true</code> was not set in the Android manifest.</li>
     <li>The LogCat view in DDMS properly handles UTF-8 characters.</li>
     <li>The SDK Manager is more reliable on Windows
     (<a href="http://tools.android.com/recent/sdkmanagerfixes">Details</a>).</li>
-    <li>A JUnit initialization bug that prevented you from working with JUnit tests was fixed 
-    (Issue <a href="http://code.google.com/p/android/issues/detail?id=12411">12411</a>).</li>   
+    <li>A JUnit initialization bug that prevented you from working with JUnit tests was fixed
+    (Issue <a href="http://code.google.com/p/android/issues/detail?id=12411">12411</a>).</li>
   </ul>
 </li>
   </ul>
@@ -828,7 +946,7 @@
 
 <ul>
 <li>If Eclipse is already installed on your computer, make sure that it is
-a version that is compatible with ADT and the Android SDK. 
+a version that is compatible with ADT and the Android SDK.
 
 <li>If you need to install or update Eclipse, you can download it from this
 location:
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 105c868..065f41b 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -1,21 +1,21 @@
 page.title=Android SDK
 sdk.redirect=0
 
-sdk.win_installer=installer_r13-windows.exe
-sdk.win_installer_bytes=36533357
-sdk.win_installer_checksum=cd3a76fe2b8ed62b2d03cf1851692e2d
+sdk.win_installer=installer_r14-windows.exe
+sdk.win_installer_bytes=33860145
+sdk.win_installer_checksum=7a563491bf4671d09b9da0dcde85f212
 
-sdk.win_download=android-sdk_r13-windows.zip
-sdk.win_bytes=36487911
-sdk.win_checksum=de8a039891e5e65b7742f188f07b992d
+sdk.win_download=android-sdk_r14-windows.zip
+sdk.win_bytes=33852972
+sdk.win_checksum=d1381a0cc8e6f9358174aa6d051ba379
 
-sdk.mac_download=android-sdk_r13-mac_x86.zip
-sdk.mac_bytes=30233944
-sdk.mac_checksum=f4002a0344b48856c09dec796acecd4d
+sdk.mac_download=android-sdk_r14-macosx.zip
+sdk.mac_bytes=30426052
+sdk.mac_checksum=df0a5c5b5327ffcaf256ce735998e12a
 
-sdk.linux_download=android-sdk_r13-linux_x86.tgz
-sdk.linux_bytes=30034328
-sdk.linux_checksum=d80d7530a46c665644ae76084a9a0dc4
+sdk.linux_download=android-sdk_r14-linux.tgz
+sdk.linux_bytes=26083315
+sdk.linux_checksum=2049d5c1a164fcae47a5e93c52200752
 
 @jd:body
 
@@ -39,7 +39,7 @@
 <style type="text/css">
   .offline-message { display:none; }
 </style>
-      
+
 <p class="offline-message">For more information about how to set up your
 development environment, read the guide to <a href="installing.html">Installing the SDK</a>.</p>
 
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 8a57312..74d1c23 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -22,7 +22,7 @@
           <span style="display:none" class="ja">ダウンロード</span>
           <span style="display:none" class="zh-CN">下载</span>
           <span style="display:none" class="zh-TW">下載</span>
-        </a></li><?cs 
+        </a></li><?cs
   /if ?>
       <li><a href="<?cs var:toroot ?>sdk/installing.html">
           <span class="en">Installing the SDK</span>
@@ -87,7 +87,7 @@
         <div><a href="<?cs var:toroot ?>sdk/android-3.1.html">
         <span class="en">Android 3.1 Platform</span></a></div>
         <ul>
-          <li><a href="<?cs var:toroot ?>sdk/android-3.1-highlights.html">Platform Highlights</a></li> 
+          <li><a href="<?cs var:toroot ?>sdk/android-3.1-highlights.html">Platform Highlights</a></li>
           <li><a href="<?cs var:toroot ?>sdk/api_diff/12/changes.html">API Differences Report &raquo;</a></li>
         </ul>
       </li>
@@ -95,7 +95,7 @@
         <div><a href="<?cs var:toroot ?>sdk/android-3.0.html">
         <span class="en">Android 3.0 Platform</span></a></div>
         <ul>
-          <li><a href="<?cs var:toroot ?>sdk/android-3.0-highlights.html">Platform Highlights</a></li> 
+          <li><a href="<?cs var:toroot ?>sdk/android-3.0-highlights.html">Platform Highlights</a></li>
           <li><a href="<?cs var:toroot ?>sdk/api_diff/11/changes.html">API Differences Report &raquo;</a></li>
         </ul>
       </li>
@@ -104,14 +104,14 @@
       <div><a href="<?cs var:toroot ?>sdk/android-2.3.3.html">
       <span class="en">Android 2.3.3 Platform</span></a></div>
         <ul>
-          <li><a href="<?cs var:toroot ?>sdk/api_diff/10/changes.html">API Differences Report &raquo;</a></li> 
+          <li><a href="<?cs var:toroot ?>sdk/api_diff/10/changes.html">API Differences Report &raquo;</a></li>
         </ul>
       </li>
       <li class="toggle-list">
         <div><a href="<?cs var:toroot ?>sdk/android-2.2.html">
         <span class="en">Android 2.2 Platform</span></a></div>
         <ul>
-          <li><a href="<?cs var:toroot ?>sdk/android-2.2-highlights.html">Platform Highlights</a></li> 
+          <li><a href="<?cs var:toroot ?>sdk/android-2.2-highlights.html">Platform Highlights</a></li>
           <li><a href="<?cs var:toroot ?>sdk/api_diff/8/changes.html">API Differences Report &raquo;</a></li>
         </ul>
       </li>
@@ -129,8 +129,8 @@
             <div><a href="<?cs var:toroot ?>sdk/android-2.3.html">
             <span class="en">Android 2.3 Platform</span></a></div>
               <ul>
-                <li><a href="<?cs var:toroot ?>sdk/android-2.3-highlights.html">Platform Highlights</a></li> 
-                <li><a href="<?cs var:toroot ?>sdk/api_diff/9/changes.html">API Differences Report &raquo;</a></li> 
+                <li><a href="<?cs var:toroot ?>sdk/android-2.3-highlights.html">Platform Highlights</a></li>
+                <li><a href="<?cs var:toroot ?>sdk/api_diff/9/changes.html">API Differences Report &raquo;</a></li>
               </ul>
           </li>
           <li><a href="<?cs var:toroot ?>sdk/android-2.0.1.html">Android 2.0.1 Platform</a></li>
@@ -142,7 +142,7 @@
       </li>
     </ul>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r13</a> <span
+      <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r14</a> <span
 class="new">new!</span></li>
       <li><a href="<?cs var:toroot ?>sdk/win-usb.html">Google USB Driver, r4</a></li>
       <li><a href="<?cs var:toroot ?>sdk/compatibility-library.html">Compatibility Package,
@@ -161,14 +161,15 @@
       <span style="display:none" class="zh-TW"></span>
       </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 12.0.0
+      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 14.0.0
       <span style="display:none" class="de"></span>
       <span style="display:none" class="es"></span>
       <span style="display:none" class="fr"></span>
       <span style="display:none" class="it"></span>
       <span style="display:none" class="ja"></span>
       <span style="display:none" class="zh-CN"></span>
-      <span style="display:none" class="zh-TW"></span></a>
+      <span style="display:none" class="zh-TW"></span></a> <span
+class="new">new!</span>
       </li>
     </ul>
   </li>
diff --git a/docs/html/sdk/tools-notes.jd b/docs/html/sdk/tools-notes.jd
index 2179cec..2d044ed 100644
--- a/docs/html/sdk/tools-notes.jd
+++ b/docs/html/sdk/tools-notes.jd
@@ -2,7 +2,7 @@
 @jd:body
 
 <p>SDK Tools is a downloadable component for the Android SDK. It includes the
-complete set of development and debugging tools for the Android SDK. </p>
+complete set of development and debugging tools for the Android SDK.</p>
 
 <p>If you are new to the Android SDK, the <a
 href="{@docRoot}sdk/index.html">SDK starter package</a> installs the
@@ -13,7 +13,7 @@
 update, rather than downloading a new SDK starter package. For more information
 about how to update, see <a
 href="{@docRoot}sdk/adding-components.html#UpdatingComponents">Updating SDK
-Components</a>. </p>
+Components</a>.</p>
 
 
 <h2 id="notes">Revisions</h2>
@@ -62,10 +62,56 @@
 }
 </style>
 
-
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
-        <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+    <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px"
+    width="9px" />SDK Tools, Revision 14</a> <em>(October 2011)</em>
+  <div class="toggleme">
+  <dl>
+<dt>Dependencies:</dt>
+<dd>
+  <ul><li>Android SDK Platform-tools revision 8 or later.</li>
+  <li>If you are developing in Eclipse with ADT, note that the SDK Tools r14 is designed for use
+  with ADT 14.0.0 and later. If you haven't already, we highly recommend updating your <a
+  href="{@docRoot}sdk/eclipse-adt.html">ADT Plugin</a> to 14.0.0.</li>
+  <li>If you are developing outside Eclipse, you must have <a href="http://ant.apache.org/">Apache
+  Ant</a> 1.8 or later.</li>
+</ul>
+
+<dt>General notes:</dt>
+<dd>
+  <ul>
+     <li>Changed <code>default.properties</code> to <code>project.properties</code> and
+    <code>build.properties</code> to <code>ant.properties</code>. Any existing
+    projects that you build with Ant must be updated with the <code>android update project</code>
+    command.</li>
+    <li>Changed Ant <code>build.xml</code> file to support improvements to the
+    build system and added and modified Ant commands to support these changes. For a list of Ant
+commands, see the
+<a href="{@docRoot}guide/developing/building/building-cmdline.html#AntReference">Ant Command
+Reference</a>.</li>
+
+    <li>Changed how library projects are built.</a></li>
+    <li>Improved incremental builds, so that resource compilation runs less frequently. Builds no
+    longer run when you edit strings or layouts (unless you add a new <code>id</code>) and no longer
+    run once for each library project.</li>
+    <li>Introduced a "PNG crunch cache" that only runs on modified PNG files, instead of
+    crunching all existing PNG files, all the time.</li>
+    <li>Revamped the SDK Manager UI (<a href="http://tools.android.com/recent/newsdkmanager">more
+info</a>).</li>
+  </ul>
+  <p>For a complete overview of the build system changes and what you need to do to support them,
+see the <a href="http://tools.android.com/recent/buildchangesinrevision14">Android Tools Project
+site</a>.</p>
+</dd>
+</dl>
+</div>
+</div>
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
+        width="9px" />
 SDK Tools, Revision 13</a> <em>(September 2011)</em>
   <div class="toggleme">
   <dl>
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 3b79f06..1e24599 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1972,6 +1972,12 @@
                     sampleIndex, &syncSampleIndex, findFlags);
         }
 
+        uint32_t sampleTime;
+        if (err == OK) {
+            err = mSampleTable->getMetaDataForSample(
+                    sampleIndex, NULL, NULL, &sampleTime);
+        }
+
         if (err != OK) {
             if (err == ERROR_OUT_OF_RANGE) {
                 // An attempt to seek past the end of the stream would
@@ -1984,10 +1990,6 @@
             return err;
         }
 
-        uint32_t sampleTime;
-        CHECK_EQ((status_t)OK, mSampleTable->getMetaDataForSample(
-                    sampleIndex, NULL, NULL, &sampleTime));
-
         if (mode == ReadOptions::SEEK_CLOSEST) {
             targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 79abfed..d6e4d1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -87,6 +87,7 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NotificationRowLayout;
 
 public class PhoneStatusBar extends StatusBar {
     static final String TAG = "PhoneStatusBar";
@@ -103,8 +104,10 @@
     static final int EXPANDED_LEAVE_ALONE = -10000;
     static final int EXPANDED_FULL_OPEN = -10001;
 
-    private static final int MSG_ANIMATE = 1000;
-    private static final int MSG_ANIMATE_REVEAL = 1001;
+    private static final int MSG_ANIMATE = 100;
+    private static final int MSG_ANIMATE_REVEAL = 101;
+    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
+    private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
     private static final int MSG_SHOW_INTRUDER = 1002;
     private static final int MSG_HIDE_INTRUDER = 1003;
     private static final int MSG_OPEN_RECENTS_PANEL = 1020;
@@ -165,7 +168,7 @@
     
     // all notifications
     NotificationData mNotificationData = new NotificationData();
-    ViewGroup mPile;
+    NotificationRowLayout mPile;
 
     // position
     int[] mPositionTmp = new int[2];
@@ -324,7 +327,7 @@
 
         mExpandedDialog = new ExpandedDialog(context);
         mExpandedView = expanded;
-        mPile = (ViewGroup)expanded.findViewById(R.id.latestItems);
+        mPile = (NotificationRowLayout)expanded.findViewById(R.id.latestItems);
         mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
         mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
         mNoNotificationsTitle.setVisibility(View.GONE); // disabling for now
@@ -332,6 +335,7 @@
         mClearButton = expanded.findViewById(R.id.clear_all_button);
         mClearButton.setOnClickListener(mClearButtonListener);
         mClearButton.setAlpha(0f);
+        mClearButton.setEnabled(false);
         mDateView = (DateView)expanded.findViewById(R.id.date);
         mSettingsButton = expanded.findViewById(R.id.settings_button);
         mSettingsButton.setOnClickListener(mSettingsButtonListener);
@@ -1005,6 +1009,7 @@
         } else {
             mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
         }
+        mClearButton.setEnabled(clearable);
 
         /*
         if (mNoNotificationsTitle.isShown()) {
@@ -1114,6 +1119,12 @@
                 case MSG_ANIMATE_REVEAL:
                     doRevealAnimation();
                     break;
+                case MSG_OPEN_NOTIFICATION_PANEL:
+                    animateExpand();
+                    break;
+                case MSG_CLOSE_NOTIFICATION_PANEL:
+                    animateCollapse();
+                    break;
                 case MSG_SHOW_INTRUDER:
                     setIntruderAlertVisibility(true);
                     break;
@@ -1181,6 +1192,10 @@
     }
 
     public void animateCollapse(boolean excludeRecents) {
+        animateCollapse(excludeRecents, 1.0f);
+    }
+    
+    public void animateCollapse(boolean excludeRecents, float velocityMultiplier) {
         if (SPEW) {
             Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
                     + " mExpandedVisible=" + mExpandedVisible
@@ -1209,7 +1224,7 @@
         // and doesn't try to re-open the windowshade.
         mExpanded = true;
         prepareTracking(y, false);
-        performFling(y, -mSelfCollapseVelocityPx, true);
+        performFling(y, -mSelfCollapseVelocityPx*velocityMultiplier, true);
     }
 
     void performExpand() {
@@ -2086,13 +2101,57 @@
     }
 
     private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+        final int mini(int a, int b) {
+            return (b>a?a:b);
+        }
         public void onClick(View v) {
-            try {
-                mBarService.onClearAllNotifications();
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
+            synchronized (mNotificationData) {
+                // let's also queue up 400ms worth of animated dismissals
+                final int N = mini(5, mPile.getChildCount());
+
+                final ArrayList<View> snapshot = new ArrayList<View>(N);
+                for (int i=0; i<N; i++) {
+                    final View child = mPile.getChildAt(i);
+                    if (mPile.canChildBeDismissed(child)) snapshot.add(child);
+                }
+                new Thread(new Runnable() {
+                    @Override
+                    public void run() {
+                        final int ROW_DELAY = 100;
+
+                        mHandler.postDelayed(new Runnable() {
+                            public void run() {
+                                animateCollapse(false, 0f);
+                            }
+                        }, (N-1) * ROW_DELAY);
+
+                        mHandler.postDelayed(new Runnable() {
+                            public void run() {
+                                try {
+                                    mBarService.onClearAllNotifications();
+                                } catch (RemoteException ex) { }
+                            }
+                        }, N * ROW_DELAY + 500);
+
+                        mPile.setAnimateBounds(false); // temporarily disable some re-layouts
+
+                        for (View v : snapshot) {
+                            final View _v = v;
+                            mHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    mPile.dismissRowAnimated(_v, (int)(ROW_DELAY*0.25f));
+                                }
+                            });
+                            try {
+                                Thread.sleep(ROW_DELAY);
+                            } catch (InterruptedException ex) { }
+                        }
+                        
+                        mPile.setAnimateBounds(true); // reenable layout animation
+                    }
+                }).start();
             }
-            animateCollapse();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 06798c6..a7342dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -44,11 +44,11 @@
     private static final boolean DEBUG = false;
     private static final boolean SLOW_ANIMATIONS = DEBUG;
 
-    private static final boolean ANIMATE_LAYOUT = true;
-
     private static final int APPEAR_ANIM_LEN = SLOW_ANIMATIONS ? 5000 : 250;
     private static final int DISAPPEAR_ANIM_LEN = APPEAR_ANIM_LEN;
 
+    boolean mAnimateBounds = true;
+
     Rect mTmpRect = new Rect();
     int mNumRows = 0;
     int mRowHeight = 0;
@@ -93,6 +93,10 @@
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
     }
 
+    public void setAnimateBounds(boolean anim) {
+        mAnimateBounds = anim;
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
@@ -165,7 +169,7 @@
 
         final View childF = child;
 
-        if (ANIMATE_LAYOUT) {
+        if (mAnimateBounds) {
             child.setPivotY(0);
             final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
             alphaFade.setDuration(APPEAR_ANIM_LEN);
@@ -185,10 +189,18 @@
         }
     }
 
+    public void dismissRowAnimated(View child) {
+        dismissRowAnimated(child, 0);
+    }
+
+    public void dismissRowAnimated(View child, int vel) {
+        mSwipeHelper.dismissChild(child, vel);
+    }
+
     @Override
     public void removeView(View child) {
         final View childF = child;
-        if (ANIMATE_LAYOUT) {
+        if (mAnimateBounds) {
             if (mAppearingViews.containsKey(child)) {
                 mAppearingViews.remove(child);
             }
@@ -264,7 +276,7 @@
 
             mNumRows = numRows;
 
-            if (ANIMATE_LAYOUT && isShown()) {
+            if (mAnimateBounds && isShown()) {
                 ObjectAnimator.ofInt(this, "forcedHeight", computedHeight)
                     .setDuration(APPEAR_ANIM_LEN)
                     .start();
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7b8657a..5758954 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -228,6 +228,7 @@
     // completed.
     final Object mAgentConnectLock = new Object();
     IBackupAgent mConnectedAgent;
+    volatile boolean mBackupRunning;
     volatile boolean mConnecting;
     volatile long mLastBackupPass;
     volatile long mNextBackupPass;
@@ -434,6 +435,9 @@
                 IBackupTransport transport = getTransport(mCurrentTransport);
                 if (transport == null) {
                     Slog.v(TAG, "Backup requested but no transport available");
+                    synchronized (mQueueLock) {
+                        mBackupRunning = false;
+                    }
                     mWakelock.release();
                     break;
                 }
@@ -470,6 +474,9 @@
                     sendMessage(pbtMessage);
                 } else {
                     Slog.v(TAG, "Backup requested but nothing pending");
+                    synchronized (mQueueLock) {
+                        mBackupRunning = false;
+                    }
                     mWakelock.release();
                 }
                 break;
@@ -804,14 +811,19 @@
                         // Don't run backups now if we're disabled or not yet
                         // fully set up.
                         if (mEnabled && mProvisioned) {
-                            if (DEBUG) Slog.v(TAG, "Running a backup pass");
+                            if (!mBackupRunning) {
+                                if (DEBUG) Slog.v(TAG, "Running a backup pass");
 
-                            // Acquire the wakelock and pass it to the backup thread.  it will
-                            // be released once backup concludes.
-                            mWakelock.acquire();
+                                // Acquire the wakelock and pass it to the backup thread.  it will
+                                // be released once backup concludes.
+                                mBackupRunning = true;
+                                mWakelock.acquire();
 
-                            Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
-                            mBackupHandler.sendMessage(msg);
+                                Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
+                                mBackupHandler.sendMessage(msg);
+                            } else {
+                                Slog.i(TAG, "Backup time but one already running");
+                            }
                         } else {
                             Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
                         }
@@ -1948,9 +1960,14 @@
                 writeRestoreTokens();
             }
 
-            // Set up the next backup pass
-            if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
-                backupNow();
+            // Set up the next backup pass - at this point we can set mBackupRunning
+            // to false to allow another pass to fire, because we're done with the
+            // state machine sequence and the wakelock is refcounted.
+            synchronized (mQueueLock) {
+                mBackupRunning = false;
+                if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+                    backupNow();
+                }
             }
 
             // Only once we're entirely finished do we release the wakelock
@@ -2400,8 +2417,8 @@
                     mLatchObject.notifyAll();
                 }
                 sendEndBackup();
-                mWakelock.release();
                 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
+                mWakelock.release();
             }
         }
 
@@ -2908,8 +2925,8 @@
                     mLatchObject.notifyAll();
                 }
                 sendEndRestore();
-                mWakelock.release();
                 Slog.d(TAG, "Full restore pass complete.");
+                mWakelock.release();
             }
         }
 
@@ -5630,7 +5647,8 @@
                     + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
                     + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
             pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
-            pw.println("Last backup pass: " + mLastBackupPass
+            if (mBackupRunning) pw.println("Backup currently running");
+            pw.println("Last backup pass started: " + mLastBackupPass
                     + " (now = " + System.currentTimeMillis() + ')');
             pw.println("  next scheduled: " + mNextBackupPass);
 
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 5bdc146..44bdaeb 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -325,7 +325,7 @@
      *
      * {@hide}
      */
-    public ArrayList<SmsMessage> getAllMessagesFromIcc() {
+    public static ArrayList<SmsMessage> getAllMessagesFromIcc() {
         List<SmsRawData> records = null;
 
         try {
@@ -470,7 +470,7 @@
      *   <code>getAllMessagesFromIcc</code>
      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
      */
-    private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+    private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
         ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
         if (records != null) {
             int count = records.size();
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index e75d96d..fc8a145 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -36,7 +36,6 @@
  * A Short Message Service message.
  */
 public class SmsMessage {
-    private static final boolean LOCAL_DEBUG = true;
     private static final String LOG_TAG = "SMS";
 
     /**
@@ -78,6 +77,18 @@
      */
     public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
 
+    /**
+     * Indicates a 3GPP format SMS message.
+     * @hide pending API council approval
+     */
+    public static final String FORMAT_3GPP = "3gpp";
+
+    /**
+     * Indicates a 3GPP2 format SMS message.
+     * @hide pending API council approval
+     */
+    public static final String FORMAT_3GPP2 = "3gpp2";
+
     /** Contains actual SmsMessage. Only public for debugging and for framework layer.
      *
      * @hide
@@ -106,30 +117,47 @@
 
     }
 
-    /**
-     * Constructor
-     *
-     * @hide
-     */
-    public SmsMessage() {
-        this(getSmsFacility());
-    }
-
     private SmsMessage(SmsMessageBase smb) {
         mWrappedSmsMessage = smb;
     }
 
     /**
      * Create an SmsMessage from a raw PDU.
+     *
+     * <p><b>This method will soon be deprecated</b> and all applications which handle
+     * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
+     * intent <b>must</b> now pass the new {@code format} String extra from the intent
+     * into the new method {@code createFromPdu(byte[], String)} which takes an
+     * extra format parameter. This is required in order to correctly decode the PDU on
+     * devices that require support for both 3GPP and 3GPP2 formats at the same time,
+     * such as dual-mode GSM/CDMA and CDMA/LTE phones.
      */
     public static SmsMessage createFromPdu(byte[] pdu) {
-        SmsMessageBase wrappedMessage;
         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        String format = (PHONE_TYPE_CDMA == activePhone) ? FORMAT_3GPP2 : FORMAT_3GPP;
+        return createFromPdu(pdu, format);
+    }
 
-        if (PHONE_TYPE_CDMA == activePhone) {
+    /**
+     * Create an SmsMessage from a raw PDU with the specified message format. The
+     * message format is passed in the {@code SMS_RECEIVED_ACTION} as the {@code format}
+     * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
+     * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
+     *
+     * @param pdu the message PDU from the SMS_RECEIVED_ACTION intent
+     * @param format the format extra from the SMS_RECEIVED_ACTION intent
+     * @hide pending API council approval
+     */
+    public static SmsMessage createFromPdu(byte[] pdu, String format) {
+        SmsMessageBase wrappedMessage;
+
+        if (FORMAT_3GPP2.equals(format)) {
             wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
-        } else {
+        } else if (FORMAT_3GPP.equals(format)) {
             wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+        } else {
+            Log.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
+            return null;
         }
 
         return new SmsMessage(wrappedMessage);
@@ -144,57 +172,19 @@
      *
      * {@hide}
      */
-    public static SmsMessage newFromCMT(String[] lines){
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCMT(lines);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines);
-        }
-
-        return new SmsMessage(wrappedMessage);
-    }
-
-    /** @hide */
-    protected static SmsMessage newFromCMTI(String line) {
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCMTI(line);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMTI(line);
-        }
-
-        return new SmsMessage(wrappedMessage);
-    }
-
-    /** @hide */
-    public static SmsMessage newFromCDS(String line) {
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCDS(line);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCDS(line);
-        }
+    public static SmsMessage newFromCMT(String[] lines) {
+        // received SMS in 3GPP format
+        SmsMessageBase wrappedMessage =
+                com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines);
 
         return new SmsMessage(wrappedMessage);
     }
 
     /** @hide */
     public static SmsMessage newFromParcel(Parcel p) {
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromParcel(p);
-        }
+        // received SMS in 3GPP2 format
+        SmsMessageBase wrappedMessage =
+                com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p);
 
         return new SmsMessage(wrappedMessage);
     }
@@ -227,6 +217,9 @@
     /**
      * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
      * length in bytes (not hex chars) less the SMSC header
+     *
+     * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices.
+     * We should probably deprecate it and remove the obsolete test case.
      */
     public static int getTPLayerLengthForPDU(String pdu) {
         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
@@ -381,34 +374,6 @@
      * @return a <code>SubmitPdu</code> containing the encoded SC
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
-     * @hide
-     */
-    public static SubmitPdu getSubmitPdu(String scAddress,
-            String destinationAddress, String message,
-            boolean statusReportRequested, byte[] header) {
-        SubmitPduBase spb;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
-                    destinationAddress, message, statusReportRequested,
-                    SmsHeader.fromByteArray(header));
-        } else {
-            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
-                    destinationAddress, message, statusReportRequested, header);
-        }
-
-        return new SubmitPdu(spb);
-    }
-
-    /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message.
-     * This method will not attempt to use any GSM national language 7 bit encodings.
-     *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
      */
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message, boolean statusReportRequested) {
@@ -603,15 +568,6 @@
     }
 
     /**
-     * Return the user data header (UDH).
-     *
-     * @hide
-     */
-    public SmsHeader getUserDataHeader() {
-        return mWrappedSmsMessage.getUserDataHeader();
-    }
-
-    /**
      * Returns the raw PDU for the message.
      *
      * @return the raw PDU for the message.
@@ -646,7 +602,6 @@
      *         SmsManager.STATUS_ON_ICC_UNSENT
      */
     public int getStatusOnIcc() {
-
         return mWrappedSmsMessage.getStatusOnIcc();
     }
 
@@ -666,7 +621,6 @@
      *         SmsMessage was not created from a ICC SMS EF record.
      */
     public int getIndexOnIcc() {
-
         return mWrappedSmsMessage.getIndexOnIcc();
     }
 
@@ -704,19 +658,4 @@
     public boolean isReplyPathPresent() {
         return mWrappedSmsMessage.isReplyPathPresent();
     }
-
-    /** This method returns the reference to a specific
-     *  SmsMessage object, which is used for accessing its static methods.
-     * @return Specific SmsMessage.
-     *
-     * @hide
-     */
-    private static final SmsMessageBase getSmsFacility(){
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-        if (PHONE_TYPE_CDMA == activePhone) {
-            return new com.android.internal.telephony.cdma.SmsMessage();
-        } else {
-            return new com.android.internal.telephony.gsm.SmsMessage();
-        }
-    }
 }
diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java
index 4af99a6..8d86ec2 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -166,104 +166,6 @@
     }
 
     /**
-     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
-     * +CMT unsolicited response (PDU mode, of course)
-     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
-     *
-     * Only public for debugging and for RIL
-     * @deprecated Use android.telephony.SmsMessage.
-     * {@hide}
-     */
-    @Deprecated
-    public static SmsMessage newFromCMT(String[] lines){
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCMT(lines);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines);
-        }
-
-        return new SmsMessage(wrappedMessage);
-    }
-
-    /** @deprecated Use android.telephony.SmsMessage.
-     *  @hide */
-    @Deprecated
-    protected static SmsMessage newFromCMTI(String line) {
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCMTI(line);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMTI(line);
-        }
-
-        return new SmsMessage(wrappedMessage);
-    }
-
-    /** @deprecated Use android.telephony.SmsMessage.
-     *  @hide */
-    @Deprecated
-    public static SmsMessage newFromCDS(String line) {
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCDS(line);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCDS(line);
-        }
-
-        return new SmsMessage(wrappedMessage);
-    }
-
-    /** @deprecated Use android.telephony.SmsMessage.
-     *  @hide */
-    @Deprecated
-    public static SmsMessage newFromParcel(Parcel p) {
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromParcel(p);
-        }
-
-        return new SmsMessage(wrappedMessage);
-    }
-
-    /**
-     * Create an SmsMessage from an SMS EF record.
-     *
-     * @param index Index of SMS record. This should be index in ArrayList
-     *              returned by SmsManager.getAllMessagesFromSim + 1.
-     * @param data Record data.
-     * @return An SmsMessage representing the record.
-     *
-     * @deprecated Use android.telephony.SmsMessage.
-     * @hide
-     */
-    @Deprecated
-    public static SmsMessage createFromEfRecord(int index, byte[] data) {
-        SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
-                    index, data);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
-                    index, data);
-        }
-
-        return new SmsMessage(wrappedMessage);
-    }
-
-    /**
      * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
      * length in bytes (not hex chars) less the SMSC header
      * @deprecated Use android.telephony.SmsMessage.
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index f0d2fba..f111dd6 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -79,7 +79,8 @@
     protected RegistrantList mRilConnectedRegistrants = new RegistrantList();
     protected RegistrantList mIccRefreshRegistrants = new RegistrantList();
 
-    protected Registrant mSMSRegistrant;
+    protected Registrant mGsmSmsRegistrant;
+    protected Registrant mCdmaSmsRegistrant;
     protected Registrant mNITZTimeRegistrant;
     protected Registrant mSignalStrengthRegistrant;
     protected Registrant mUSSDRegistrant;
@@ -358,12 +359,20 @@
         mIccStatusChangedRegistrants.remove(h);
     }
 
-    public void setOnNewSMS(Handler h, int what, Object obj) {
-        mSMSRegistrant = new Registrant (h, what, obj);
+    public void setOnNewGsmSms(Handler h, int what, Object obj) {
+        mGsmSmsRegistrant = new Registrant (h, what, obj);
     }
 
-    public void unSetOnNewSMS(Handler h) {
-        mSMSRegistrant.clear();
+    public void unSetOnNewGsmSms(Handler h) {
+        mGsmSmsRegistrant.clear();
+    }
+
+    public void setOnNewCdmaSms(Handler h, int what, Object obj) {
+        mCdmaSmsRegistrant = new Registrant (h, what, obj);
+    }
+
+    public void unSetOnNewCdmaSms(Handler h) {
+        mCdmaSmsRegistrant.clear();
     }
 
     public void setOnNewGsmBroadcastSms(Handler h, int what, Object obj) {
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 1caea70..33eed38 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -20,8 +20,6 @@
 
 import android.os.Message;
 import android.os.Handler;
-import android.os.SystemProperties;
-
 
 /**
  * {@hide}
@@ -267,14 +265,32 @@
     void unregisterForRUIMReady(Handler h);
 
     /**
-     * unlike the register* methods, there's only one new SMS handler
+     * unlike the register* methods, there's only one new 3GPP format SMS handler.
      * if you need to unregister, you should also tell the radio to stop
      * sending SMS's to you (via AT+CNMI)
      *
      * AsyncResult.result is a String containing the SMS PDU
      */
-    void setOnNewSMS(Handler h, int what, Object obj);
-    void unSetOnNewSMS(Handler h);
+    void setOnNewGsmSms(Handler h, int what, Object obj);
+    void unSetOnNewGsmSms(Handler h);
+
+    /**
+     * unlike the register* methods, there's only one new 3GPP2 format SMS handler.
+     * if you need to unregister, you should also tell the radio to stop
+     * sending SMS's to you (via AT+CNMI)
+     *
+     * AsyncResult.result is a String containing the SMS PDU
+     */
+    void setOnNewCdmaSms(Handler h, int what, Object obj);
+    void unSetOnNewCdmaSms(Handler h);
+
+    /**
+     * Set the handler for SMS Cell Broadcast messages.
+     *
+     * AsyncResult.result is a byte array containing the SMS-CB PDU
+     */
+    void setOnNewGsmBroadcastSms(Handler h, int what, Object obj);
+    void unSetOnNewGsmBroadcastSms(Handler h);
 
    /**
      * Register for NEW_SMS_ON_SIM unsolicited message
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 444f0d2..ca04eb2 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -1394,7 +1394,7 @@
     String getDeviceSvn();
 
     /**
-     * Retrieves the unique sbuscriber ID, e.g., IMSI for GSM phones.
+     * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
      */
     String getSubscriberId();
 
@@ -1756,4 +1756,13 @@
      * @param response a callback message with the String response in the obj field
      */
     void requestIsimAuthentication(String nonce, Message response);
+
+    /**
+     * Sets the SIM voice message waiting indicator records.
+     * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
+     * @param countWaiting The number of messages waiting, if known. Use
+     *                     -1 to indicate that an unknown number of
+     *                      messages are waiting
+     */
+    void setVoiceMessageWaiting(int line, int countWaiting);
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 82f3955..a7a4908 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -112,15 +112,17 @@
     /* Instance Variables */
     public CommandsInterface mCM;
     protected IccFileHandler mIccFileHandler;
-    boolean mDnsCheckDisabled = false;
+    boolean mDnsCheckDisabled;
     public DataConnectionTracker mDataConnectionTracker;
     boolean mDoesRilSendMultipleCallRing;
-    int mCallRingContinueToken = 0;
+    int mCallRingContinueToken;
     int mCallRingDelay;
     public boolean mIsTheCurrentActivePhone = true;
     boolean mIsVoiceCapable = true;
     public IccRecords mIccRecords;
     public IccCard mIccCard;
+    public SmsStorageMonitor mSmsStorageMonitor;
+    public SmsUsageMonitor mSmsUsageMonitor;
     public SMSDispatcher mSMS;
 
     /**
@@ -164,7 +166,7 @@
 
     protected Looper mLooper; /* to insure registrants are in correct thread*/
 
-    protected Context mContext;
+    protected final Context mContext;
 
     /**
      * PhoneNotifier is an abstraction for all system-wide
@@ -238,6 +240,10 @@
         mCallRingDelay = SystemProperties.getInt(
                 TelephonyProperties.PROPERTY_CALL_RING_DELAY, 3000);
         Log.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
+
+        // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
+        mSmsStorageMonitor = new SmsStorageMonitor(this);
+        mSmsUsageMonitor = new SmsUsageMonitor(context.getContentResolver());
     }
 
     public void dispose() {
@@ -246,9 +252,17 @@
             // Must cleanup all connectionS and needs to use sendMessage!
             mDataConnectionTracker.cleanUpAllConnections(null);
             mIsTheCurrentActivePhone = false;
+            // Dispose the SMS usage and storage monitors
+            mSmsStorageMonitor.dispose();
+            mSmsUsageMonitor.dispose();
         }
     }
 
+    public void removeReferences() {
+        mSmsStorageMonitor = null;
+        mSmsUsageMonitor = null;
+    }
+
     /**
      * When overridden the derived class needs to call
      * super.handleMessage(msg) so this method has a
@@ -1037,37 +1051,6 @@
     }
 
     /**
-     * simulateDataConnection
-     *
-     * simulates various data connection states. This messes with
-     * DataConnectionTracker's internal states, but doesn't actually change
-     * the underlying radio connection states.
-     *
-     * @param state Phone.DataState enum.
-     */
-    public void simulateDataConnection(Phone.DataState state) {
-        DataConnectionTracker.State dcState;
-
-        switch (state) {
-            case CONNECTED:
-                dcState = DataConnectionTracker.State.CONNECTED;
-                break;
-            case SUSPENDED:
-                dcState = DataConnectionTracker.State.CONNECTED;
-                break;
-            case DISCONNECTED:
-                dcState = DataConnectionTracker.State.FAILED;
-                break;
-            default:
-                dcState = DataConnectionTracker.State.CONNECTING;
-                break;
-        }
-
-        mDataConnectionTracker.setState(dcState);
-        notifyDataConnection(null, Phone.APN_TYPE_DEFAULT);
-    }
-
-    /**
      * Notify registrants of a new ringing Connection.
      * Subclasses of Phone probably want to replace this with a
      * version scoped to their packages
@@ -1132,7 +1115,7 @@
     /**
      * Common error logger method for unexpected calls to CDMA-only methods.
      */
-    private void logUnexpectedCdmaMethodCall(String name)
+    private static void logUnexpectedCdmaMethodCall(String name)
     {
         Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
                 "called, CDMAPhone inactive.");
@@ -1145,7 +1128,7 @@
     /**
      * Common error logger method for unexpected calls to GSM/WCDMA-only methods.
      */
-    private void logUnexpectedGsmMethodCall(String name) {
+    private static void logUnexpectedGsmMethodCall(String name) {
         Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " +
                 "called, GSMPhone inactive.");
     }
@@ -1167,4 +1150,16 @@
     public int getLteOnCdmaMode() {
         return mCM.getLteOnCdmaMode();
     }
+
+    /**
+     * Sets the SIM voice message waiting indicator records.
+     * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
+     * @param countWaiting The number of messages waiting, if known. Use
+     *                     -1 to indicate that an unknown number of
+     *                      messages are waiting
+     */
+    @Override
+    public void setVoiceMessageWaiting(int line, int countWaiting) {
+        mIccRecords.setVoiceMessageWaiting(line, countWaiting);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index e0e8d49..b497ec8 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -72,7 +72,7 @@
         switch(msg.what) {
         case EVENT_RADIO_TECHNOLOGY_CHANGED:
             //switch Phone from CDMA to GSM or vice versa
-            mOutgoingPhone = ((PhoneBase)mActivePhone).getPhoneName();
+            mOutgoingPhone = mActivePhone.getPhoneName();
             logd("Switching phone from " + mOutgoingPhone + "Phone to " +
                     (mOutgoingPhone.equals("GSM") ? "CDMAPhone" : "GSMPhone") );
             boolean oldPowerState = false; // old power state to off
@@ -144,23 +144,10 @@
         super.handleMessage(msg);
     }
 
-    private void logv(String msg) {
-        Log.v(LOG_TAG, "[PhoneProxy] " + msg);
-    }
-
-    private void logd(String msg) {
+    private static void logd(String msg) {
         Log.d(LOG_TAG, "[PhoneProxy] " + msg);
     }
 
-    private void logw(String msg) {
-        Log.w(LOG_TAG, "[PhoneProxy] " + msg);
-    }
-
-    private void loge(String msg) {
-        Log.e(LOG_TAG, "[PhoneProxy] " + msg);
-    }
-
-
     public ServiceState getServiceState() {
         return mActivePhone.getServiceState();
     }
@@ -739,19 +726,19 @@
     }
 
     public int getCdmaEriIconIndex() {
-         return mActivePhone.getCdmaEriIconIndex();
+        return mActivePhone.getCdmaEriIconIndex();
     }
 
-     public String getCdmaEriText() {
-         return mActivePhone.getCdmaEriText();
-     }
+    public String getCdmaEriText() {
+        return mActivePhone.getCdmaEriText();
+    }
 
     public int getCdmaEriIconMode() {
-         return mActivePhone.getCdmaEriIconMode();
+        return mActivePhone.getCdmaEriIconMode();
     }
 
     public Phone getActivePhone() {
-         return mActivePhone;
+        return mActivePhone;
     }
 
     public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete){
@@ -861,4 +848,9 @@
     public int getLteOnCdmaMode() {
         return mActivePhone.getLteOnCdmaMode();
     }
+
+    @Override
+    public void setVoiceMessageWaiting(int line, int countWaiting) {
+        mActivePhone.setVoiceMessageWaiting(line, countWaiting);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index bd35058..8aae0d4 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -2434,8 +2434,8 @@
                 SmsMessage sms;
 
                 sms = SmsMessage.newFromCMT(a);
-                if (mSMSRegistrant != null) {
-                    mSMSRegistrant
+                if (mGsmSmsRegistrant != null) {
+                    mGsmSmsRegistrant
                         .notifyRegistrant(new AsyncResult(null, sms, null));
                 }
             break;
@@ -2607,8 +2607,8 @@
 
                 SmsMessage sms = (SmsMessage) ret;
 
-                if (mSMSRegistrant != null) {
-                    mSMSRegistrant
+                if (mCdmaSmsRegistrant != null) {
+                    mCdmaSmsRegistrant
                         .notifyRegistrant(new AsyncResult(null, sms, null));
                 }
                 break;
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 76e719c..e4c6028 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -44,10 +44,12 @@
 import android.util.Log;
 import android.view.WindowManager;
 
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
 import com.android.internal.util.HexDump;
 
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Random;
 
@@ -60,68 +62,66 @@
 import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
 import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
 
-
 public abstract class SMSDispatcher extends Handler {
-    private static final String TAG = "SMS";
+    static final String TAG = "SMS";    // accessed from inner class
     private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
 
-    /** Default checking period for SMS sent without user permit */
-    private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000;
-
-    /** Default number of SMS sent in checking period without user permit */
-    private static final int DEFAULT_SMS_MAX_COUNT = 100;
-
     /** Default timeout for SMS sent query */
     private static final int DEFAULT_SMS_TIMEOUT = 6000;
 
-    protected static final String[] RAW_PROJECTION = new String[] {
-        "pdu",
-        "sequence",
-        "destination_port",
+    /** Permission required to receive SMS and SMS-CB messages. */
+    public static final String RECEIVE_SMS_PERMISSION = "android.permission.RECEIVE_SMS";
+
+    /** Permission required to receive ETWS and CMAS emergency broadcasts. */
+    public static final String RECEIVE_EMERGENCY_BROADCAST_PERMISSION =
+            "android.permission.RECEIVE_EMERGENCY_BROADCAST";
+
+    /** Query projection for checking for duplicate message segments. */
+    private static final String[] PDU_PROJECTION = new String[] {
+            "pdu"
     };
 
-    static final protected int EVENT_NEW_SMS = 1;
+    /** Query projection for combining concatenated message segments. */
+    private static final String[] PDU_SEQUENCE_PORT_PROJECTION = new String[] {
+            "pdu",
+            "sequence",
+            "destination_port"
+    };
 
-    static final protected int EVENT_SEND_SMS_COMPLETE = 2;
+    private static final int PDU_COLUMN = 0;
+    private static final int SEQUENCE_COLUMN = 1;
+    private static final int DESTINATION_PORT_COLUMN = 2;
+
+    /** New SMS received. */
+    protected static final int EVENT_NEW_SMS = 1;
+
+    /** SMS send complete. */
+    protected static final int EVENT_SEND_SMS_COMPLETE = 2;
 
     /** Retry sending a previously failed SMS message */
-    static final protected int EVENT_SEND_RETRY = 3;
-
-    /** Status report received */
-    static final protected int EVENT_NEW_SMS_STATUS_REPORT = 5;
-
-    /** SIM/RUIM storage is full */
-    static final protected int EVENT_ICC_FULL = 6;
+    private static final int EVENT_SEND_RETRY = 3;
 
     /** SMS confirm required */
-    static final protected int EVENT_POST_ALERT = 7;
+    private static final int EVENT_POST_ALERT = 4;
 
     /** Send the user confirmed SMS */
-    static final protected int EVENT_SEND_CONFIRMED_SMS = 8;
+    static final int EVENT_SEND_CONFIRMED_SMS = 5;  // accessed from inner class
 
     /** Alert is timeout */
-    static final protected int EVENT_ALERT_TIMEOUT = 9;
+    private static final int EVENT_ALERT_TIMEOUT = 6;
 
     /** Stop the sending */
-    static final protected int EVENT_STOP_SENDING = 10;
+    static final int EVENT_STOP_SENDING = 7;        // accessed from inner class
 
-    /** Memory status reporting is acknowledged by RIL */
-    static final protected int EVENT_REPORT_MEMORY_STATUS_DONE = 11;
-
-    /** Radio is ON */
-    static final protected int EVENT_RADIO_ON = 12;
-
-    /** New broadcast SMS */
-    static final protected int EVENT_NEW_BROADCAST_SMS = 13;
-
-    protected Phone mPhone;
-    protected Context mContext;
-    protected ContentResolver mResolver;
-    protected CommandsInterface mCm;
+    protected final Phone mPhone;
+    protected final Context mContext;
+    protected final ContentResolver mResolver;
+    protected final CommandsInterface mCm;
+    protected final SmsStorageMonitor mStorageMonitor;
 
     protected final WapPushOverSms mWapPush;
 
-    protected final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
+    protected static final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
 
     /** Maximum number of times to retry sending a failed SMS. */
     private static final int MAX_SEND_RETRIES = 3;
@@ -136,12 +136,14 @@
      * Message reference for a CONCATENATED_8_BIT_REFERENCE or
      * CONCATENATED_16_BIT_REFERENCE message set.  Should be
      * incremented for each set of concatenated messages.
+     * Static field shared by all dispatcher objects.
      */
-    private static int sConcatenatedRef;
+    private static int sConcatenatedRef = new Random().nextInt(256);
 
-    private SmsCounter mCounter;
+    /** Outgoing message counter. Shared by all dispatchers. */
+    private final SmsUsageMonitor mUsageMonitor;
 
-    private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
+    private final ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
 
     /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
     private PowerManager.WakeLock mWakeLock;
@@ -150,17 +152,14 @@
      * Hold the wake lock for 5 seconds, which should be enough time for
      * any receiver(s) to grab its own wake lock.
      */
-    private final int WAKE_LOCK_TIMEOUT = 5000;
-
-    protected boolean mStorageAvailable = true;
-    protected boolean mReportMemoryStatusPending = false;
+    private static final int WAKE_LOCK_TIMEOUT = 5000;
 
     /* Flags indicating whether the current device allows sms service */
     protected boolean mSmsCapable = true;
     protected boolean mSmsReceiveDisabled;
     protected boolean mSmsSendDisabled;
 
-    protected static int mRemainingMessages = -1;
+    protected int mRemainingMessages = -1;
 
     protected static int getNextConcatenatedRef() {
         sConcatenatedRef += 1;
@@ -168,111 +167,52 @@
     }
 
     /**
-     *  Implement the per-application based SMS control, which only allows
-     *  a limit on the number of SMS/MMS messages an app can send in checking
-     *  period.
+     * Create a new SMS dispatcher.
+     * @param phone the Phone to use
+     * @param storageMonitor the SmsStorageMonitor to use
+     * @param usageMonitor the SmsUsageMonitor to use
      */
-    private class SmsCounter {
-        private int mCheckPeriod;
-        private int mMaxAllowed;
-        private HashMap<String, ArrayList<Long>> mSmsStamp;
-
-        /**
-         * Create SmsCounter
-         * @param mMax is the number of SMS allowed without user permit
-         * @param mPeriod is the checking period
-         */
-        SmsCounter(int mMax, int mPeriod) {
-            mMaxAllowed = mMax;
-            mCheckPeriod = mPeriod;
-            mSmsStamp = new HashMap<String, ArrayList<Long>> ();
-        }
-
-        /**
-         * Check to see if an application allow to send new SMS messages
-         *
-         * @param appName is the application sending sms
-         * @param smsWaiting is the number of new sms wants to be sent
-         * @return true if application is allowed to send the requested number
-         *         of new sms messages
-         */
-        boolean check(String appName, int smsWaiting) {
-            if (!mSmsStamp.containsKey(appName)) {
-                mSmsStamp.put(appName, new ArrayList<Long>());
-            }
-
-            return isUnderLimit(mSmsStamp.get(appName), smsWaiting);
-        }
-
-        private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) {
-            Long ct =  System.currentTimeMillis();
-
-            Log.d(TAG, "SMS send size=" + sent.size() + "time=" + ct);
-
-            while (sent.size() > 0 && (ct - sent.get(0)) > mCheckPeriod ) {
-                    sent.remove(0);
-            }
-
-
-            if ( (sent.size() + smsWaiting) <= mMaxAllowed) {
-                for (int i = 0; i < smsWaiting; i++ ) {
-                    sent.add(ct);
-                }
-                return true;
-            }
-            return false;
-        }
-    }
-
-    protected SMSDispatcher(PhoneBase phone) {
+    protected SMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
+            SmsUsageMonitor usageMonitor) {
         mPhone = phone;
         mWapPush = new WapPushOverSms(phone, this);
         mContext = phone.getContext();
         mResolver = mContext.getContentResolver();
         mCm = phone.mCM;
+        mStorageMonitor = storageMonitor;
+        mUsageMonitor = usageMonitor;
 
         createWakelock();
 
-        int check_period = Settings.Secure.getInt(mResolver,
-                Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS,
-                DEFAULT_SMS_CHECK_PERIOD);
-        int max_count = Settings.Secure.getInt(mResolver,
-                Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT,
-                DEFAULT_SMS_MAX_COUNT);
-        mCounter = new SmsCounter(max_count, check_period);
-
-        mCm.setOnNewSMS(this, EVENT_NEW_SMS, null);
-        mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
-        mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);
-        mCm.registerForOn(this, EVENT_RADIO_ON, null);
-
-        // Don't always start message ref at 0.
-        sConcatenatedRef = new Random().nextInt(256);
-
-        // Register for device storage intents.  Use these to notify the RIL
-        // that storage for SMS is or is not available.
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL);
-        filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
-        mContext.registerReceiver(mResultReceiver, filter);
-
         mSmsCapable = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_sms_capable);
         mSmsReceiveDisabled = !SystemProperties.getBoolean(
                                 TelephonyProperties.PROPERTY_SMS_RECEIVE, mSmsCapable);
         mSmsSendDisabled = !SystemProperties.getBoolean(
                                 TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable);
-        Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable
+        Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat()
                 + " mSmsReceiveDisabled=" + mSmsReceiveDisabled
                 + " mSmsSendDisabled=" + mSmsSendDisabled);
     }
 
-    public void dispose() {
-        mCm.unSetOnNewSMS(this);
-        mCm.unSetOnSmsStatus(this);
-        mCm.unSetOnIccSmsFull(this);
-        mCm.unregisterForOn(this);
-    }
+    /** Unregister for incoming SMS events. */
+    public abstract void dispose();
+
+    /**
+     * The format of the message PDU in the associated broadcast intent.
+     * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
+     * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
+     *
+     * Note: All applications which handle incoming SMS messages by processing the
+     * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent
+     * into the new methods in {@link android.telephony.SmsMessage} which take an
+     * extra format parameter. This is required in order to correctly decode the PDU on
+     * devices which require support for both 3GPP and 3GPP2 formats at the same time,
+     * such as CDMA/LTE devices and GSM/CDMA world phones.
+     *
+     * @return the format of the message PDU
+     */
+    protected abstract String getFormat();
 
     @Override
     protected void finalize() {
@@ -338,14 +278,6 @@
             sendSms((SmsTracker) msg.obj);
             break;
 
-        case EVENT_NEW_SMS_STATUS_REPORT:
-            handleStatusReport((AsyncResult)msg.obj);
-            break;
-
-        case EVENT_ICC_FULL:
-            handleIccFull();
-            break;
-
         case EVENT_POST_ALERT:
             handleReachSentLimit((SmsTracker)(msg.obj));
             break;
@@ -369,7 +301,7 @@
         case EVENT_SEND_CONFIRMED_SMS:
             if (mSTrackers.isEmpty() == false) {
                 SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
-                if (isMultipartTracker(sTracker)) {
+                if (sTracker.isMultipart()) {
                     sendMultipartSms(sTracker);
                 } else {
                     sendSms(sTracker);
@@ -390,30 +322,6 @@
                 removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
             }
             break;
-
-        case EVENT_REPORT_MEMORY_STATUS_DONE:
-            ar = (AsyncResult)msg.obj;
-            if (ar.exception != null) {
-                mReportMemoryStatusPending = true;
-                Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
-                        + mStorageAvailable);
-            } else {
-                mReportMemoryStatusPending = false;
-            }
-            break;
-
-        case EVENT_RADIO_ON:
-            if (mReportMemoryStatusPending) {
-                Log.v(TAG, "Sending pending memory status report : mStorageAvailable = "
-                        + mStorageAvailable);
-                mCm.reportSmsMemoryStatus(mStorageAvailable,
-                        obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
-            }
-            break;
-
-        case EVENT_NEW_BROADCAST_SMS:
-            handleBroadcastSms((AsyncResult)msg.obj);
-            break;
         }
     }
 
@@ -440,26 +348,6 @@
     }
 
     /**
-     * Called when SIM_FULL message is received from the RIL.  Notifies interested
-     * parties that SIM storage for SMS messages is full.
-     */
-    private void handleIccFull(){
-        // broadcast SIM_FULL intent
-        Intent intent = new Intent(Intents.SIM_FULL_ACTION);
-        mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
-        mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS");
-    }
-
-    /**
-     * Called when a status report is received.  This should correspond to
-     * a previously successful SEND.
-     *
-     * @param ar AsyncResult passed into the message handler.  ar.result should
-     *           be a String representing the status report PDU, as ASCII hex.
-     */
-    protected abstract void handleStatusReport(AsyncResult ar);
-
-    /**
      * Called when SMS send completes. Broadcasts a sentIntent on success.
      * On failure, either sets up retries or broadcasts a sentIntent with
      * the failure in the result code.
@@ -559,7 +447,7 @@
      *                  POWER_OFF
      * @param tracker   An SmsTracker for the current message.
      */
-    protected void handleNotInService(int ss, SmsTracker tracker) {
+    protected static void handleNotInService(int ss, SmsTracker tracker) {
         if (tracker.mSentIntent != null) {
             try {
                 if (ss == ServiceState.STATE_POWER_OFF) {
@@ -581,86 +469,171 @@
      */
     public abstract int dispatchMessage(SmsMessageBase sms);
 
+    /**
+     * Dispatch a normal incoming SMS. This is called from the format-specific
+     * {@link #dispatchMessage(SmsMessageBase)} if no format-specific handling is required.
+     *
+     * @param sms
+     * @return
+     */
+    protected int dispatchNormalMessage(SmsMessageBase sms) {
+        SmsHeader smsHeader = sms.getUserDataHeader();
+
+        // See if message is partial or port addressed.
+        if ((smsHeader == null) || (smsHeader.concatRef == null)) {
+            // Message is not partial (not part of concatenated sequence).
+            byte[][] pdus = new byte[1][];
+            pdus[0] = sms.getPdu();
+
+            if (smsHeader != null && smsHeader.portAddrs != null) {
+                if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
+                    // GSM-style WAP indication
+                    return mWapPush.dispatchWapPdu(sms.getUserData());
+                } else {
+                    // The message was sent to a port, so concoct a URI for it.
+                    dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
+                }
+            } else {
+                // Normal short and non-port-addressed message, dispatch it.
+                dispatchPdus(pdus);
+            }
+            return Activity.RESULT_OK;
+        } else {
+            // Process the message part.
+            SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
+            SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs;
+            return processMessagePart(sms.getPdu(), sms.getOriginatingAddress(),
+                    concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount,
+                    sms.getTimestampMillis(), (portAddrs != null ? portAddrs.destPort : -1), false);
+        }
+    }
 
     /**
      * If this is the last part send the parts out to the application, otherwise
-     * the part is stored for later processing.
+     * the part is stored for later processing. Handles both 3GPP concatenated messages
+     * as well as 3GPP2 format WAP push messages processed by
+     * {@link com.android.internal.telephony.cdma.CdmaSMSDispatcher#processCdmaWapPdu}.
      *
-     * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
+     * @param pdu the message PDU, or the datagram portion of a CDMA WDP datagram segment
+     * @param address the originating address
+     * @param referenceNumber distinguishes concatenated messages from the same sender
+     * @param sequenceNumber the order of this segment in the message
+     * @param messageCount the number of segments in the message
+     * @param timestamp the service center timestamp in millis
+     * @param destPort the destination port for the message, or -1 for no destination port
+     * @param isCdmaWapPush true if pdu is a CDMA WDP datagram segment and not an SM PDU
+     *
      * @return a result code from {@link Telephony.Sms.Intents}, or
      *         {@link Activity#RESULT_OK} if the message has been broadcast
      *         to applications
      */
-    protected int processMessagePart(SmsMessageBase sms,
-            SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) {
-
-        // Lookup all other related parts
-        StringBuilder where = new StringBuilder("reference_number =");
-        where.append(concatRef.refNumber);
-        where.append(" AND address = ?");
-        String[] whereArgs = new String[] {sms.getOriginatingAddress()};
-
+    protected int processMessagePart(byte[] pdu, String address, int referenceNumber,
+            int sequenceNumber, int messageCount, long timestamp, int destPort,
+            boolean isCdmaWapPush) {
         byte[][] pdus = null;
         Cursor cursor = null;
         try {
-            cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null);
+            // used by several query selection arguments
+            String refNumber = Integer.toString(referenceNumber);
+            String seqNumber = Integer.toString(sequenceNumber);
+
+            // Check for duplicate message segment
+            cursor = mResolver.query(mRawUri, PDU_PROJECTION,
+                    "address=? AND reference_number=? AND sequence=?",
+                    new String[] {address, refNumber, seqNumber}, null);
+
+            // moveToNext() returns false if no duplicates were found
+            if (cursor.moveToNext()) {
+                Log.w(TAG, "Discarding duplicate message segment from address=" + address
+                        + " refNumber=" + refNumber + " seqNumber=" + seqNumber);
+                String oldPduString = cursor.getString(PDU_COLUMN);
+                byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
+                if (!Arrays.equals(oldPdu, pdu)) {
+                    Log.e(TAG, "Warning: dup message segment PDU of length " + pdu.length
+                            + " is different from existing PDU of length " + oldPdu.length);
+                }
+                return Intents.RESULT_SMS_HANDLED;
+            }
+            cursor.close();
+
+            // not a dup, query for all other segments of this concatenated message
+            String where = "address=? AND reference_number=?";
+            String[] whereArgs = new String[] {address, refNumber};
+            cursor = mResolver.query(mRawUri, PDU_SEQUENCE_PORT_PROJECTION, where, whereArgs, null);
+
             int cursorCount = cursor.getCount();
-            if (cursorCount != concatRef.msgCount - 1) {
+            if (cursorCount != messageCount - 1) {
                 // We don't have all the parts yet, store this one away
                 ContentValues values = new ContentValues();
-                values.put("date", new Long(sms.getTimestampMillis()));
-                values.put("pdu", HexDump.toHexString(sms.getPdu()));
-                values.put("address", sms.getOriginatingAddress());
-                values.put("reference_number", concatRef.refNumber);
-                values.put("count", concatRef.msgCount);
-                values.put("sequence", concatRef.seqNumber);
-                if (portAddrs != null) {
-                    values.put("destination_port", portAddrs.destPort);
+                values.put("date", timestamp);
+                values.put("pdu", HexDump.toHexString(pdu));
+                values.put("address", address);
+                values.put("reference_number", referenceNumber);
+                values.put("count", messageCount);
+                values.put("sequence", sequenceNumber);
+                if (destPort != -1) {
+                    values.put("destination_port", destPort);
                 }
                 mResolver.insert(mRawUri, values);
                 return Intents.RESULT_SMS_HANDLED;
             }
 
             // All the parts are in place, deal with them
-            int pduColumn = cursor.getColumnIndex("pdu");
-            int sequenceColumn = cursor.getColumnIndex("sequence");
-
-            pdus = new byte[concatRef.msgCount][];
+            pdus = new byte[messageCount][];
             for (int i = 0; i < cursorCount; i++) {
                 cursor.moveToNext();
-                int cursorSequence = (int)cursor.getLong(sequenceColumn);
+                int cursorSequence = cursor.getInt(SEQUENCE_COLUMN);
                 pdus[cursorSequence - 1] = HexDump.hexStringToByteArray(
-                        cursor.getString(pduColumn));
+                        cursor.getString(PDU_COLUMN));
+
+                // Read the destination port from the first segment (needed for CDMA WAP PDU).
+                // It's not a bad idea to prefer the port from the first segment for 3GPP as well.
+                if (cursorSequence == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
+                    destPort = cursor.getInt(DESTINATION_PORT_COLUMN);
+                }
             }
             // This one isn't in the DB, so add it
-            pdus[concatRef.seqNumber - 1] = sms.getPdu();
+            pdus[sequenceNumber - 1] = pdu;
 
             // Remove the parts from the database
-            mResolver.delete(mRawUri, where.toString(), whereArgs);
+            mResolver.delete(mRawUri, where, whereArgs);
         } catch (SQLException e) {
             Log.e(TAG, "Can't access multipart SMS database", e);
-            // TODO:  Would OUT_OF_MEMORY be more appropriate?
             return Intents.RESULT_SMS_GENERIC_ERROR;
         } finally {
             if (cursor != null) cursor.close();
         }
 
-        /**
-         * TODO(cleanup): The following code has duplicated logic with
-         * the radio-specific dispatchMessage code, which is fragile,
-         * in addition to being redundant.  Instead, if this method
-         * maybe returned the reassembled message (or just contents),
-         * the following code (which is not really related to
-         * reconstruction) could be better consolidated.
-         */
+        // Special handling for CDMA WDP datagrams
+        if (isCdmaWapPush) {
+            // Build up the data stream
+            ByteArrayOutputStream output = new ByteArrayOutputStream();
+            for (int i = 0; i < messageCount; i++) {
+                // reassemble the (WSP-)pdu
+                output.write(pdus[i], 0, pdus[i].length);
+            }
+            byte[] datagram = output.toByteArray();
+
+            // Dispatch the PDU to applications
+            if (destPort == SmsHeader.PORT_WAP_PUSH) {
+                // Handle the PUSH
+                return mWapPush.dispatchWapPdu(datagram);
+            } else {
+                pdus = new byte[1][];
+                pdus[0] = datagram;
+                // The messages were sent to any other WAP port
+                dispatchPortAddressedPdus(pdus, destPort);
+                return Activity.RESULT_OK;
+            }
+        }
 
         // Dispatch the PDUs to applications
-        if (portAddrs != null) {
-            if (portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
+        if (destPort != -1) {
+            if (destPort == SmsHeader.PORT_WAP_PUSH) {
                 // Build up the data stream
                 ByteArrayOutputStream output = new ByteArrayOutputStream();
-                for (int i = 0; i < concatRef.msgCount; i++) {
-                    SmsMessage msg = SmsMessage.createFromPdu(pdus[i]);
+                for (int i = 0; i < messageCount; i++) {
+                    SmsMessage msg = SmsMessage.createFromPdu(pdus[i], getFormat());
                     byte[] data = msg.getUserData();
                     output.write(data, 0, data.length);
                 }
@@ -668,7 +641,7 @@
                 return mWapPush.dispatchWapPdu(output.toByteArray());
             } else {
                 // The messages were sent to a port, so concoct a URI for it
-                dispatchPortAddressedPdus(pdus, portAddrs.destPort);
+                dispatchPortAddressedPdus(pdus, destPort);
             }
         } else {
             // The messages were not sent to a port
@@ -685,7 +658,8 @@
     protected void dispatchPdus(byte[][] pdus) {
         Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
         intent.putExtra("pdus", pdus);
-        dispatch(intent, "android.permission.RECEIVE_SMS");
+        intent.putExtra("format", getFormat());
+        dispatch(intent, RECEIVE_SMS_PERMISSION);
     }
 
     /**
@@ -698,7 +672,8 @@
         Uri uri = Uri.parse("sms://localhost:" + port);
         Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri);
         intent.putExtra("pdus", pdus);
-        dispatch(intent, "android.permission.RECEIVE_SMS");
+        intent.putExtra("format", getFormat());
+        dispatch(intent, RECEIVE_SMS_PERMISSION);
     }
 
     /**
@@ -759,6 +734,16 @@
             String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
 
     /**
+     * Calculate the number of septets needed to encode the message.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly ignore (but still count) illegal characters if true
+     * @return TextEncodingDetails
+     */
+    protected abstract TextEncodingDetails calculateLength(CharSequence messageBody,
+            boolean use7bitOnly);
+
+    /**
      * Send a multi-part text based SMS.
      *
      * @param destAddr the address to send the message to
@@ -784,9 +769,70 @@
      *   to the recipient.  The raw pdu of the status report is in the
      *   extended data ("pdu").
      */
-    protected abstract void sendMultipartText(String destAddr, String scAddr,
+    protected void sendMultipartText(String destAddr, String scAddr,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
-            ArrayList<PendingIntent> deliveryIntents);
+            ArrayList<PendingIntent> deliveryIntents) {
+
+        int refNumber = getNextConcatenatedRef() & 0x00FF;
+        int msgCount = parts.size();
+        int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;
+
+        mRemainingMessages = msgCount;
+
+        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
+        for (int i = 0; i < msgCount; i++) {
+            TextEncodingDetails details = calculateLength(parts.get(i), false);
+            if (encoding != details.codeUnitSize
+                    && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN
+                            || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
+                encoding = details.codeUnitSize;
+            }
+            encodingForParts[i] = details;
+        }
+
+        for (int i = 0; i < msgCount; i++) {
+            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+            concatRef.refNumber = refNumber;
+            concatRef.seqNumber = i + 1;  // 1-based sequence
+            concatRef.msgCount = msgCount;
+            // TODO: We currently set this to true since our messaging app will never
+            // send more than 255 parts (it converts the message to MMS well before that).
+            // However, we should support 3rd party messaging apps that might need 16-bit
+            // references
+            // Note:  It's not sufficient to just flip this bit to true; it will have
+            // ripple effects (several calculations assume 8-bit ref).
+            concatRef.isEightBits = true;
+            SmsHeader smsHeader = new SmsHeader();
+            smsHeader.concatRef = concatRef;
+
+            // Set the national language tables for 3GPP 7-bit encoding, if enabled.
+            if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
+                smsHeader.languageTable = encodingForParts[i].languageTable;
+                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
+            }
+
+            PendingIntent sentIntent = null;
+            if (sentIntents != null && sentIntents.size() > i) {
+                sentIntent = sentIntents.get(i);
+            }
+
+            PendingIntent deliveryIntent = null;
+            if (deliveryIntents != null && deliveryIntents.size() > i) {
+                deliveryIntent = deliveryIntents.get(i);
+            }
+
+            sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding,
+                    sentIntent, deliveryIntent, (i == (msgCount - 1)));
+        }
+
+    }
+
+    /**
+     * Create a new SubmitPdu and send it.
+     */
+    protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress,
+            String message, SmsHeader smsHeader, int encoding,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart);
 
     /**
      * Send a SMS
@@ -842,7 +888,7 @@
             handleNotInService(ss, tracker);
         } else {
             String appName = getAppNameByIntent(sentIntent);
-            if (mCounter.check(appName, SINGLE_PART_SMS)) {
+            if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {
                 sendSms(tracker);
             } else {
                 sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
@@ -885,7 +931,7 @@
                 DEFAULT_SMS_TIMEOUT);
     }
 
-    protected String getAppNameByIntent(PendingIntent intent) {
+    protected static String getAppNameByIntent(PendingIntent intent) {
         Resources r = Resources.getSystem();
         return (intent != null) ? intent.getTargetPackage()
             : r.getString(R.string.sms_control_default_app_name);
@@ -903,7 +949,35 @@
      *
      * @param tracker holds the multipart Sms tracker ready to be sent
      */
-    protected abstract void sendMultipartSms (SmsTracker tracker);
+    private void sendMultipartSms(SmsTracker tracker) {
+        ArrayList<String> parts;
+        ArrayList<PendingIntent> sentIntents;
+        ArrayList<PendingIntent> deliveryIntents;
+
+        HashMap<String, Object> map = tracker.mData;
+
+        String destinationAddress = (String) map.get("destination");
+        String scAddress = (String) map.get("scaddress");
+
+        parts = (ArrayList<String>) map.get("parts");
+        sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
+        deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
+
+        // check if in service
+        int ss = mPhone.getServiceState().getState();
+        if (ss != ServiceState.STATE_IN_SERVICE) {
+            for (int i = 0, count = parts.size(); i < count; i++) {
+                PendingIntent sentIntent = null;
+                if (sentIntents != null && sentIntents.size() > i) {
+                    sentIntent = sentIntents.get(i);
+                }
+                handleNotInService(ss, new SmsTracker(null, sentIntent, null));
+            }
+            return;
+        }
+
+        sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents);
+    }
 
     /**
      * Send an acknowledge message.
@@ -934,66 +1008,38 @@
     }
 
     /**
-     * Check if a SmsTracker holds multi-part Sms
-     *
-     * @param tracker a SmsTracker could hold a multi-part Sms
-     * @return true for tracker holds Multi-parts Sms
-     */
-    private boolean isMultipartTracker (SmsTracker tracker) {
-        HashMap map = tracker.mData;
-        return ( map.get("parts") != null);
-    }
-
-    /**
      * Keeps track of an SMS that has been sent to the RIL, until it has
      * successfully been sent, or we're done trying.
      *
      */
-    static protected class SmsTracker {
+    protected static final class SmsTracker {
         // fields need to be public for derived SmsDispatchers
-        public HashMap<String, Object> mData;
+        public final HashMap<String, Object> mData;
         public int mRetryCount;
         public int mMessageRef;
 
-        public PendingIntent mSentIntent;
-        public PendingIntent mDeliveryIntent;
+        public final PendingIntent mSentIntent;
+        public final PendingIntent mDeliveryIntent;
 
-        SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
+        public SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
                 PendingIntent deliveryIntent) {
             mData = data;
             mSentIntent = sentIntent;
             mDeliveryIntent = deliveryIntent;
             mRetryCount = 0;
         }
+
+        /**
+         * Returns whether this tracker holds a multi-part SMS.
+         * @return true if the tracker holds a multi-part SMS; false otherwise
+         */
+        protected boolean isMultipart() {
+            HashMap map = mData;
+            return map.containsKey("parts");
+        }
     }
 
-    protected SmsTracker SmsTrackerFactory(HashMap<String, Object> data, PendingIntent sentIntent,
-            PendingIntent deliveryIntent) {
-        return new SmsTracker(data, sentIntent, deliveryIntent);
-    }
-
-    public void initSipStack(boolean isObg) {
-        // This function should be overridden by the classes that support
-        // switching modes such as the CdmaSMSDispatcher.
-        // Not implemented in GsmSMSDispatcher.
-        Log.e(TAG, "Error! This function should never be executed.");
-    }
-
-    public void switchToCdma() {
-        // This function should be overridden by the classes that support
-        // switching modes such as the CdmaSMSDispatcher.
-        // Not implemented in GsmSMSDispatcher.
-        Log.e(TAG, "Error! This function should never be executed.");
-    }
-
-    public void switchToGsm() {
-        // This function should be overridden by the classes that support
-        // switching modes such as the CdmaSMSDispatcher.
-        // Not implemented in GsmSMSDispatcher.
-        Log.e(TAG, "Error! This function should never be executed.");
-    }
-
-    private DialogInterface.OnClickListener mListener =
+    private final DialogInterface.OnClickListener mListener =
         new DialogInterface.OnClickListener() {
 
             public void onClick(DialogInterface dialog, int which) {
@@ -1007,42 +1053,32 @@
             }
         };
 
-    private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) {
-                mStorageAvailable = false;
-                mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
-            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
-                mStorageAvailable = true;
-                mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
-            } else {
-                // Assume the intent is one of the SMS receive intents that
-                // was sent as an ordered broadcast.  Check result and ACK.
-                int rc = getResultCode();
-                boolean success = (rc == Activity.RESULT_OK)
-                        || (rc == Intents.RESULT_SMS_HANDLED);
+            // Assume the intent is one of the SMS receive intents that
+            // was sent as an ordered broadcast.  Check result and ACK.
+            int rc = getResultCode();
+            boolean success = (rc == Activity.RESULT_OK)
+                    || (rc == Intents.RESULT_SMS_HANDLED);
 
-                // For a multi-part message, this only ACKs the last part.
-                // Previous parts were ACK'd as they were received.
-                acknowledgeLastIncomingSms(success, rc, null);
-            }
+            // For a multi-part message, this only ACKs the last part.
+            // Previous parts were ACK'd as they were received.
+            acknowledgeLastIncomingSms(success, rc, null);
         }
     };
 
-    protected abstract void handleBroadcastSms(AsyncResult ar);
-
     protected void dispatchBroadcastPdus(byte[][] pdus, boolean isEmergencyMessage) {
         if (isEmergencyMessage) {
             Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
             intent.putExtra("pdus", pdus);
             Log.d(TAG, "Dispatching " + pdus.length + " emergency SMS CB pdus");
-            dispatch(intent, "android.permission.RECEIVE_EMERGENCY_BROADCAST");
+            dispatch(intent, RECEIVE_EMERGENCY_BROADCAST_PERMISSION);
         } else {
             Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
             intent.putExtra("pdus", pdus);
             Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
-            dispatch(intent, "android.permission.RECEIVE_SMS");
+            dispatch(intent, RECEIVE_SMS_PERMISSION);
         }
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/SmsStorageMonitor.java b/telephony/java/com/android/internal/telephony/SmsStorageMonitor.java
new file mode 100644
index 0000000..0c06ffc
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/SmsStorageMonitor.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.provider.Telephony.Sms.Intents;
+import android.util.Log;
+
+/**
+ * Monitors the device and ICC storage, and sends the appropriate events.
+ *
+ * This code was formerly part of {@link SMSDispatcher}, and has been moved
+ * into a separate class to support instantiation of multiple SMSDispatchers on
+ * dual-mode devices that require support for both 3GPP and 3GPP2 format messages.
+ */
+public final class SmsStorageMonitor extends Handler {
+    private static final String TAG = "SmsStorageMonitor";
+
+    /** SIM/RUIM storage is full */
+    private static final int EVENT_ICC_FULL = 1;
+
+    /** Memory status reporting is acknowledged by RIL */
+    private static final int EVENT_REPORT_MEMORY_STATUS_DONE = 2;
+
+    /** Radio is ON */
+    private static final int EVENT_RADIO_ON = 3;
+
+    /** Context from phone object passed to constructor. */
+    private final Context mContext;
+
+    /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
+    private PowerManager.WakeLock mWakeLock;
+
+    private boolean mReportMemoryStatusPending;
+
+    final CommandsInterface mCm;                            // accessed from inner class
+    boolean mStorageAvailable = true;                       // accessed from inner class
+
+    /**
+     * Hold the wake lock for 5 seconds, which should be enough time for
+     * any receiver(s) to grab its own wake lock.
+     */
+    private static final int WAKE_LOCK_TIMEOUT = 5000;
+
+    /**
+     * Creates an SmsStorageMonitor and registers for events.
+     * @param phone the Phone to use
+     */
+    public SmsStorageMonitor(PhoneBase phone) {
+        mContext = phone.getContext();
+        mCm = phone.mCM;
+
+        createWakelock();
+
+        mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);
+        mCm.registerForOn(this, EVENT_RADIO_ON, null);
+
+        // Register for device storage intents.  Use these to notify the RIL
+        // that storage for SMS is or is not available.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL);
+        filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
+        mContext.registerReceiver(mResultReceiver, filter);
+    }
+
+    public void dispose() {
+        mCm.unSetOnIccSmsFull(this);
+        mCm.unregisterForOn(this);
+        mContext.unregisterReceiver(mResultReceiver);
+    }
+
+    /**
+     * Handles events coming from the phone stack. Overridden from handler.
+     * @param msg the message to handle
+     */
+    @Override
+    public void handleMessage(Message msg) {
+        AsyncResult ar;
+
+        switch (msg.what) {
+            case EVENT_ICC_FULL:
+                handleIccFull();
+                break;
+
+            case EVENT_REPORT_MEMORY_STATUS_DONE:
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    mReportMemoryStatusPending = true;
+                    Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
+                            + mStorageAvailable);
+                } else {
+                    mReportMemoryStatusPending = false;
+                }
+                break;
+
+            case EVENT_RADIO_ON:
+                if (mReportMemoryStatusPending) {
+                    Log.v(TAG, "Sending pending memory status report : mStorageAvailable = "
+                            + mStorageAvailable);
+                    mCm.reportSmsMemoryStatus(mStorageAvailable,
+                            obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+                }
+                break;
+        }
+    }
+
+    private void createWakelock() {
+        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SmsStorageMonitor");
+        mWakeLock.setReferenceCounted(true);
+    }
+
+    /**
+     * Called when SIM_FULL message is received from the RIL.  Notifies interested
+     * parties that SIM storage for SMS messages is full.
+     */
+    private void handleIccFull() {
+        // broadcast SIM_FULL intent
+        Intent intent = new Intent(Intents.SIM_FULL_ACTION);
+        mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
+        mContext.sendBroadcast(intent, SMSDispatcher.RECEIVE_SMS_PERMISSION);
+    }
+
+    /** Returns whether or not there is storage available for an incoming SMS. */
+    public boolean isStorageAvailable() {
+        return mStorageAvailable;
+    }
+
+    private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) {
+                mStorageAvailable = false;
+                mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
+                mStorageAvailable = true;
+                mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
+            }
+        }
+    };
+}
diff --git a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
new file mode 100644
index 0000000..bd2ae8b
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Implement the per-application based SMS control, which limits the number of
+ * SMS/MMS messages an app can send in the checking period.
+ *
+ * This code was formerly part of {@link SMSDispatcher}, and has been moved
+ * into a separate class to support instantiation of multiple SMSDispatchers on
+ * dual-mode devices that require support for both 3GPP and 3GPP2 format messages.
+ */
+public class SmsUsageMonitor {
+    private static final String TAG = "SmsStorageMonitor";
+
+    /** Default checking period for SMS sent without user permission. */
+    private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000;
+
+    /** Default number of SMS sent in checking period without user permission. */
+    private static final int DEFAULT_SMS_MAX_COUNT = 100;
+
+    private final int mCheckPeriod;
+    private final int mMaxAllowed;
+    private final HashMap<String, ArrayList<Long>> mSmsStamp =
+            new HashMap<String, ArrayList<Long>>();
+
+    /**
+     * Create SMS usage monitor.
+     * @param resolver the ContentResolver to use to load from secure settings
+     */
+    public SmsUsageMonitor(ContentResolver resolver) {
+        mMaxAllowed = Settings.Secure.getInt(resolver,
+                Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT,
+                DEFAULT_SMS_MAX_COUNT);
+
+        mCheckPeriod = Settings.Secure.getInt(resolver,
+                Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS,
+                DEFAULT_SMS_CHECK_PERIOD);
+    }
+
+    /** Clear the SMS application list for disposal. */
+    void dispose() {
+        mSmsStamp.clear();
+    }
+
+    /**
+     * Check to see if an application is allowed to send new SMS messages.
+     *
+     * @param appName the application sending sms
+     * @param smsWaiting the number of new messages desired to send
+     * @return true if application is allowed to send the requested number
+     *  of new sms messages
+     */
+    public boolean check(String appName, int smsWaiting) {
+        synchronized (mSmsStamp) {
+            removeExpiredTimestamps();
+
+            ArrayList<Long> sentList = mSmsStamp.get(appName);
+            if (sentList == null) {
+                sentList = new ArrayList<Long>();
+                mSmsStamp.put(appName, sentList);
+            }
+
+            return isUnderLimit(sentList, smsWaiting);
+        }
+    }
+
+    /**
+     * Remove keys containing only old timestamps. This can happen if an SMS app is used
+     * to send messages and then uninstalled.
+     */
+    private void removeExpiredTimestamps() {
+        long beginCheckPeriod = System.currentTimeMillis() - mCheckPeriod;
+
+        synchronized (mSmsStamp) {
+            Iterator<Map.Entry<String, ArrayList<Long>>> iter = mSmsStamp.entrySet().iterator();
+            while (iter.hasNext()) {
+                Map.Entry<String, ArrayList<Long>> entry = iter.next();
+                ArrayList<Long> oldList = entry.getValue();
+                if (oldList.isEmpty() || oldList.get(oldList.size() - 1) < beginCheckPeriod) {
+                    iter.remove();
+                }
+            }
+        }
+    }
+
+    private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) {
+        Long ct = System.currentTimeMillis();
+        long beginCheckPeriod = ct - mCheckPeriod;
+
+        Log.d(TAG, "SMS send size=" + sent.size() + " time=" + ct);
+
+        while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) {
+            sent.remove(0);
+        }
+
+        if ((sent.size() + smsWaiting) <= mMaxAllowed) {
+            for (int i = 0; i < smsWaiting; i++ ) {
+                sent.add(ct);
+            }
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index 6903025..c2b9e4f 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -27,6 +27,9 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.SMSDispatcher;
+import com.android.internal.telephony.gsm.GsmSMSDispatcher;
 import com.android.internal.telephony.gsm.SimCard;
 import com.android.internal.telephony.ims.IsimRecords;
 
@@ -35,14 +38,13 @@
 
     private static final boolean DBG = true;
 
+    /** Secondary SMSDispatcher for 3GPP format messages. */
+    SMSDispatcher m3gppSMS;
+
     // Constructors
     public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) {
-        this(context, ci, notifier, false);
-    }
-
-    public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
-            boolean unitTestMode) {
         super(context, ci, notifier, false);
+        m3gppSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor);
     }
 
     @Override
@@ -54,6 +56,20 @@
     }
 
     @Override
+    public void dispose() {
+        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+            super.dispose();
+            m3gppSMS.dispose();
+        }
+    }
+
+    @Override
+    public void removeReferences() {
+        super.removeReferences();
+        m3gppSMS = null;
+    }
+
+    @Override
     public DataState getDataConnectionState(String apnType) {
         DataState ret = DataState.DISCONNECTED;
 
@@ -92,13 +108,15 @@
         return ret;
     }
 
+    @Override
     public boolean updateCurrentCarrierInProvider() {
         if (mIccRecords != null) {
             try {
                 Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
                 ContentValues map = new ContentValues();
-                map.put(Telephony.Carriers.NUMERIC, mIccRecords.getOperatorNumeric());
-                log("updateCurrentCarrierInProvider insert uri=" + uri);
+                String operatorNumeric = mIccRecords.getOperatorNumeric();
+                map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
+                log("updateCurrentCarrierInProvider from UICC: numeric=" + operatorNumeric);
                 mContext.getContentResolver().insert(uri, map);
                 return true;
             } catch (SQLException e) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 286515e..09ee28c 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -17,10 +17,9 @@
 package com.android.internal.telephony.cdma;
 
 import android.app.ActivityManagerNative;
-import android.content.Context;
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.content.SharedPreferences;
 import android.database.SQLException;
 import android.net.Uri;
@@ -31,7 +30,6 @@
 import android.os.PowerManager.WakeLock;
 import android.os.Registrant;
 import android.os.RegistrantList;
-import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Telephony;
@@ -42,20 +40,17 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.internal.telephony.cat.CatService;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CallTracker;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.DataConnection;
-import com.android.internal.telephony.IccRecords;
-import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccException;
 import com.android.internal.telephony.IccFileHandler;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
 import com.android.internal.telephony.IccSmsInterfaceManager;
+import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.MmiCode;
 import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.Phone;
@@ -67,19 +62,17 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.CallTracker;
-
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
+import com.android.internal.telephony.cat.CatService;
 
 import java.util.ArrayList;
 import java.util.List;
-
-
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
+
 /**
  * {@hide}
  */
@@ -109,13 +102,13 @@
     CatService mCcatService;
 
     // mNvLoadedRegistrants are informed after the EVENT_NV_READY
-    private RegistrantList mNvLoadedRegistrants = new RegistrantList();
+    private final RegistrantList mNvLoadedRegistrants = new RegistrantList();
 
     // mEriFileLoadedRegistrants are informed after the ERI text has been loaded
-    private RegistrantList mEriFileLoadedRegistrants = new RegistrantList();
+    private final RegistrantList mEriFileLoadedRegistrants = new RegistrantList();
 
     // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started
-    private RegistrantList mEcmTimerResetRegistrants = new RegistrantList();
+    private final RegistrantList mEcmTimerResetRegistrants = new RegistrantList();
 
     // mEcmExitRespRegistrant is informed after the phone has been exited
     //the emergency callback mode
@@ -131,6 +124,7 @@
 
     // A runnable which is used to automatically exit from Ecm after a period of time.
     private Runnable mExitEcmRunnable = new Runnable() {
+        @Override
         public void run() {
             exitEmergencyCallbackMode();
         }
@@ -164,7 +158,7 @@
     protected void init(Context context, PhoneNotifier notifier) {
         mCM.setPhoneType(Phone.PHONE_TYPE_CDMA);
         mCT = new CdmaCallTracker(this);
-        mSMS = new CdmaSMSDispatcher(this);
+        mSMS = new CdmaSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor);
         mDataConnectionTracker = new CdmaDataConnectionTracker (this);
         mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
         mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS);
@@ -188,7 +182,7 @@
 
         //Change the system setting
         SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
-                new Integer(Phone.PHONE_TYPE_CDMA).toString());
+                Integer.toString(Phone.PHONE_TYPE_CDMA));
 
         // This is needed to handle phone process crashes
         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
@@ -220,6 +214,7 @@
         notifier.notifyMessageWaitingChanged(this);
     }
 
+    @Override
     public void dispose() {
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
             super.dispose();
@@ -253,23 +248,26 @@
         }
     }
 
+    @Override
     public void removeReferences() {
-            log("removeReferences");
-            this.mRuimPhoneBookInterfaceManager = null;
-            this.mRuimSmsInterfaceManager = null;
-            this.mSMS = null;
-            this.mSubInfo = null;
-            this.mIccRecords = null;
-            this.mIccFileHandler = null;
-            this.mIccCard = null;
-            this.mDataConnectionTracker = null;
-            this.mCT = null;
-            this.mSST = null;
-            this.mEriManager = null;
-            this.mCcatService = null;
-            this.mExitEcmRunnable = null;
+        log("removeReferences");
+        super.removeReferences();
+        mRuimPhoneBookInterfaceManager = null;
+        mRuimSmsInterfaceManager = null;
+        mSMS = null;
+        mSubInfo = null;
+        mIccRecords = null;
+        mIccFileHandler = null;
+        mIccCard = null;
+        mDataConnectionTracker = null;
+        mCT = null;
+        mSST = null;
+        mEriManager = null;
+        mCcatService = null;
+        mExitEcmRunnable = null;
     }
 
+    @Override
     protected void finalize() {
         if(DBG) Log.d(LOG_TAG, "CDMAPhone finalized");
         if (mWakeLock.isHeld()) {
@@ -813,7 +811,7 @@
         return null;
     }
 
-   /**
+    /**
      * Notify any interested party of a Phone state change  {@link Phone.State}
      */
     /*package*/ void notifyPhoneStateChanged() {
@@ -858,18 +856,6 @@
         if (DBG) Log.d(LOG_TAG, "sendEmergencyCallbackModeChange");
     }
 
-    /*package*/ void
-    updateMessageWaitingIndicator(boolean mwi) {
-        // this also calls notifyMessageWaitingIndicator()
-        mIccRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
-    }
-
-    /* This function is overloaded to send number of voicemails instead of sending true/false */
-    /*package*/ void
-    updateMessageWaitingIndicator(int mwi) {
-        mIccRecords.setVoiceMessageWaiting(1, mwi);
-    }
-
     @Override
     public void exitEmergencyCallbackMode() {
         if (mWakeLock.isHeld()) {
@@ -1013,6 +999,7 @@
 
             case EVENT_RUIM_RECORDS_LOADED:{
                 Log.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received");
+                updateCurrentCarrierInProvider();
             }
             break;
 
@@ -1172,7 +1159,7 @@
     private static final int IS683_CONST_1900MHZ_F_BLOCK = 7;
     private static final int INVALID_SYSTEM_SELECTION_CODE = -1;
 
-    private boolean isIs683OtaSpDialStr(String dialStr) {
+    private static boolean isIs683OtaSpDialStr(String dialStr) {
         int sysSelCodeInt;
         boolean isOtaspDialString = false;
         int dialStrLen = dialStr.length();
@@ -1203,7 +1190,7 @@
     /**
      * This function extracts the system selection code from the dial string.
      */
-    private int extractSelCodeFromOtaSpNum(String dialStr) {
+    private static int extractSelCodeFromOtaSpNum(String dialStr) {
         int dialStrLen = dialStr.length();
         int sysSelCodeInt = INVALID_SYSTEM_SELECTION_CODE;
 
@@ -1226,7 +1213,7 @@
      * the dial string "sysSelCodeInt' is the system selection code specified
      * in the carrier ota sp number schema "sch".
      */
-    private boolean
+    private static boolean
     checkOtaSpNumBasedOnSysSelCode (int sysSelCodeInt, String sch[]) {
         boolean isOtaSpNum = false;
         try {
@@ -1414,7 +1401,7 @@
                 Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
                 ContentValues map = new ContentValues();
                 map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
-                log("updateCurrentCarrierInProvider insert uri=" + uri);
+                log("updateCurrentCarrierInProvider from system: numeric=" + operatorNumeric);
                 getContext().getContentResolver().insert(uri, map);
 
                 // Updates MCC MNC device configuration information
@@ -1428,6 +1415,16 @@
         return false;
     }
 
+    /**
+     * Sets the "current" field in the telephony provider according to the SIM's operator.
+     * Implemented in {@link CDMALTEPhone} for CDMA/LTE devices.
+     *
+     * @return true for success; false otherwise.
+     */
+    boolean updateCurrentCarrierInProvider() {
+        return true;
+    }
+
     public void prepareEri() {
         mEriManager.loadEriFile();
         if(mEriManager.isEriFileLoaded()) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index e92a276..57aae56 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -390,6 +390,7 @@
 
             if (operatorNumeric == null) {
                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
+                mGotCountryCode = false;
             } else {
                 String isoCountryCode = "";
                 try {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
index 0617fee..47c638f 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
@@ -26,6 +26,7 @@
 import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.telephony.gsm.SIMRecords;
 import com.android.internal.telephony.ims.IsimRecords;
@@ -438,4 +439,13 @@
         }
         return true;
     }
+
+    /**
+     * Dispatch 3GPP format message. For CDMA/LTE phones,
+     * send the message to the secondary 3GPP format SMS dispatcher.
+     */
+    @Override
+    protected int dispatchGsmMessage(SmsMessageBase message) {
+        return ((CDMALTEPhone) phone).m3gppSMS.dispatchMessage(message);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 07b0f4f..dded39e 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -25,7 +25,6 @@
 import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.database.SQLException;
-import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
 import android.preference.PreferenceManager;
@@ -40,6 +39,8 @@
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
+import com.android.internal.telephony.SmsStorageMonitor;
+import com.android.internal.telephony.SmsUsageMonitor;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.WspTypeDecoder;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
@@ -47,7 +48,6 @@
 import com.android.internal.util.HexDump;
 
 import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 
@@ -60,24 +60,23 @@
     private byte[] mLastDispatchedSmsFingerprint;
     private byte[] mLastAcknowledgedSmsFingerprint;
 
-    private boolean mCheckForDuplicatePortsInOmadmWapPush = Resources.getSystem().getBoolean(
+    private final boolean mCheckForDuplicatePortsInOmadmWapPush = Resources.getSystem().getBoolean(
             com.android.internal.R.bool.config_duplicate_port_omadm_wappush);
 
-    CdmaSMSDispatcher(CDMAPhone phone) {
-        super(phone);
+    CdmaSMSDispatcher(CDMAPhone phone, SmsStorageMonitor storageMonitor,
+            SmsUsageMonitor usageMonitor) {
+        super(phone, storageMonitor, usageMonitor);
+        mCm.setOnNewCdmaSms(this, EVENT_NEW_SMS, null);
     }
 
-    /**
-     * Called when a status report is received.  This should correspond to
-     * a previously successful SEND.
-     * Is a special GSM function, should never be called in CDMA!!
-     *
-     * @param ar AsyncResult passed into the message handler.  ar.result should
-     *           be a String representing the status report PDU, as ASCII hex.
-     */
     @Override
-    protected void handleStatusReport(AsyncResult ar) {
-        Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!");
+    public void dispose() {
+        mCm.unSetOnNewCdmaSms(this);
+    }
+
+    @Override
+    protected String getFormat() {
+        return android.telephony.SmsMessage.FORMAT_3GPP2;
     }
 
     private void handleCdmaStatusReport(SmsMessage sms) {
@@ -138,11 +137,11 @@
             Log.d(TAG, "Voicemail count=" + voicemailCount);
             // Store the voicemail count in preferences.
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
-                    mPhone.getContext());
+                    mContext);
             SharedPreferences.Editor editor = sp.edit();
             editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
             editor.apply();
-            ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount);
+            mPhone.setVoiceMessageWaiting(1, voicemailCount);
             handled = true;
         } else if (((SmsEnvelope.TELESERVICE_WMT == teleService) ||
                 (SmsEnvelope.TELESERVICE_WEMT == teleService)) &&
@@ -160,7 +159,8 @@
             return Intents.RESULT_SMS_HANDLED;
         }
 
-        if (!mStorageAvailable && (sms.getMessageClass() != MessageClass.CLASS_0)) {
+        if (!mStorageMonitor.isStorageAvailable() &&
+                sms.getMessageClass() != MessageClass.CLASS_0) {
             // It's a storable message and there's no storage available.  Bail.
             // (See C.S0015-B v2.0 for a description of "Immediate Display"
             // messages, which we represent as CLASS_0.)
@@ -181,48 +181,7 @@
             return Intents.RESULT_SMS_UNSUPPORTED;
         }
 
-        /*
-         * TODO(cleanup): Why are we using a getter method for this
-         * (and for so many other sms fields)?  Trivial getters and
-         * setters like this are direct violations of the style guide.
-         * If the purpose is to protect against writes (by not
-         * providing a setter) then any protection is illusory (and
-         * hence bad) for cases where the values are not primitives,
-         * such as this call for the header.  Since this is an issue
-         * with the public API it cannot be changed easily, but maybe
-         * something can be done eventually.
-         */
-        SmsHeader smsHeader = sms.getUserDataHeader();
-
-        /*
-         * TODO(cleanup): Since both CDMA and GSM use the same header
-         * format, this dispatch processing is naturally identical,
-         * and code should probably not be replicated explicitly.
-         */
-
-        // See if message is partial or port addressed.
-        if ((smsHeader == null) || (smsHeader.concatRef == null)) {
-            // Message is not partial (not part of concatenated sequence).
-            byte[][] pdus = new byte[1][];
-            pdus[0] = sms.getPdu();
-
-            if (smsHeader != null && smsHeader.portAddrs != null) {
-                if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
-                    // GSM-style WAP indication
-                    return mWapPush.dispatchWapPdu(sms.getUserData());
-                } else {
-                    // The message was sent to a port, so concoct a URI for it.
-                    dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
-                }
-            } else {
-                // Normal short and non-port-addressed message, dispatch it.
-                dispatchPdus(pdus);
-            }
-            return Activity.RESULT_OK;
-        } else {
-            // Process the message part.
-            return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
-        }
+        return dispatchNormalMessage(smsb);
     }
 
     /**
@@ -236,23 +195,19 @@
      *         to applications
      */
     protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) {
-        int segment;
-        int totalSegments;
         int index = 0;
-        int msgType;
 
-        int sourcePort = 0;
-        int destinationPort = 0;
-
-        msgType = pdu[index++];
-        if (msgType != 0){
+        int msgType = pdu[index++];
+        if (msgType != 0) {
             Log.w(TAG, "Received a WAP SMS which is not WDP. Discard.");
             return Intents.RESULT_SMS_HANDLED;
         }
-        totalSegments = pdu[index++]; // >=1
-        segment = pdu[index++]; // >=0
+        int totalSegments = pdu[index++];   // >= 1
+        int segment = pdu[index++];         // >= 0
 
         // Only the first segment contains sourcePort and destination Port
+        int sourcePort = 0;
+        int destinationPort = 0;
         if (segment == 0) {
             //process WDP segment
             sourcePort = (0xFF & pdu[index++]) << 8;
@@ -269,90 +224,16 @@
         }
 
         // Lookup all other related parts
-        StringBuilder where = new StringBuilder("reference_number =");
-        where.append(referenceNumber);
-        where.append(" AND address = ?");
-        String[] whereArgs = new String[] {address};
-
         Log.i(TAG, "Received WAP PDU. Type = " + msgType + ", originator = " + address
                 + ", src-port = " + sourcePort + ", dst-port = " + destinationPort
-                + ", ID = " + referenceNumber + ", segment# = " + segment + "/" + totalSegments);
+                + ", ID = " + referenceNumber + ", segment# = " + segment + '/' + totalSegments);
 
-        byte[][] pdus = null;
-        Cursor cursor = null;
-        try {
-            cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null);
-            int cursorCount = cursor.getCount();
-            if (cursorCount != totalSegments - 1) {
-                // We don't have all the parts yet, store this one away
-                ContentValues values = new ContentValues();
-                values.put("date", (long) 0);
-                values.put("pdu", HexDump.toHexString(pdu, index, pdu.length - index));
-                values.put("address", address);
-                values.put("reference_number", referenceNumber);
-                values.put("count", totalSegments);
-                values.put("sequence", segment);
-                values.put("destination_port", destinationPort);
+        // pass the user data portion of the PDU to the shared handler in SMSDispatcher
+        byte[] userData = new byte[pdu.length - index];
+        System.arraycopy(pdu, index, userData, 0, pdu.length - index);
 
-                mResolver.insert(mRawUri, values);
-
-                return Intents.RESULT_SMS_HANDLED;
-            }
-
-            // All the parts are in place, deal with them
-            int pduColumn = cursor.getColumnIndex("pdu");
-            int sequenceColumn = cursor.getColumnIndex("sequence");
-
-            pdus = new byte[totalSegments][];
-            for (int i = 0; i < cursorCount; i++) {
-                cursor.moveToNext();
-                int cursorSequence = (int)cursor.getLong(sequenceColumn);
-                // Read the destination port from the first segment
-                if (cursorSequence == 0) {
-                    int destinationPortColumn = cursor.getColumnIndex("destination_port");
-                    destinationPort = (int)cursor.getLong(destinationPortColumn);
-                }
-                pdus[cursorSequence] = HexDump.hexStringToByteArray(
-                        cursor.getString(pduColumn));
-            }
-            // The last part will be added later
-
-            // Remove the parts from the database
-            mResolver.delete(mRawUri, where.toString(), whereArgs);
-        } catch (SQLException e) {
-            Log.e(TAG, "Can't access multipart SMS database", e);
-            return Intents.RESULT_SMS_GENERIC_ERROR;
-        } finally {
-            if (cursor != null) cursor.close();
-        }
-
-        // Build up the data stream
-        ByteArrayOutputStream output = new ByteArrayOutputStream();
-        for (int i = 0; i < totalSegments; i++) {
-            // reassemble the (WSP-)pdu
-            if (i == segment) {
-                // This one isn't in the DB, so add it
-                output.write(pdu, index, pdu.length - index);
-            } else {
-                output.write(pdus[i], 0, pdus[i].length);
-            }
-        }
-
-        byte[] datagram = output.toByteArray();
-        // Dispatch the PDU to applications
-        switch (destinationPort) {
-        case SmsHeader.PORT_WAP_PUSH:
-            // Handle the PUSH
-            return mWapPush.dispatchWapPdu(datagram);
-
-        default:{
-            pdus = new byte[1][];
-            pdus[0] = datagram;
-            // The messages were sent to any other WAP port
-            dispatchPortAddressedPdus(pdus, destinationPort);
-            return Activity.RESULT_OK;
-        }
-        }
+        return processMessagePart(userData, address, referenceNumber, segment, totalSegments,
+                0L, destinationPort, true);
     }
 
     /** {@inheritDoc} */
@@ -375,68 +256,34 @@
 
     /** {@inheritDoc} */
     @Override
-    protected void sendMultipartText(String destAddr, String scAddr,
-            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
-            ArrayList<PendingIntent> deliveryIntents) {
+    protected TextEncodingDetails calculateLength(CharSequence messageBody,
+            boolean use7bitOnly) {
+        return SmsMessage.calculateLength(messageBody, use7bitOnly);
+    }
 
-        /**
-         * TODO(cleanup): There is no real code difference between
-         * this and the GSM version, and hence it should be moved to
-         * the base class or consolidated somehow, provided calling
-         * the proper submit pdu stuff can be arranged.
-         */
-
-        int refNumber = getNextConcatenatedRef() & 0x00FF;
-        int msgCount = parts.size();
-        int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;
-
-        for (int i = 0; i < msgCount; i++) {
-            TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
-            if (encoding != details.codeUnitSize
-                    && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN
-                            || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
-                encoding = details.codeUnitSize;
-            }
+    /** {@inheritDoc} */
+    @Override
+    protected void sendNewSubmitPdu(String destinationAddress, String scAddress,
+            String message, SmsHeader smsHeader, int encoding,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) {
+        UserData uData = new UserData();
+        uData.payloadStr = message;
+        uData.userDataHeader = smsHeader;
+        if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
+            uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+        } else { // assume UTF-16
+            uData.msgEncoding = UserData.ENCODING_UNICODE_16;
         }
+        uData.msgEncodingSet = true;
 
-        for (int i = 0; i < msgCount; i++) {
-            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
-            concatRef.refNumber = refNumber;
-            concatRef.seqNumber = i + 1;  // 1-based sequence
-            concatRef.msgCount = msgCount;
-            concatRef.isEightBits = true;
-            SmsHeader smsHeader = new SmsHeader();
-            smsHeader.concatRef = concatRef;
+        /* By setting the statusReportRequested bit only for the
+         * last message fragment, this will result in only one
+         * callback to the sender when that last fragment delivery
+         * has been acknowledged. */
+        SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress,
+                uData, (deliveryIntent != null) && lastPart);
 
-            PendingIntent sentIntent = null;
-            if (sentIntents != null && sentIntents.size() > i) {
-                sentIntent = sentIntents.get(i);
-            }
-
-            PendingIntent deliveryIntent = null;
-            if (deliveryIntents != null && deliveryIntents.size() > i) {
-                deliveryIntent = deliveryIntents.get(i);
-            }
-
-            UserData uData = new UserData();
-            uData.payloadStr = parts.get(i);
-            uData.userDataHeader = smsHeader;
-            if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
-                uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
-            } else { // assume UTF-16
-                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
-            }
-            uData.msgEncodingSet = true;
-
-            /* By setting the statusReportRequested bit only for the
-             * last message fragment, this will result in only one
-             * callback to the sender when that last fragment delivery
-             * has been acknowledged. */
-            SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destAddr,
-                    uData, (deliveryIntent != null) && (i == (msgCount - 1)));
-
-            sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
-        }
+        sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
     }
 
     protected void sendSubmitPdu(SmsMessage.SubmitPdu pdu,
@@ -464,43 +311,27 @@
         byte pdu[] = (byte[]) map.get("pdu");
 
         Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
-
         mCm.sendCdmaSms(pdu, reply);
     }
 
-     /** {@inheritDoc} */
-    @Override
-    protected void sendMultipartSms (SmsTracker tracker) {
-        Log.d(TAG, "TODO: CdmaSMSDispatcher.sendMultipartSms not implemented");
-    }
-
     /** {@inheritDoc} */
     @Override
-    protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){
-        // FIXME unit test leaves cm == null. this should change
-
+    protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) {
         String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
         if (inEcm.equals("true")) {
             return;
         }
 
-        if (mCm != null) {
-            int causeCode = resultToCause(result);
-            mCm.acknowledgeLastIncomingCdmaSms(success, causeCode, response);
+        int causeCode = resultToCause(result);
+        mCm.acknowledgeLastIncomingCdmaSms(success, causeCode, response);
 
-            if (causeCode == 0) {
-                mLastAcknowledgedSmsFingerprint = mLastDispatchedSmsFingerprint;
-            }
-            mLastDispatchedSmsFingerprint = null;
+        if (causeCode == 0) {
+            mLastAcknowledgedSmsFingerprint = mLastDispatchedSmsFingerprint;
         }
+        mLastDispatchedSmsFingerprint = null;
     }
 
-    protected void handleBroadcastSms(AsyncResult ar) {
-        // Not supported
-        Log.e(TAG, "Error! Not implemented for CDMA.");
-    }
-
-    private int resultToCause(int rc) {
+    private static int resultToCause(int rc) {
         switch (rc) {
         case Activity.RESULT_OK:
         case Intents.RESULT_SMS_HANDLED:
@@ -527,7 +358,7 @@
      * @return True if OrigPdu is OmaDM Push Message which has duplicate ports.
      *         False if OrigPdu is NOT OmaDM Push Message which has duplicate ports.
      */
-    private boolean checkDuplicatePortOmadmWappush(byte[] origPdu, int index) {
+    private static boolean checkDuplicatePortOmadmWappush(byte[] origPdu, int index) {
         index += 4;
         byte[] omaPdu = new byte[origPdu.length - index];
         System.arraycopy(origPdu, index, omaPdu, 0, omaPdu.length);
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 0aed77e..8f5a2eb 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -964,6 +964,7 @@
 
             if (operatorNumeric == null) {
                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
+                mGotCountryCode = false;
             } else {
                 String isoCountryCode = "";
                 try{
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index be5c616..1409cab 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -114,30 +114,6 @@
     }
 
     /**
-     * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
-     */
-    public static SmsMessage newFromCMT(String[] lines) {
-        Log.w(LOG_TAG, "newFromCMT: is not supported in CDMA mode.");
-        return null;
-    }
-
-    /**
-     * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
-     */
-    public static SmsMessage newFromCMTI(String line) {
-        Log.w(LOG_TAG, "newFromCMTI: is not supported in CDMA mode.");
-        return null;
-    }
-
-    /**
-     * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
-     */
-    public static SmsMessage newFromCDS(String line) {
-        Log.w(LOG_TAG, "newFromCDS: is not supported in CDMA mode.");
-        return null;
-    }
-
-    /**
      *  Create a "raw" CDMA SmsMessage from a Parcel that was forged in ril.cpp.
      *  Note: Only primitive fields are set.
      */
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index d325aaa..e1f4c4b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -56,9 +56,6 @@
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.DataConnection;
-import com.android.internal.telephony.DataConnectionTracker;
-import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccFileHandler;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
 import com.android.internal.telephony.IccSmsInterfaceManager;
@@ -140,7 +137,7 @@
         mCM.setPhoneType(Phone.PHONE_TYPE_GSM);
         mCT = new GsmCallTracker(this);
         mSST = new GsmServiceStateTracker (this);
-        mSMS = new GsmSMSDispatcher(this);
+        mSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor);
         mIccFileHandler = new SIMFileHandler(this);
         mIccRecords = new SIMRecords(this);
         mDataConnectionTracker = new GsmDataConnectionTracker (this);
@@ -199,6 +196,7 @@
                 new Integer(Phone.PHONE_TYPE_GSM).toString());
     }
 
+    @Override
     public void dispose() {
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
             super.dispose();
@@ -228,19 +226,22 @@
         }
     }
 
+    @Override
     public void removeReferences() {
-            this.mSimulatedRadioControl = null;
-            this.mStkService = null;
-            this.mSimPhoneBookIntManager = null;
-            this.mSimSmsIntManager = null;
-            this.mSMS = null;
-            this.mSubInfo = null;
-            this.mIccRecords = null;
-            this.mIccFileHandler = null;
-            this.mIccCard = null;
-            this.mDataConnectionTracker = null;
-            this.mCT = null;
-            this.mSST = null;
+        Log.d(LOG_TAG, "removeReferences");
+        super.removeReferences();
+        mSimulatedRadioControl = null;
+        mStkService = null;
+        mSimPhoneBookIntManager = null;
+        mSimSmsIntManager = null;
+        mSMS = null;
+        mSubInfo = null;
+        mIccRecords = null;
+        mIccFileHandler = null;
+        mIccCard = null;
+        mDataConnectionTracker = null;
+        mCT = null;
+        mSST = null;
     }
 
     protected void finalize() {
@@ -406,17 +407,6 @@
     }
 
     public void
-    notifyDataConnectionFailed(String reason, String apnType) {
-        mNotifier.notifyDataConnectionFailed(this, reason, apnType);
-    }
-
-    /*package*/ void
-    updateMessageWaitingIndicator(boolean mwi) {
-        // this also calls notifyMessageWaitingIndicator()
-        mIccRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
-    }
-
-    public void
     notifyCallForwardingIndicator() {
         mNotifier.notifyCallForwardingChanged(this);
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 52ca453..4e1cc9a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -30,13 +30,15 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.util.Log;
 
-import com.android.internal.telephony.BaseCommands;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
+import com.android.internal.telephony.SmsStorageMonitor;
+import com.android.internal.telephony.SmsUsageMonitor;
 import com.android.internal.telephony.TelephonyProperties;
 
 import java.util.ArrayList;
@@ -45,16 +47,55 @@
 
 import static android.telephony.SmsMessage.MessageClass;
 
-final class GsmSMSDispatcher extends SMSDispatcher {
+public final class GsmSMSDispatcher extends SMSDispatcher {
     private static final String TAG = "GSM";
 
-    private GSMPhone mGsmPhone;
+    /** Status report received */
+    private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
 
-    GsmSMSDispatcher(GSMPhone phone) {
-        super(phone);
-        mGsmPhone = phone;
+    /** New broadcast SMS */
+    private static final int EVENT_NEW_BROADCAST_SMS = 101;
 
-        ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null);
+    public GsmSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
+            SmsUsageMonitor usageMonitor) {
+        super(phone, storageMonitor, usageMonitor);
+        mCm.setOnNewGsmSms(this, EVENT_NEW_SMS, null);
+        mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
+        mCm.setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null);
+    }
+
+    @Override
+    public void dispose() {
+        mCm.unSetOnNewGsmSms(this);
+        mCm.unSetOnSmsStatus(this);
+        mCm.unSetOnNewGsmBroadcastSms(this);
+    }
+
+    @Override
+    protected String getFormat() {
+        return android.telephony.SmsMessage.FORMAT_3GPP;
+    }
+
+    /**
+     * Handles 3GPP format-specific events coming from the phone stack.
+     * Other events are handled by {@link SMSDispatcher#handleMessage}.
+     *
+     * @param msg the message to handle
+     */
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+        case EVENT_NEW_SMS_STATUS_REPORT:
+            handleStatusReport((AsyncResult) msg.obj);
+            break;
+
+        case EVENT_NEW_BROADCAST_SMS:
+            handleBroadcastSms((AsyncResult)msg.obj);
+            break;
+
+        default:
+            super.handleMessage(msg);
+        }
     }
 
     /**
@@ -64,8 +105,7 @@
      * @param ar AsyncResult passed into the message handler.  ar.result should
      *           be a String representing the status report PDU, as ASCII hex.
      */
-    @Override
-    protected void handleStatusReport(AsyncResult ar) {
+    private void handleStatusReport(AsyncResult ar) {
         String pduString = (String) ar.result;
         SmsMessage sms = SmsMessage.newFromCDS(pduString);
 
@@ -94,17 +134,17 @@
         acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null);
     }
 
-
     /** {@inheritDoc} */
     @Override
     public int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
         if (smsb == null) {
+            Log.e(TAG, "dispatchMessage: message is null");
             return Intents.RESULT_SMS_GENERIC_ERROR;
         }
+
         SmsMessage sms = (SmsMessage) smsb;
-        boolean handled = false;
 
         if (sms.isTypeZero()) {
             // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
@@ -121,14 +161,15 @@
         }
 
         // Special case the message waiting indicator messages
+        boolean handled = false;
         if (sms.isMWISetMessage()) {
-            mGsmPhone.updateMessageWaitingIndicator(true);
+            mPhone.setVoiceMessageWaiting(1, -1);  // line 1: unknown number of msgs waiting
             handled = sms.isMwiDontStore();
             if (false) {
                 Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
             }
         } else if (sms.isMWIClearMessage()) {
-            mGsmPhone.updateMessageWaitingIndicator(false);
+            mPhone.setVoiceMessageWaiting(1, 0);   // line 1: no msgs waiting
             handled = sms.isMwiDontStore();
             if (false) {
                 Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
@@ -139,35 +180,14 @@
             return Intents.RESULT_SMS_HANDLED;
         }
 
-        if (!mStorageAvailable && (sms.getMessageClass() != MessageClass.CLASS_0)) {
+        if (!mStorageMonitor.isStorageAvailable() &&
+                sms.getMessageClass() != MessageClass.CLASS_0) {
             // It's a storable message and there's no storage available.  Bail.
             // (See TS 23.038 for a description of class 0 messages.)
             return Intents.RESULT_SMS_OUT_OF_MEMORY;
         }
 
-        SmsHeader smsHeader = sms.getUserDataHeader();
-         // See if message is partial or port addressed.
-        if ((smsHeader == null) || (smsHeader.concatRef == null)) {
-            // Message is not partial (not part of concatenated sequence).
-            byte[][] pdus = new byte[1][];
-            pdus[0] = sms.getPdu();
-
-            if (smsHeader != null && smsHeader.portAddrs != null) {
-                if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
-                    return mWapPush.dispatchWapPdu(sms.getUserData());
-                } else {
-                    // The message was sent to a port, so concoct a URI for it.
-                    dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
-                }
-            } else {
-                // Normal short and non-port-addressed message, dispatch it.
-                dispatchPdus(pdus);
-            }
-            return Activity.RESULT_OK;
-        } else {
-            // Process the message part.
-            return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
-        }
+        return dispatchNormalMessage(smsb);
     }
 
     /** {@inheritDoc} */
@@ -190,158 +210,20 @@
 
     /** {@inheritDoc} */
     @Override
-    protected void sendMultipartText(String destinationAddress, String scAddress,
-            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
-            ArrayList<PendingIntent> deliveryIntents) {
-
-        int refNumber = getNextConcatenatedRef() & 0x00FF;
-        int msgCount = parts.size();
-        int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;
-
-        mRemainingMessages = msgCount;
-
-        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
-        for (int i = 0; i < msgCount; i++) {
-            TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
-            if (encoding != details.codeUnitSize
-                    && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN
-                            || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
-                encoding = details.codeUnitSize;
-            }
-            encodingForParts[i] = details;
-        }
-
-        for (int i = 0; i < msgCount; i++) {
-            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
-            concatRef.refNumber = refNumber;
-            concatRef.seqNumber = i + 1;  // 1-based sequence
-            concatRef.msgCount = msgCount;
-            // TODO: We currently set this to true since our messaging app will never
-            // send more than 255 parts (it converts the message to MMS well before that).
-            // However, we should support 3rd party messaging apps that might need 16-bit
-            // references
-            // Note:  It's not sufficient to just flip this bit to true; it will have
-            // ripple effects (several calculations assume 8-bit ref).
-            concatRef.isEightBits = true;
-            SmsHeader smsHeader = new SmsHeader();
-            smsHeader.concatRef = concatRef;
-            if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
-                smsHeader.languageTable = encodingForParts[i].languageTable;
-                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
-            }
-
-            PendingIntent sentIntent = null;
-            if (sentIntents != null && sentIntents.size() > i) {
-                sentIntent = sentIntents.get(i);
-            }
-
-            PendingIntent deliveryIntent = null;
-            if (deliveryIntents != null && deliveryIntents.size() > i) {
-                deliveryIntent = deliveryIntents.get(i);
-            }
-
-            SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
-                    parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
-                    encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
-
-            sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
-        }
+    protected TextEncodingDetails calculateLength(CharSequence messageBody,
+            boolean use7bitOnly) {
+        return SmsMessage.calculateLength(messageBody, use7bitOnly);
     }
 
-    /**
-     * Send a multi-part text based SMS which already passed SMS control check.
-     *
-     * It is the working function for sendMultipartText().
-     *
-     * @param destinationAddress the address to send the message to
-     * @param scAddress is the service center address or null to use
-     *   the current default SMSC
-     * @param parts an <code>ArrayList</code> of strings that, in order,
-     *   comprise the original message
-     * @param sentIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been sent.
-     *   The result code will be <code>Activity.RESULT_OK<code> for success,
-     *   or one of these errors:
-     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
-     *   <code>RESULT_ERROR_RADIO_OFF</code>
-     *   <code>RESULT_ERROR_NULL_PDU</code>.
-     * @param deliveryIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been delivered
-     *   to the recipient.  The raw pdu of the status report is in the
-     *   extended data ("pdu").
-     */
-    private void sendMultipartTextWithPermit(String destinationAddress,
-            String scAddress, ArrayList<String> parts,
-            ArrayList<PendingIntent> sentIntents,
-            ArrayList<PendingIntent> deliveryIntents) {
-
-        // check if in service
-        int ss = mPhone.getServiceState().getState();
-        if (ss != ServiceState.STATE_IN_SERVICE) {
-            for (int i = 0, count = parts.size(); i < count; i++) {
-                PendingIntent sentIntent = null;
-                if (sentIntents != null && sentIntents.size() > i) {
-                    sentIntent = sentIntents.get(i);
-                }
-                SmsTracker tracker = SmsTrackerFactory(null, sentIntent, null);
-                handleNotInService(ss, tracker);
-            }
-            return;
-        }
-
-        int refNumber = getNextConcatenatedRef() & 0x00FF;
-        int msgCount = parts.size();
-        int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;
-
-        mRemainingMessages = msgCount;
-
-        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
-        for (int i = 0; i < msgCount; i++) {
-            TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
-            if (encoding != details.codeUnitSize
-                    && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN
-                            || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
-                encoding = details.codeUnitSize;
-            }
-            encodingForParts[i] = details;
-        }
-
-        for (int i = 0; i < msgCount; i++) {
-            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
-            concatRef.refNumber = refNumber;
-            concatRef.seqNumber = i + 1;  // 1-based sequence
-            concatRef.msgCount = msgCount;
-            concatRef.isEightBits = false;
-            SmsHeader smsHeader = new SmsHeader();
-            smsHeader.concatRef = concatRef;
-            if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
-                smsHeader.languageTable = encodingForParts[i].languageTable;
-                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
-            }
-
-            PendingIntent sentIntent = null;
-            if (sentIntents != null && sentIntents.size() > i) {
-                sentIntent = sentIntents.get(i);
-            }
-
-            PendingIntent deliveryIntent = null;
-            if (deliveryIntents != null && deliveryIntents.size() > i) {
-                deliveryIntent = deliveryIntents.get(i);
-            }
-
-            SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
-                    parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
-                    encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
-
-            HashMap<String, Object> map = new HashMap<String, Object>();
-            map.put("smsc", pdus.encodedScAddress);
-            map.put("pdu", pdus.encodedMessage);
-
-            SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent);
-            sendSms(tracker);
-        }
+    /** {@inheritDoc} */
+    @Override
+    protected void sendNewSubmitPdu(String destinationAddress, String scAddress,
+            String message, SmsHeader smsHeader, int encoding,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) {
+        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
+                message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
+                encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
+        sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
     }
 
     /** {@inheritDoc} */
@@ -353,45 +235,16 @@
         byte pdu[] = (byte[]) map.get("pdu");
 
         Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
-        mCm.sendSMS(IccUtils.bytesToHexString(smsc),
-                IccUtils.bytesToHexString(pdu), reply);
-    }
-
-    /**
-     * Send the multi-part SMS based on multipart Sms tracker
-     *
-     * @param tracker holds the multipart Sms tracker ready to be sent
-     */
-    @Override
-    protected void sendMultipartSms (SmsTracker tracker) {
-        ArrayList<String> parts;
-        ArrayList<PendingIntent> sentIntents;
-        ArrayList<PendingIntent> deliveryIntents;
-
-        HashMap<String, Object> map = tracker.mData;
-
-        String destinationAddress = (String) map.get("destination");
-        String scAddress = (String) map.get("scaddress");
-
-        parts = (ArrayList<String>) map.get("parts");
-        sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
-        deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
-
-        sendMultipartTextWithPermit(destinationAddress,
-                scAddress, parts, sentIntents, deliveryIntents);
-
+        mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);
     }
 
     /** {@inheritDoc} */
     @Override
-    protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){
-        // FIXME unit test leaves cm == null. this should change
-        if (mCm != null) {
-            mCm.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response);
-        }
+    protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) {
+        mCm.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response);
     }
 
-    private int resultToCause(int rc) {
+    private static int resultToCause(int rc) {
         switch (rc) {
             case Activity.RESULT_OK:
             case Intents.RESULT_SMS_HANDLED:
@@ -485,10 +338,12 @@
     private final HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
             new HashMap<SmsCbConcatInfo, byte[][]>();
 
-    @Override
-    protected void handleBroadcastSms(AsyncResult ar) {
+    /**
+     * Handle 3GPP format SMS-CB message.
+     * @param ar the AsyncResult containing the received PDUs
+     */
+    private void handleBroadcastSms(AsyncResult ar) {
         try {
-            byte[][] pdus = null;
             byte[] receivedPdu = (byte[])ar.result;
 
             if (false) {
@@ -507,10 +362,11 @@
 
             SmsCbHeader header = new SmsCbHeader(receivedPdu);
             String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC);
-            GsmCellLocation cellLocation = (GsmCellLocation)mGsmPhone.getCellLocation();
+            GsmCellLocation cellLocation = (GsmCellLocation) mPhone.getCellLocation();
             int lac = cellLocation.getLac();
             int cid = cellLocation.getCid();
 
+            byte[][] pdus;
             if (header.nrOfPages > 1) {
                 // Multi-page message
                 SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid);
@@ -563,5 +419,4 @@
             Log.e(TAG, "Error in decoding SMS CB pdu", e);
         }
     }
-
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index d3645fa..eea2780 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -846,6 +846,7 @@
 
             if (operatorNumeric == null) {
                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
+                mGotCountryCode = false;
             } else {
                 String iso = "";
                 try{
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 73c319c..5d6f181 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -38,6 +38,7 @@
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.SmsMessageBase;
 
 import java.util.ArrayList;
 
@@ -1160,6 +1161,15 @@
         }
     }
 
+    /**
+     * Dispatch 3GPP format message. Overridden for CDMA/LTE phones by
+     * {@link com.android.internal.telephony.cdma.CdmaLteUiccRecords}
+     * to send messages to the secondary 3GPP format SMS dispatcher.
+     */
+    protected int dispatchGsmMessage(SmsMessageBase message) {
+        return phone.mSMS.dispatchMessage(message);
+    }
+
     private void handleSms(byte[] ba) {
         if (ba[0] != 0)
             Log.d("ENF", "status : " + ba[0]);
@@ -1175,7 +1185,7 @@
             System.arraycopy(ba, 1, pdu, 0, n - 1);
             SmsMessage message = SmsMessage.createFromPdu(pdu);
 
-            phone.mSMS.dispatchMessage(message);
+            dispatchGsmMessage(message);
         }
     }
 
@@ -1201,7 +1211,7 @@
                 System.arraycopy(ba, 1, pdu, 0, n - 1);
                 SmsMessage message = SmsMessage.createFromPdu(pdu);
 
-                phone.mSMS.dispatchMessage(message);
+                dispatchGsmMessage(message);
 
                 // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
                 // 1 == "received by MS from network; message read"
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 3784e7c..ea030e6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -137,14 +137,6 @@
     }
 
     /** @hide */
-    public static SmsMessage newFromCMTI(String line) {
-        // the thinking here is not to read the message immediately
-        // FTA test case
-        Log.e(LOG_TAG, "newFromCMTI: not yet supported");
-        return null;
-    }
-
-    /** @hide */
     public static SmsMessage newFromCDS(String line) {
         try {
             SmsMessage msg = new SmsMessage();
@@ -157,15 +149,6 @@
     }
 
     /**
-     * Note: This functionality is currently not supported in GSM mode.
-     * @hide
-     */
-    public static SmsMessageBase newFromParcel(Parcel p){
-        Log.w(LOG_TAG, "newFromParcel: is not supported in GSM mode.");
-        return null;
-    }
-
-    /**
      * Create an SmsMessage from an SMS EF record.
      *
      * @param index Index of SMS record. This should be index in ArrayList