Merge "Recents background protection increased"
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c30675b..e593d5b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2174,6 +2174,7 @@
         info.screenOrientation = target.info.screenOrientation;
         info.taskAffinity = target.info.taskAffinity;
         info.theme = target.info.theme;
+        info.softInputMode = target.info.softInputMode;
         info.uiOptions = target.info.uiOptions;
         
         Activity a = new Activity(mParseActivityAliasArgs, info);
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index ded496c..b14ca2b0 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -304,7 +304,8 @@
          * or an empty string are reserved for indicating that the calendar does
          * not use an index for looking up the color. The provider will update
          * {@link #CALENDAR_COLOR} automatically when a valid index is written
-         * to this column. @see Colors
+         * to this column. The index must reference an existing row of the
+         * {@link Colors} table. @see Colors
          * <P>
          * Type: TEXT
          * </P>
@@ -852,8 +853,11 @@
          * string are reserved for indicating that the event does not use an
          * index for looking up the color. The provider will update
          * {@link #EVENT_COLOR} automatically when a valid index is written to
-         * this column. @see Colors
-         * <P>Type: TEXT</P>
+         * this column. The index must reference an existing row of the
+         * {@link Colors} table. @see Colors
+         * <P>
+         * Type: TEXT
+         * </P>
          * TODO UNHIDE
          *
          * @hide
@@ -2329,7 +2333,7 @@
         /**
          * The index used to reference this color. This can be any non-empty
          * string, but must be unique for a given {@link #ACCOUNT_TYPE} and
-         * {@link #ACCOUNT_NAME} . Column name.
+         * {@link #ACCOUNT_NAME}. Column name.
          * <P>
          * Type: TEXT
          * </P>
@@ -2353,8 +2357,10 @@
     /**
      * Fields for accessing colors available for a given account. Colors are
      * referenced by {@link #COLOR_INDEX} which must be unique for a given
-     * account name/type. These values should only be updated by the sync
-     * adapter.
+     * account name/type. These values can only be updated by the sync
+     * adapter. Only {@link #COLOR} may be updated after the initial insert. In
+     * addition, a row can only be deleted once all references to that color
+     * have been removed from the {@link Calendars} or {@link Events} tables.
      * TODO UNHIDE
      *
      * @hide
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index 7759ff3..4662040 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -22,6 +22,7 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import java.io.File;
 import java.io.InputStream;
 
 class JniUtil {
@@ -79,7 +80,12 @@
         checkInitialized();
 
         if (sCacheDirectory == null) {
-            sCacheDirectory = sContext.getCacheDir().getAbsolutePath();
+            File cacheDir = sContext.getCacheDir();
+            if (cacheDir == null) {
+                sCacheDirectory = "";
+            } else {
+                sCacheDirectory = cacheDir.getAbsolutePath();
+            }
         }
 
         return sCacheDirectory;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index f1c2bde..f240a2e 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -777,7 +777,7 @@
     public void setDoubleTapZoom(int doubleTapZoom) {
         if (mDoubleTapZoom != doubleTapZoom) {
             mDoubleTapZoom = doubleTapZoom;
-            mWebView.updateDoubleTapZoom();
+            mWebView.updateDoubleTapZoom(doubleTapZoom);
         }
     }
 
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index b0ecf09..ccacd09 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -607,23 +607,31 @@
         // character or the existing selection, so it will not get cleared
         // above.
         mGotDelete = false;
+        // Prefer sending javascript events, so when adding one character,
+        // don't replace the unchanged text.
+        if (count > 1 && before == count - 1) {
+            String replaceButOne =  s.subSequence(start,
+                    start + before).toString();
+            String replacedString = getText().subSequence(start,
+                    start + before).toString();
+            if (replaceButOne.equals(replacedString)) {
+                // we're just adding one character
+                start += before;
+                before = 0;
+                count = 1;
+            }
+        }
         // Find the last character being replaced.  If it can be represented by
-        // events, we will pass them to native (after replacing the beginning
-        // of the changed text), so we can see javascript events.
-        // Otherwise, replace the text being changed (including the last
-        // character) in the textfield.
-        TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0);
-        KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
-        KeyEvent[] events = kmap.getEvents(mCharacter);
-        boolean cannotUseKeyEvents = null == events;
-        int charactersFromKeyEvents = cannotUseKeyEvents ? 0 : 1;
-        if (count > 1 || cannotUseKeyEvents) {
-            String replace = s.subSequence(start,
-                    start + count - charactersFromKeyEvents).toString();
-            mWebView.replaceTextfieldText(start, start + before, replace,
-                    start + count - charactersFromKeyEvents,
-                    start + count - charactersFromKeyEvents);
-        } else {
+        // events, we will pass them to native so we can see javascript events.
+        // Otherwise, replace the text being changed in the textfield.
+        KeyEvent[] events = null;
+        if (count == 1) {
+            TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0);
+            KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+            events = kmap.getEvents(mCharacter);
+        }
+        boolean useKeyEvents = (events != null);
+        if (useKeyEvents) {
             // This corrects the selection which may have been affected by the
             // trackball or auto-correct.
             if (DebugFlags.WEB_TEXT_VIEW) {
@@ -633,8 +641,6 @@
             if (!mInSetTextAndKeepSelection) {
                 mWebView.setSelection(start, start + before);
             }
-        }
-        if (!cannotUseKeyEvents) {
             int length = events.length;
             for (int i = 0; i < length; i++) {
                 // We never send modifier keys to native code so don't send them
@@ -643,6 +649,12 @@
                     sendDomEvent(events[i]);
                 }
             }
+        } else {
+            String replace = s.subSequence(start,
+                    start + count).toString();
+            mWebView.replaceTextfieldText(start, start + before, replace,
+                    start + count,
+                    start + count);
         }
         updateCachedTextfield();
     }
@@ -966,8 +978,11 @@
      * @param   text    The new text to place in the textfield.
      */
     /* package */ void setTextAndKeepSelection(String text) {
-        mPreChange = text.toString();
         Editable edit = getText();
+        mPreChange = text;
+        if (edit.toString().equals(text)) {
+            return;
+        }
         int selStart = Selection.getSelectionStart(edit);
         int selEnd = Selection.getSelectionEnd(edit);
         mInSetTextAndKeepSelection = true;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 71ba7eb..a814b12 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2995,8 +2995,8 @@
     /**
      * Update the double-tap zoom.
      */
-    /* package */ void updateDoubleTapZoom() {
-        mZoomManager.updateDoubleTapZoom();
+    /* package */ void updateDoubleTapZoom(int doubleTapZoom) {
+        mZoomManager.updateDoubleTapZoom(doubleTapZoom);
     }
 
     private int computeRealHorizontalScrollRange() {
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 9151fdd..7d3cf8e 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -152,6 +152,12 @@
     private float mDisplayDensity;
 
     /*
+     * The factor that is used to tweak the zoom scale on a double-tap,
+     * and can be changed via WebSettings. Range is from 0.75f to 1.25f.
+     */
+    private float mDoubleTapZoomFactor = 1.0f;
+
+    /*
      * The scale factor that is used as the minimum increment when going from
      * overview to reading level on a double tap.
      */
@@ -314,10 +320,7 @@
      * Returns the zoom scale used for reading text on a double-tap.
      */
     public final float getReadingLevelScale() {
-        WebSettings settings = mWebView.getSettings();
-        final float doubleTapZoomFactor = settings != null
-            ? settings.getDoubleTapZoom() / 100.f : 1.0f;
-        return mDisplayDensity * doubleTapZoomFactor;
+        return mDisplayDensity * mDoubleTapZoomFactor;
     }
 
     public final float getInvDefaultScale() {
@@ -516,8 +519,9 @@
         return mZoomScale != 0 || mInHWAcceleratedZoom;
     }
 
-    public void updateDoubleTapZoom() {
+    public void updateDoubleTapZoom(int doubleTapZoom) {
         if (mInZoomOverview) {
+            mDoubleTapZoomFactor = doubleTapZoom / 100.0f;
             mTextWrapScale = getReadingLevelScale();
             refreshZoomScale(true);
         }
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 0cd14d0..03e6e99 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -55,7 +55,6 @@
      * Default animation parameters
      */
     private static final int DEFAULT_ANIMATION_DURATION = 400;
-    private static final int FADE_IN_ANIMATION_DURATION = 800;
     private static final int MINIMUM_ANIMATION_DURATION = 50;
     private static final int STACK_RELAYOUT_DURATION = 100;
 
@@ -222,8 +221,6 @@
      * Animate the views between different relative indexes within the {@link AdapterViewAnimator}
      */
     void transformViewForTransition(int fromIndex, int toIndex, final View view, boolean animate) {
-        ObjectAnimator alphaOa;
-
         if (!animate) {
             ((StackFrame) view).cancelSliderAnimator();
             view.setRotationX(0f);
@@ -233,22 +230,9 @@
         }
 
         if (fromIndex == -1 && toIndex == getNumActiveViews() -1) {
-            // Fade item in
-            if (view.getAlpha() == 1) {
-                view.setAlpha(0);
-            }
             transformViewAtIndex(toIndex, view, false);
             view.setVisibility(VISIBLE);
-
-            ((StackFrame) view).cancelAlphaAnimator();
-            if (animate) {
-                alphaOa = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 1.0f);
-                alphaOa.setDuration(FADE_IN_ANIMATION_DURATION);
-                ((StackFrame) view).setAlphaAnimator(alphaOa);
-                alphaOa.start();
-            } else {
-                view.setAlpha(1.0f);
-            }
+            view.setAlpha(1.0f);
         } else if (fromIndex == 0 && toIndex == 1) {
             // Slide item in
             ((StackFrame) view).cancelSliderAnimator();
@@ -306,13 +290,12 @@
             view.setAlpha(1.0f);
             view.setVisibility(VISIBLE);
         } else if (toIndex == -1) {
-            // Fade item out
-            ((StackFrame) view).cancelAlphaAnimator();
             if (animate) {
-                alphaOa = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 0.0f);
-                alphaOa.setDuration(STACK_RELAYOUT_DURATION);
-                ((StackFrame) view).setAlphaAnimator(alphaOa);
-                alphaOa.start();
+                postDelayed(new Runnable() {
+                    public void run() {
+                        view.setAlpha(0);
+                    }
+                }, STACK_RELAYOUT_DURATION);
             } else {
                 view.setAlpha(0f);
             }
@@ -485,7 +468,6 @@
     }
 
     private static class StackFrame extends FrameLayout {
-        WeakReference<ObjectAnimator> alphaAnimator;
         WeakReference<ObjectAnimator> transformAnimator;
         WeakReference<ObjectAnimator> sliderAnimator;
 
@@ -493,10 +475,6 @@
             super(context);
         }
 
-        void setAlphaAnimator(ObjectAnimator oa) {
-            alphaAnimator = new WeakReference<ObjectAnimator>(oa);
-        }
-
         void setTransformAnimator(ObjectAnimator oa) {
             transformAnimator = new WeakReference<ObjectAnimator>(oa);
         }
@@ -505,17 +483,6 @@
             sliderAnimator = new WeakReference<ObjectAnimator>(oa);
         }
 
-        boolean cancelAlphaAnimator() {
-            if (alphaAnimator != null) {
-                ObjectAnimator oa = alphaAnimator.get();
-                if (oa != null) {
-                    oa.cancel();
-                    return true;
-                }
-            }
-            return false;
-        }
-
         boolean cancelTransformAnimator() {
             if (transformAnimator != null) {
                 ObjectAnimator oa = transformAnimator.get();
diff --git a/core/res/res/drawable-hdpi/ic_suggestions_add.png b/core/res/res/drawable-hdpi/ic_suggestions_add.png
index 79c017a..919872c 100644
--- a/core/res/res/drawable-hdpi/ic_suggestions_add.png
+++ b/core/res/res/drawable-hdpi/ic_suggestions_add.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_suggestions_delete.png b/core/res/res/drawable-hdpi/ic_suggestions_delete.png
index 1ae6b96..fa42db0 100644
--- a/core/res/res/drawable-hdpi/ic_suggestions_delete.png
+++ b/core/res/res/drawable-hdpi/ic_suggestions_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_paste_window.9.png b/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
index b74f37b..8a64d36 100644
--- a/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
+++ b/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_side_paste_window.9.png b/core/res/res/drawable-hdpi/text_edit_side_paste_window.9.png
index c6adea3..2b50efa 100644
--- a/core/res/res/drawable-hdpi/text_edit_side_paste_window.9.png
+++ b/core/res/res/drawable-hdpi/text_edit_side_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png b/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png
index b74f37b..8a64d36 100644
--- a/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png
+++ b/core/res/res/drawable-hdpi/text_edit_suggestions_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_suggestions_add.png b/core/res/res/drawable-mdpi/ic_suggestions_add.png
index f91951b..e98bdf8 100644
--- a/core/res/res/drawable-mdpi/ic_suggestions_add.png
+++ b/core/res/res/drawable-mdpi/ic_suggestions_add.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_suggestions_delete.png b/core/res/res/drawable-mdpi/ic_suggestions_delete.png
index 98eb565..78e6ec1 100644
--- a/core/res/res/drawable-mdpi/ic_suggestions_delete.png
+++ b/core/res/res/drawable-mdpi/ic_suggestions_delete.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_paste_window.9.png b/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
index 16f623d..caacb5a 100644
--- a/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
+++ b/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_side_paste_window.9.png b/core/res/res/drawable-mdpi/text_edit_side_paste_window.9.png
index 23684fa..04300d4 100644
--- a/core/res/res/drawable-mdpi/text_edit_side_paste_window.9.png
+++ b/core/res/res/drawable-mdpi/text_edit_side_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png b/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png
index 16f623d..caacb5a 100644
--- a/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png
+++ b/core/res/res/drawable-mdpi/text_edit_suggestions_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-nodpi/text_cursor_holo_dark.9.png
index 450c486..a1bddc3 100644
--- a/core/res/res/drawable-nodpi/text_cursor_holo_dark.9.png
+++ b/core/res/res/drawable-nodpi/text_cursor_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/text_cursor_holo_light.9.png b/core/res/res/drawable-nodpi/text_cursor_holo_light.9.png
index 22633eb0..cfdb849 100644
--- a/core/res/res/drawable-nodpi/text_cursor_holo_light.9.png
+++ b/core/res/res/drawable-nodpi/text_cursor_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_suggestions_add.png b/core/res/res/drawable-xhdpi/ic_suggestions_add.png
index aac038c..b1edef7 100644
--- a/core/res/res/drawable-xhdpi/ic_suggestions_add.png
+++ b/core/res/res/drawable-xhdpi/ic_suggestions_add.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_suggestions_delete.png b/core/res/res/drawable-xhdpi/ic_suggestions_delete.png
index 077e9fc..6b1f447 100644
--- a/core/res/res/drawable-xhdpi/ic_suggestions_delete.png
+++ b/core/res/res/drawable-xhdpi/ic_suggestions_delete.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_edit_paste_window.9.png b/core/res/res/drawable-xhdpi/text_edit_paste_window.9.png
index 5c043b6..a6e199a 100644
--- a/core/res/res/drawable-xhdpi/text_edit_paste_window.9.png
+++ b/core/res/res/drawable-xhdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_edit_side_paste_window.9.png b/core/res/res/drawable-xhdpi/text_edit_side_paste_window.9.png
index ac10b3e..f96ff01 100644
--- a/core/res/res/drawable-xhdpi/text_edit_side_paste_window.9.png
+++ b/core/res/res/drawable-xhdpi/text_edit_side_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_edit_suggestions_window.9.png b/core/res/res/drawable-xhdpi/text_edit_suggestions_window.9.png
index 5c043b6..a6e199a 100644
--- a/core/res/res/drawable-xhdpi/text_edit_suggestions_window.9.png
+++ b/core/res/res/drawable-xhdpi/text_edit_suggestions_window.9.png
Binary files differ
diff --git a/docs/html/sdk/android-4.0-highlights.jd b/docs/html/sdk/android-4.0-highlights.jd
index d6a2dcd..c1162d4 100644
--- a/docs/html/sdk/android-4.0-highlights.jd
+++ b/docs/html/sdk/android-4.0-highlights.jd
@@ -176,7 +176,7 @@
 <p style="margin-top:1em;margin-bottom:.75em;"><strong>Swipe to dismiss
 notifications, tasks, and browser tabs</strong></p>
 
-<p>Android 4.0 makes managing notifications, recent apps, and brwoser tabs even
+<p>Android 4.0 makes managing notifications, recent apps, and browser tabs even
 easier. Users can now dismiss individual notifications, apps from the Recent
 Apps list, and browser tabs with a simple swipe of a finger. </p>
 
diff --git a/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png
deleted file mode 100644
index e14111d..0000000
--- a/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/screenshot_panel.9.png b/packages/SystemUI/res/drawable-hdpi/screenshot_panel.9.png
new file mode 100644
index 0000000..9447e01
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/screenshot_panel.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png
deleted file mode 100644
index e14111d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/screenshot_panel.9.png b/packages/SystemUI/res/drawable-mdpi/screenshot_panel.9.png
new file mode 100644
index 0000000..7f1aea1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/screenshot_panel.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-xhdpi/global_screenshot_background.9.png
deleted file mode 100644
index db116b1..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/global_screenshot_background.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/screenshot_panel.9.png b/packages/SystemUI/res/drawable-xhdpi/screenshot_panel.9.png
new file mode 100644
index 0000000..e5cfc36
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/screenshot_panel.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 6d70135..d416af9 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -26,11 +26,16 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:background="@drawable/global_screenshot_background"
+        android:background="@drawable/screenshot_panel"
         android:visibility="gone">
         <ImageView android:id="@+id/global_screenshot"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:adjustViewBounds="true" />
     </FrameLayout>
+    <ImageView android:id="@+id/global_screenshot_flash"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#FFFFFFFF"
+        android:visibility="gone" />
 </FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bf19286..55b722b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -89,9 +89,6 @@
     <dimen name="collapse_accel">2000dp</dimen>
 
     <!-- The padding on the global screenshot background image -->
-    <dimen name="global_screenshot_bg_padding">0dp</dimen>
-    <!-- The top-left offset for the screenshot drop animation target bounds -->
-    <dimen name="global_screenshot_drop_offset_x">6dp</dimen>
-    <dimen name="global_screenshot_drop_offset_y">0dp</dimen>
+    <dimen name="global_screenshot_bg_padding">20dp</dimen>
 
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index cc1b8ed..6549610 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -34,7 +34,6 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.PointF;
-import android.graphics.RectF;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Environment;
@@ -52,6 +51,7 @@
 import android.view.WindowManager;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
@@ -229,15 +229,18 @@
 class GlobalScreenshot {
     private static final String TAG = "GlobalScreenshot";
     private static final int SCREENSHOT_NOTIFICATION_ID = 789;
-    private static final int SCREENSHOT_FADE_IN_DURATION = 250;
-    private static final int SCREENSHOT_FADE_OUT_DELAY = 750;
-    private static final int SCREENSHOT_FADE_OUT_DURATION = 500;
-    private static final int SCREENSHOT_FAST_FADE_OUT_DURATION = 350;
-    private static final float BACKGROUND_ALPHA = 0.65f;
-    private static final float SCREENSHOT_SCALE_FUDGE = 0.075f; // To account for the border padding
-    private static final float SCREENSHOT_SCALE = 0.55f;
-    private static final float SCREENSHOT_FADE_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.975f;
-    private static final float SCREENSHOT_FADE_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.925f;
+    private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
+    private static final int SCREENSHOT_DROP_IN_DURATION = 430;
+    private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
+    private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
+    private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
+    private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;
+    private static final float BACKGROUND_ALPHA = 0.5f;
+    private static final float SCREENSHOT_SCALE = 1f;
+    private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;
+    private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;
+    private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;
+    private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;
 
     private Context mContext;
     private LayoutInflater mLayoutInflater;
@@ -254,13 +257,11 @@
     private ImageView mBackgroundView;
     private FrameLayout mScreenshotContainerView;
     private ImageView mScreenshotView;
+    private ImageView mScreenshotFlash;
 
     private AnimatorSet mScreenshotAnimation;
 
-    private int mStatusBarIconSize;
     private int mNotificationIconSize;
-    private float mDropOffsetX;
-    private float mDropOffsetY;
     private float mBgPadding;
     private float mBgPaddingScale;
 
@@ -280,6 +281,7 @@
         mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
         mScreenshotContainerView = (FrameLayout) mScreenshotLayout.findViewById(R.id.global_screenshot_container);
         mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
+        mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
         mScreenshotLayout.setFocusable(true);
         mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {
             @Override
@@ -309,16 +311,12 @@
         mDisplay.getRealMetrics(mDisplayMetrics);
 
         // Get the various target sizes
-        mStatusBarIconSize =
-            r.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
         mNotificationIconSize =
             r.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
-        mDropOffsetX = r.getDimensionPixelSize(R.dimen.global_screenshot_drop_offset_x);
-        mDropOffsetY = r.getDimensionPixelSize(R.dimen.global_screenshot_drop_offset_y);
 
         // Scale has to account for both sides of the bg
         mBgPadding = (float) r.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding);
-        mBgPaddingScale = (2f * mBgPadding) /  mDisplayMetrics.widthPixels;
+        mBgPaddingScale = mBgPadding /  mDisplayMetrics.widthPixels;
     }
 
     /**
@@ -413,11 +411,11 @@
         }
 
         mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
-        ValueAnimator screenshotFadeInAnim = createScreenshotFadeInAnimation();
-        ValueAnimator screenshotFadeOutAnim = createScreenshotFadeOutAnimation(w, h,
+        ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
+        ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
                 statusBarVisible, navBarVisible);
         mScreenshotAnimation = new AnimatorSet();
-        mScreenshotAnimation.play(screenshotFadeInAnim).before(screenshotFadeOutAnim);
+        mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
         mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -435,42 +433,71 @@
             }
         });
     }
-    private ValueAnimator createScreenshotFadeInAnimation() {
+    private ValueAnimator createScreenshotDropInAnimation() {
+        final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
+                / SCREENSHOT_DROP_IN_DURATION);
+        final float flashDurationPct = 2f * flashPeakDurationPct;
+        final Interpolator flashAlphaInterpolator = new Interpolator() {
+            @Override
+            public float getInterpolation(float x) {
+                // Flash the flash view in and out quickly
+                if (x <= flashDurationPct) {
+                    return (float) Math.sin(Math.PI * (x / flashDurationPct));
+                }
+                return 0;
+            }
+        };
+        final Interpolator scaleInterpolator = new Interpolator() {
+            @Override
+            public float getInterpolation(float x) {
+                // We start scaling when the flash is at it's peak
+                if (x < flashPeakDurationPct) {
+                    return 0;
+                }
+                return (x - flashDurationPct) / (1f - flashDurationPct);
+            }
+        };
         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.setInterpolator(new AccelerateInterpolator(1.5f));
-        anim.setDuration(SCREENSHOT_FADE_IN_DURATION);
+        anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 mBackgroundView.setAlpha(0f);
                 mBackgroundView.setVisibility(View.VISIBLE);
+                mScreenshotContainerView.setAlpha(0f);
                 mScreenshotContainerView.setTranslationX(0f);
                 mScreenshotContainerView.setTranslationY(0f);
-                mScreenshotContainerView.setScaleX(SCREENSHOT_FADE_IN_MIN_SCALE);
-                mScreenshotContainerView.setScaleY(SCREENSHOT_FADE_IN_MIN_SCALE);
-                mScreenshotContainerView.setAlpha(0f);
+                mScreenshotContainerView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
+                mScreenshotContainerView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
                 mScreenshotContainerView.setVisibility(View.VISIBLE);
+                mScreenshotFlash.setAlpha(0f);
+                mScreenshotFlash.setVisibility(View.VISIBLE);
+            }
+            @Override
+            public void onAnimationEnd(android.animation.Animator animation) {
+                mScreenshotFlash.setVisibility(View.GONE);
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 float t = ((Float) animation.getAnimatedValue()).floatValue();
-                float scaleT = (SCREENSHOT_FADE_IN_MIN_SCALE)
-                    + (float) t * (SCREENSHOT_SCALE - SCREENSHOT_FADE_IN_MIN_SCALE);
-                mBackgroundView.setAlpha(t * BACKGROUND_ALPHA);
+                float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
+                    - (float) scaleInterpolator.getInterpolation(t)
+                        * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
+                mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
+                mScreenshotContainerView.setAlpha(t);
                 mScreenshotContainerView.setScaleX(scaleT);
                 mScreenshotContainerView.setScaleY(scaleT);
-                mScreenshotContainerView.setAlpha(t);
+                mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
             }
         });
         return anim;
     }
-    private ValueAnimator createScreenshotFadeOutAnimation(int w, int h, boolean statusBarVisible,
+    private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
             boolean navBarVisible) {
         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.setInterpolator(new DecelerateInterpolator(0.5f));
-        anim.setStartDelay(SCREENSHOT_FADE_OUT_DELAY);
+        anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -482,54 +509,58 @@
 
         if (!statusBarVisible || !navBarVisible) {
             // There is no status bar/nav bar, so just fade the screenshot away in place
-            anim.setDuration(SCREENSHOT_FAST_FADE_OUT_DURATION);
+            anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
             anim.addUpdateListener(new AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
                     float t = ((Float) animation.getAnimatedValue()).floatValue();
-                    float scaleT = (SCREENSHOT_FADE_OUT_MIN_SCALE)
-                            + (float) (1f - t) * (SCREENSHOT_SCALE - SCREENSHOT_FADE_OUT_MIN_SCALE);
+                    float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
+                            - (float) t * (SCREENSHOT_DROP_IN_MIN_SCALE
+                                    - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
                     mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
-                    mScreenshotContainerView.setAlpha((1f - t) * BACKGROUND_ALPHA);
+                    mScreenshotContainerView.setAlpha(1f - t);
                     mScreenshotContainerView.setScaleX(scaleT);
                     mScreenshotContainerView.setScaleY(scaleT);
                 }
             });
         } else {
+            // In the case where there is a status bar, animate to the origin of the bar (top-left)
+            final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
+                    / SCREENSHOT_DROP_OUT_DURATION;
+            final Interpolator scaleInterpolator = new Interpolator() {
+                @Override
+                public float getInterpolation(float x) {
+                    if (x < scaleDurationPct) {
+                        // Decelerate, and scale the input accordingly
+                        return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
+                    }
+                    return 1f;
+                }
+            };
+
             // Determine the bounds of how to scale
             float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
             float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
-            final RectF finalBounds = new RectF(mDropOffsetX, mDropOffsetY,
-                    mDropOffsetX + mStatusBarIconSize,
-                    mDropOffsetY + mStatusBarIconSize);
-            final PointF currentPos = new PointF(0f, 0f);
-            final PointF finalPos = new PointF(-halfScreenWidth + finalBounds.centerX(),
-                    -halfScreenHeight + finalBounds.centerY());
-            final DecelerateInterpolator d = new DecelerateInterpolator(2f);
-            // Note: since the scale origin is in the center of the view, divide difference by 2
-            float tmpMinScale = 0f;
-            if (w > h) {
-                tmpMinScale = finalBounds.width() / (2f * w);
-            } else {
-                tmpMinScale = finalBounds.height() / (2f * h);
-            }
-            final float minScale = tmpMinScale;
+            final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
+            final PointF finalPos = new PointF(
+                -halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
+                -halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
 
             // Animate the screenshot to the status bar
-            anim.setDuration(SCREENSHOT_FADE_OUT_DURATION);
+            anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
             anim.addUpdateListener(new AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
                     float t = ((Float) animation.getAnimatedValue()).floatValue();
-                    float scaleT = minScale
-                            + (float) (1f - t) * (SCREENSHOT_SCALE - minScale - mBgPaddingScale)
-                            + mBgPaddingScale;
-                    mScreenshotContainerView.setAlpha(d.getInterpolation(1f - t));
-                    mScreenshotContainerView.setTranslationX(d.getInterpolation(t) * finalPos.x);
-                    mScreenshotContainerView.setTranslationY(d.getInterpolation(t) * finalPos.y);
+                    float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
+                        - (float) scaleInterpolator.getInterpolation(t)
+                            * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
+                    mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
+                    mScreenshotContainerView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
                     mScreenshotContainerView.setScaleX(scaleT);
                     mScreenshotContainerView.setScaleY(scaleT);
-                    mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
+                    mScreenshotContainerView.setTranslationX(t * finalPos.x);
+                    mScreenshotContainerView.setTranslationY(t * finalPos.y);
                 }
             });
         }
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 994201b..99dcd9b 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -264,7 +264,7 @@
     // could be either static or controllable at runtime
     private static final boolean mSpew = false;
     private static final boolean mDebugProximitySensor = (false || mSpew);
-    private static final boolean mDebugLightSensor = (false || mSpew);
+    private static final boolean mDebugLightSensor = (true || mSpew);
     
     private native void nativeInit();
     private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index a653322..1976eba 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -701,7 +701,7 @@
         public void onServiceDisconnected(ComponentName name) {
             synchronized(mSpellCheckerMap) {
                 final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
-                if (this == group.mInternalConnection) {
+                if (group != null && this == group.mInternalConnection) {
                     mSpellCheckerBindGroups.remove(mSciId);
                 }
             }
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index a0e28ed..7fa404e 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -102,7 +102,7 @@
      * everytime the wallpaper is changed.
      */
     private final FileObserver mWallpaperObserver = new FileObserver(
-            WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE | DELETE | DELETE_SELF) {
+            WALLPAPER_DIR.getAbsolutePath(), CLOSE_WRITE | DELETE | DELETE_SELF) {
                 @Override
                 public void onEvent(int event, String path) {
                     if (path == null) {
@@ -118,8 +118,11 @@
                         File changedFile = new File(WALLPAPER_DIR, path);
                         if (WALLPAPER_FILE.equals(changedFile)) {
                             notifyCallbacksLocked();
-                            if (mWallpaperComponent == null || mImageWallpaperPending) {
-                                mImageWallpaperPending = false;
+                            if (mWallpaperComponent == null || event != CLOSE_WRITE
+                                    || mImageWallpaperPending) {
+                                if (event == CLOSE_WRITE) {
+                                    mImageWallpaperPending = false;
+                                }
                                 bindWallpaperComponentLocked(mImageWallpaperComponent,
                                         true, false);
                                 saveSettingsLocked();
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index 84bfc40..51ebd99 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -57,7 +57,6 @@
     protected int mailboxIndex = 0; // 0 is no mailbox dailing number associated
 
     protected String spn;
-    protected int spnDisplayCondition;
 
     // ***** Constants
 
diff --git a/telephony/java/com/android/internal/telephony/IccServiceTable.java b/telephony/java/com/android/internal/telephony/IccServiceTable.java
new file mode 100644
index 0000000..ed74a11
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IccServiceTable.java
@@ -0,0 +1,81 @@
+/*
+ * 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.util.Log;
+
+/**
+ * Wrapper class for an ICC EF containing a bit field of enabled services.
+ */
+public abstract class IccServiceTable {
+    protected final byte[] mServiceTable;
+
+    protected IccServiceTable(byte[] table) {
+        mServiceTable = table;
+    }
+
+    // Get the class name to use for log strings
+    protected abstract String getTag();
+
+    // Get the array of enums to use for toString
+    protected abstract Object[] getValues();
+
+    /**
+     * Returns if the specified service is available.
+     * @param service the service number as a zero-based offset (the enum ordinal)
+     * @return true if the service is available; false otherwise
+     */
+    protected boolean isAvailable(int service) {
+        int offset = service / 8;
+        if (offset >= mServiceTable.length) {
+            // Note: Enums are zero-based, but the TS service numbering is one-based
+            Log.e(getTag(), "isAvailable for service " + (service + 1) + " fails, max service is " +
+                    (mServiceTable.length * 8));
+            return false;
+        }
+        int bit = service % 8;
+        return (mServiceTable[offset] & (1 << bit)) != 0;
+    }
+
+    public String toString() {
+        Object[] values = getValues();
+        int numBytes = mServiceTable.length;
+        StringBuilder builder = new StringBuilder(getTag()).append('[')
+                .append(numBytes * 8).append("]={ ");
+
+        boolean addComma = false;
+        for (int i = 0; i < numBytes; i++) {
+            byte currentByte = mServiceTable[i];
+            for (int bit = 0; bit < 8; bit++) {
+                if ((currentByte & (1 << bit)) != 0) {
+                    if (addComma) {
+                        builder.append(", ");
+                    } else {
+                        addComma = true;
+                    }
+                    int ordinal = (i * 8) + bit;
+                    if (ordinal < values.length) {
+                        builder.append(values[ordinal]);
+                    } else {
+                        builder.append('#').append(ordinal + 1);    // service number (one-based)
+                    }
+                }
+            }
+        }
+        return builder.append(" }").toString();
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
index 47c638f..0a285b9 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
@@ -282,6 +282,9 @@
                 obtainMessage(EVENT_GET_MSISDN_DONE));
         recordsToLoad++;
 
+        iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
+        recordsToLoad++;
+
         iccFh.loadEFTransparent(EF_CSIM_LI,
                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
         recordsToLoad++;
@@ -384,12 +387,12 @@
 
     @Override
     protected void log(String s) {
-        if (DBG) Log.d(LOG_TAG, "[CSIM] " + s);
+        Log.d(LOG_TAG, "[CSIM] " + s);
     }
 
     @Override
     protected void loge(String s) {
-        if (DBG) Log.e(LOG_TAG, "[CSIM] " + s);
+        Log.e(LOG_TAG, "[CSIM] " + s);
     }
 
     public String getMdn() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 5d6f181..8e965a3 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -91,6 +91,8 @@
 
     String pnnHomeName = null;
 
+    UsimServiceTable mUsimServiceTable;
+
     // ***** Constants
 
     // Bitmasks for SPN display rules.
@@ -134,7 +136,7 @@
     private static final int EVENT_GET_SPDI_DONE = 13;
     private static final int EVENT_UPDATE_DONE = 14;
     private static final int EVENT_GET_PNN_DONE = 15;
-    private static final int EVENT_GET_SST_DONE = 17;
+    protected static final int EVENT_GET_SST_DONE = 17;
     private static final int EVENT_GET_ALL_SMS_DONE = 18;
     private static final int EVENT_MARK_SMS_READ_DONE = 19;
     private static final int EVENT_SET_MBDN_DONE = 20;
@@ -246,6 +248,10 @@
         return msisdn;
     }
 
+    public UsimServiceTable getUsimServiceTable() {
+        return mUsimServiceTable;
+    }
+
     /**
      * Set subscriber number to SIM record
      *
@@ -961,8 +967,9 @@
                     break;
                 }
 
-                //Log.d(LOG_TAG, "SST: " + IccUtils.bytesToHexString(data));
-            break;
+                mUsimServiceTable = new UsimServiceTable(data);
+                if (DBG) log("SST: " + mUsimServiceTable);
+                break;
 
             case EVENT_GET_INFO_CPHS_DONE:
                 isRecordLoadResponse = true;
diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimServiceTable.java b/telephony/java/com/android/internal/telephony/gsm/UsimServiceTable.java
new file mode 100644
index 0000000..3fe200b
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/gsm/UsimServiceTable.java
@@ -0,0 +1,142 @@
+/*
+ * 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.gsm;
+
+import com.android.internal.telephony.IccServiceTable;
+
+/**
+ * Wrapper class for the USIM Service Table EF.
+ * See 3GPP TS 31.102 Release 10 section 4.2.8
+ */
+public final class UsimServiceTable extends IccServiceTable {
+    public enum UsimService {
+        PHONEBOOK,
+        FDN,                                // Fixed Dialing Numbers
+        FDN_EXTENSION,                      // FDN extension data in EF_EXT2
+        SDN,                                // Service Dialing Numbers
+        SDN_EXTENSION,                      // SDN extension data in EF_EXT3
+        BDN,                                // Barred Dialing Numbers
+        BDN_EXTENSION,                      // BDN extension data in EF_EXT4
+        OUTGOING_CALL_INFO,
+        INCOMING_CALL_INFO,
+        SM_STORAGE,
+        SM_STATUS_REPORTS,
+        SM_SERVICE_PARAMS,
+        ADVICE_OF_CHARGE,
+        CAP_CONFIG_PARAMS_2,
+        CB_MESSAGE_ID,
+        CB_MESSAGE_ID_RANGES,
+        GROUP_ID_LEVEL_1,
+        GROUP_ID_LEVEL_2,
+        SPN,                                // Service Provider Name
+        USER_PLMN_SELECT,
+        MSISDN,
+        IMAGE,
+        LOCALISED_SERVICE_AREAS,
+        EMLPP,                              // Enhanced Multi-Level Precedence and Preemption
+        EMLPP_AUTO_ANSWER,
+        RFU,
+        GSM_ACCESS,
+        DATA_DL_VIA_SMS_PP,
+        DATA_DL_VIA_SMS_CB,
+        CALL_CONTROL_BY_USIM,
+        MO_SMS_CONTROL_BY_USIM,
+        RUN_AT_COMMAND,
+        IGNORED_1,
+        ENABLED_SERVICES_TABLE,
+        APN_CONTROL_LIST,
+        DEPERSONALISATION_CONTROL_KEYS,
+        COOPERATIVE_NETWORK_LIST,
+        GSM_SECURITY_CONTEXT,
+        CPBCCH_INFO,
+        INVESTIGATION_SCAN,
+        MEXE,
+        OPERATOR_PLMN_SELECT,
+        HPLMN_SELECT,
+        EXTENSION_5,                        // Extension data for ICI, OCI, MSISDN in EF_EXT5
+        PLMN_NETWORK_NAME,
+        OPERATOR_PLMN_LIST,
+        MBDN,                               // Mailbox Dialing Numbers
+        MWI_STATUS,                         // Message Waiting Indication status
+        CFI_STATUS,                         // Call Forwarding Indication status
+        IGNORED_2,
+        SERVICE_PROVIDER_DISPLAY_INFO,
+        MMS_NOTIFICATION,
+        MMS_NOTIFICATION_EXTENSION,         // MMS Notification extension data in EF_EXT8
+        GPRS_CALL_CONTROL_BY_USIM,
+        MMS_CONNECTIVITY_PARAMS,
+        NETWORK_INDICATION_OF_ALERTING,
+        VGCS_GROUP_ID_LIST,
+        VBS_GROUP_ID_LIST,
+        PSEUDONYM,
+        IWLAN_USER_PLMN_SELECT,
+        IWLAN_OPERATOR_PLMN_SELECT,
+        USER_WSID_LIST,
+        OPERATOR_WSID_LIST,
+        VGCS_SECURITY,
+        VBS_SECURITY,
+        WLAN_REAUTH_IDENTITY,
+        MM_STORAGE,
+        GBA,                                // Generic Bootstrapping Architecture
+        MBMS_SECURITY,
+        DATA_DL_VIA_USSD,
+        EQUIVALENT_HPLMN,
+        TERMINAL_PROFILE_AFTER_UICC_ACTIVATION,
+        EQUIVALENT_HPLMN_PRESENTATION,
+        LAST_RPLMN_SELECTION_INDICATION,
+        OMA_BCAST_PROFILE,
+        GBA_LOCAL_KEY_ESTABLISHMENT,
+        TERMINAL_APPLICATIONS,
+        SPN_ICON,
+        PLMN_NETWORK_NAME_ICON,
+        USIM_IP_CONNECTION_PARAMS,
+        IWLAN_HOME_ID_LIST,
+        IWLAN_EQUIVALENT_HPLMN_PRESENTATION,
+        IWLAN_HPLMN_PRIORITY_INDICATION,
+        IWLAN_LAST_REGISTERED_PLMN,
+        EPS_MOBILITY_MANAGEMENT_INFO,
+        ALLOWED_CSG_LISTS_AND_INDICATIONS,
+        CALL_CONTROL_ON_EPS_PDN_CONNECTION_BY_USIM,
+        HPLMN_DIRECT_ACCESS,
+        ECALL_DATA,
+        OPERATOR_CSG_LISTS_AND_INDICATIONS,
+        SM_OVER_IP,
+        CSG_DISPLAY_CONTROL,
+        IMS_COMMUNICATION_CONTROL_BY_USIM,
+        EXTENDED_TERMINAL_APPLICATIONS,
+        UICC_ACCESS_TO_IMS,
+        NAS_CONFIG_BY_USIM
+    }
+
+    public UsimServiceTable(byte[] table) {
+        super(table);
+    }
+
+    public boolean isAvailable(UsimService service) {
+        return super.isAvailable(service.ordinal());
+    }
+
+    @Override
+    protected String getTag() {
+        return "UsimServiceTable";
+    }
+
+    @Override
+    protected Object[] getValues() {
+        return UsimService.values();
+    }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/IccServiceTableTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/IccServiceTableTest.java
new file mode 100644
index 0000000..c89f33a
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/IccServiceTableTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Test IccServiceTable class.
+ */
+public class IccServiceTableTest extends AndroidTestCase {
+
+    static class TestIccServiceTable extends IccServiceTable {
+        public enum TestIccService {
+            SERVICE1,
+            SERVICE2,
+            SERVICE3,
+            SERVICE4
+        }
+
+        public TestIccServiceTable(byte[] table) {
+            super(table);
+        }
+
+        public boolean isAvailable(TestIccService service) {
+            return super.isAvailable(service.ordinal());
+        }
+
+        @Override
+        protected String getTag() {
+            return "TestIccServiceTable";
+        }
+
+        @Override
+        protected Object[] getValues() {
+            return TestIccService.values();
+        }
+    }
+
+    @SmallTest
+    public void testIccServiceTable() {
+        byte[] noServices = {0x00};
+        byte[] service1 = {0x01};
+        byte[] service4 = {0x08};
+        byte[] allServices = {0x0f};
+
+        TestIccServiceTable testTable1 = new TestIccServiceTable(noServices);
+        assertFalse(testTable1.isAvailable(TestIccServiceTable.TestIccService.SERVICE1));
+        assertFalse(testTable1.isAvailable(TestIccServiceTable.TestIccService.SERVICE2));
+        assertFalse(testTable1.isAvailable(TestIccServiceTable.TestIccService.SERVICE3));
+        assertFalse(testTable1.isAvailable(TestIccServiceTable.TestIccService.SERVICE4));
+
+        TestIccServiceTable testTable2 = new TestIccServiceTable(service1);
+        assertTrue(testTable2.isAvailable(TestIccServiceTable.TestIccService.SERVICE1));
+        assertFalse(testTable2.isAvailable(TestIccServiceTable.TestIccService.SERVICE2));
+        assertFalse(testTable2.isAvailable(TestIccServiceTable.TestIccService.SERVICE3));
+        assertFalse(testTable2.isAvailable(TestIccServiceTable.TestIccService.SERVICE4));
+
+        TestIccServiceTable testTable3 = new TestIccServiceTable(service4);
+        assertFalse(testTable3.isAvailable(TestIccServiceTable.TestIccService.SERVICE1));
+        assertFalse(testTable3.isAvailable(TestIccServiceTable.TestIccService.SERVICE2));
+        assertFalse(testTable3.isAvailable(TestIccServiceTable.TestIccService.SERVICE3));
+        assertTrue(testTable3.isAvailable(TestIccServiceTable.TestIccService.SERVICE4));
+
+        TestIccServiceTable testTable4 = new TestIccServiceTable(allServices);
+        assertTrue(testTable4.isAvailable(TestIccServiceTable.TestIccService.SERVICE1));
+        assertTrue(testTable4.isAvailable(TestIccServiceTable.TestIccService.SERVICE2));
+        assertTrue(testTable4.isAvailable(TestIccServiceTable.TestIccService.SERVICE3));
+        assertTrue(testTable4.isAvailable(TestIccServiceTable.TestIccService.SERVICE4));
+    }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimServiceTableTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimServiceTableTest.java
new file mode 100644
index 0000000..56854ed
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimServiceTableTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.gsm;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Test UsimServiceTable class.
+ */
+public class UsimServiceTableTest extends AndroidTestCase {
+
+    @SmallTest
+    public void testUsimServiceTable() {
+        byte[] noServices = {0x00};
+        byte[] service1 = {0x01, 0x00};
+        byte[] service8 = {(byte) 0x80, 0x00, 0x00};
+        byte[] service8And9 = {(byte) 0x80, 0x01};
+        byte[] service28 = {0x00, 0x00, 0x00, 0x08};
+        byte[] service89To96 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, (byte) 0xff};
+
+        UsimServiceTable testTable1 = new UsimServiceTable(noServices);
+        assertFalse(testTable1.isAvailable(UsimServiceTable.UsimService.PHONEBOOK));
+        assertFalse(testTable1.isAvailable(UsimServiceTable.UsimService.FDN));
+        assertFalse(testTable1.isAvailable(UsimServiceTable.UsimService.NAS_CONFIG_BY_USIM));
+
+        UsimServiceTable testTable2 = new UsimServiceTable(service1);
+        assertTrue(testTable2.isAvailable(UsimServiceTable.UsimService.PHONEBOOK));
+        assertFalse(testTable2.isAvailable(UsimServiceTable.UsimService.FDN));
+        assertFalse(testTable2.isAvailable(UsimServiceTable.UsimService.NAS_CONFIG_BY_USIM));
+
+        UsimServiceTable testTable3 = new UsimServiceTable(service8);
+        assertFalse(testTable3.isAvailable(UsimServiceTable.UsimService.PHONEBOOK));
+        assertFalse(testTable3.isAvailable(UsimServiceTable.UsimService.BDN_EXTENSION));
+        assertTrue(testTable3.isAvailable(UsimServiceTable.UsimService.OUTGOING_CALL_INFO));
+        assertFalse(testTable3.isAvailable(UsimServiceTable.UsimService.INCOMING_CALL_INFO));
+        assertFalse(testTable3.isAvailable(UsimServiceTable.UsimService.NAS_CONFIG_BY_USIM));
+
+        UsimServiceTable testTable4 = new UsimServiceTable(service8And9);
+        assertFalse(testTable4.isAvailable(UsimServiceTable.UsimService.PHONEBOOK));
+        assertFalse(testTable4.isAvailable(UsimServiceTable.UsimService.BDN_EXTENSION));
+        assertTrue(testTable4.isAvailable(UsimServiceTable.UsimService.OUTGOING_CALL_INFO));
+        assertTrue(testTable4.isAvailable(UsimServiceTable.UsimService.INCOMING_CALL_INFO));
+        assertFalse(testTable4.isAvailable(UsimServiceTable.UsimService.SM_STORAGE));
+        assertFalse(testTable4.isAvailable(UsimServiceTable.UsimService.NAS_CONFIG_BY_USIM));
+
+        UsimServiceTable testTable5 = new UsimServiceTable(service28);
+        assertFalse(testTable5.isAvailable(UsimServiceTable.UsimService.PHONEBOOK));
+        assertTrue(testTable5.isAvailable(UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP));
+        assertFalse(testTable5.isAvailable(UsimServiceTable.UsimService.NAS_CONFIG_BY_USIM));
+
+        UsimServiceTable testTable6 = new UsimServiceTable(service89To96);
+        assertFalse(testTable6.isAvailable(UsimServiceTable.UsimService.PHONEBOOK));
+        assertFalse(testTable6.isAvailable(UsimServiceTable.UsimService.HPLMN_DIRECT_ACCESS));
+        assertTrue(testTable6.isAvailable(UsimServiceTable.UsimService.ECALL_DATA));
+        assertTrue(testTable6.isAvailable(UsimServiceTable.UsimService.SM_OVER_IP));
+        assertTrue(testTable6.isAvailable(UsimServiceTable.UsimService.UICC_ACCESS_TO_IMS));
+        assertTrue(testTable6.isAvailable(UsimServiceTable.UsimService.NAS_CONFIG_BY_USIM));
+    }
+}