Merge "Add webview tracing bit"
diff --git a/Android.mk b/Android.mk
index ab06058..3b2d32d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -329,7 +329,10 @@
 	 )
 
 # include definition of libcore_to_document
-include $(LOCAL_PATH)/../../libcore/Docs.mk
+include libcore/Docs.mk
+
+# include definition of junit_to_document
+include external/junit/Common.mk
 
 non_base_dirs := \
 	../../external/apache-http/src/org/apache/http
@@ -353,7 +356,8 @@
 # Common sources for doc check and api check
 common_src_files := \
 	$(call find-other-html-files, $(html_dirs)) \
-	$(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore))
+	$(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore)) \
+	$(addprefix ../../external/junit/, $(call junit_to_document, $(LOCAL_PATH)/../../external/junit))
 
 # These are relative to frameworks/base
 framework_docs_LOCAL_SRC_FILES := \
diff --git a/api/16.txt b/api/16.txt
index cc67785..9e9f880 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -9513,7 +9513,7 @@
     method public abstract void onSensorChanged(int, float[]);
   }
 
-  public class SensorManager {
+  public abstract class SensorManager {
     method public static float getAltitude(float, float);
     method public static void getAngleChange(float[], float[], float[]);
     method public android.hardware.Sensor getDefaultSensor(int);
diff --git a/api/current.txt b/api/current.txt
index cd47769..1bd7be0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -534,7 +534,7 @@
     field public static final int imeSubtypeLocale = 16843500; // 0x10102ec
     field public static final int imeSubtypeMode = 16843501; // 0x10102ed
     field public static final int immersive = 16843456; // 0x10102c0
-    field public static final int importantForAccessibility = 16843699; // 0x10103b3
+    field public static final int importantForAccessibility = 16843698; // 0x10103b2
     field public static final int inAnimation = 16843127; // 0x1010177
     field public static final int includeFontPadding = 16843103; // 0x101015f
     field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -936,7 +936,6 @@
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportsRtl = 16843688; // 0x10103a8
-    field public static final int supportsSentenceSpellCheck = 16843698; // 0x10103b2
     field public static final int supportsUploading = 16843419; // 0x101029b
     field public static final int switchMinWidth = 16843632; // 0x1010370
     field public static final int switchPadding = 16843633; // 0x1010371
@@ -2896,6 +2895,7 @@
   public class ActivityOptions {
     method public void join(android.app.ActivityOptions);
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
+    method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
     method public android.os.Bundle toBundle();
   }
@@ -9782,7 +9782,7 @@
     method public abstract void onSensorChanged(int, float[]);
   }
 
-  public class SensorManager {
+  public abstract class SensorManager {
     method public static float getAltitude(float, float);
     method public static void getAngleChange(float[], float[], float[]);
     method public android.hardware.Sensor getDefaultSensor(int);
@@ -25519,9 +25519,8 @@
     method public void close();
     method public void getSentenceSuggestions(android.view.textservice.TextInfo[], int);
     method public android.view.textservice.SpellCheckerInfo getSpellChecker();
-    method public void getSuggestions(android.view.textservice.TextInfo, int);
-    method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
-    method public boolean isSentenceSpellCheckSupported();
+    method public deprecated void getSuggestions(android.view.textservice.TextInfo, int);
+    method public deprecated void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
     method public boolean isSessionDisconnected();
     field public static final java.lang.String SERVICE_META_DATA = "android.view.textservice.scs";
   }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index c3cceaf..423b02a 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -73,6 +73,18 @@
     public static final String KEY_ANIM_START_Y = "android:animStartY";
 
     /**
+     * Initial width of the animation.
+     * @hide
+     */
+    public static final String KEY_ANIM_START_WIDTH = "android:animStartWidth";
+
+    /**
+     * Initial height of the animation.
+     * @hide
+     */
+    public static final String KEY_ANIM_START_HEIGHT = "android:animStartHeight";
+
+    /**
      * Callback for when animation is started.
      * @hide
      */
@@ -83,7 +95,9 @@
     /** @hide */
     public static final int ANIM_CUSTOM = 1;
     /** @hide */
-    public static final int ANIM_THUMBNAIL = 2;
+    public static final int ANIM_SCALE_UP = 2;
+    /** @hide */
+    public static final int ANIM_THUMBNAIL = 3;
 
     private String mPackageName;
     private int mAnimationType = ANIM_NONE;
@@ -92,6 +106,8 @@
     private Bitmap mThumbnail;
     private int mStartX;
     private int mStartY;
+    private int mStartWidth;
+    private int mStartHeight;
     private IRemoteCallback mAnimationStartedListener;
 
     /**
@@ -127,6 +143,34 @@
     }
 
     /**
+     * Create an ActivityOptions specifying an animation where the new
+     * activity is scaled from a small originating area of the screen to
+     * its final full representation.
+     *
+     * @param source The View that the new activity is animating from.  This
+     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
+     * @param startX The x starting location of the new activity, relative to <var>source</var>.
+     * @param startY The y starting location of the activity, relative to <var>source</var>.
+     * @param startWidth The initial width of the new activity.
+     * @param startWidth The initial height of the new activity.
+     * @return Returns a new ActivityOptions object that you can use to
+     * supply these options as the options Bundle when starting an activity.
+     */
+    public static ActivityOptions makeScaleUpAnimation(View source,
+            int startX, int startY, int startWidth, int startHeight) {
+        ActivityOptions opts = new ActivityOptions();
+        opts.mPackageName = source.getContext().getPackageName();
+        opts.mAnimationType = ANIM_SCALE_UP;
+        int[] pts = new int[2];
+        source.getLocationOnScreen(pts);
+        opts.mStartX = pts[0] + startX;
+        opts.mStartY = pts[1] + startY;
+        opts.mStartWidth = startWidth;
+        opts.mStartHeight = startHeight;
+        return opts;
+    }
+
+    /**
      * Create an ActivityOptions specifying an animation where a thumbnail
      * is scaled from a given position to the new activity window that is
      * being started.
@@ -135,8 +179,8 @@
      * defines the coordinate space for <var>startX</var> and <var>startY</var>.
      * @param thumbnail The bitmap that will be shown as the initial thumbnail
      * of the animation.
-     * @param startX The x starting location of the bitmap, in screen coordiantes.
-     * @param startY The y starting location of the bitmap, in screen coordinates.
+     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
+     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
      * @return Returns a new ActivityOptions object that you can use to
      * supply these options as the options Bundle when starting an activity.
      */
@@ -154,8 +198,8 @@
      * defines the coordinate space for <var>startX</var> and <var>startY</var>.
      * @param thumbnail The bitmap that will be shown as the initial thumbnail
      * of the animation.
-     * @param startX The x starting location of the bitmap, in screen coordiantes.
-     * @param startY The y starting location of the bitmap, in screen coordinates.
+     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
+     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
      * @param listener Optional OnAnimationStartedListener to find out when the
      * requested animation has started running.  If for some reason the animation
      * is not executed, the callback will happen immediately.
@@ -199,6 +243,11 @@
         if (mAnimationType == ANIM_CUSTOM) {
             mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
             mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
+        } else if (mAnimationType == ANIM_SCALE_UP) {
+            mStartX = opts.getInt(KEY_ANIM_START_X, 0);
+            mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
+            mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0);
+            mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0);
         } else if (mAnimationType == ANIM_THUMBNAIL) {
             mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
             mStartX = opts.getInt(KEY_ANIM_START_X, 0);
@@ -244,6 +293,16 @@
     }
 
     /** @hide */
+    public int getStartWidth() {
+        return mStartWidth;
+    }
+
+    /** @hide */
+    public int getStartHeight() {
+        return mStartHeight;
+    }
+
+    /** @hide */
     public IRemoteCallback getOnAnimationStartListener() {
         return mAnimationStartedListener;
     }
@@ -281,6 +340,13 @@
                 mThumbnail = null;
                 mAnimationStartedListener = null;
                 break;
+            case ANIM_SCALE_UP:
+                mAnimationType = otherOptions.mAnimationType;
+                mStartX = otherOptions.mStartX;
+                mStartY = otherOptions.mStartY;
+                mStartWidth = otherOptions.mStartWidth;
+                mStartHeight = otherOptions.mStartHeight;
+                break;
             case ANIM_THUMBNAIL:
                 mAnimationType = otherOptions.mAnimationType;
                 mThumbnail = otherOptions.mThumbnail;
@@ -316,6 +382,13 @@
                 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
                 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
                 break;
+            case ANIM_SCALE_UP:
+                b.putInt(KEY_ANIM_TYPE, mAnimationType);
+                b.putInt(KEY_ANIM_START_X, mStartX);
+                b.putInt(KEY_ANIM_START_Y, mStartY);
+                b.putInt(KEY_ANIM_START_WIDTH, mStartWidth);
+                b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight);
+                break;
             case ANIM_THUMBNAIL:
                 b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
@@ -323,6 +396,7 @@
                 b.putInt(KEY_ANIM_START_Y, mStartY);
                 b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
                         != null ? mAnimationStartedListener.asBinder() : null);
+                break;
         }
         return b;
     }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 0645aa9..8942135 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -45,6 +45,7 @@
 import android.hardware.ISerialManager;
 import android.hardware.SensorManager;
 import android.hardware.SerialManager;
+import android.hardware.SystemSensorManager;
 import android.hardware.input.IInputManager;
 import android.hardware.input.InputManager;
 import android.hardware.usb.IUsbManager;
@@ -407,7 +408,7 @@
 
         registerService(SENSOR_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
-                    return new SensorManager(ctx.mMainThread.getHandler().getLooper());
+                    return new SystemSensorManager(ctx.mMainThread.getHandler().getLooper());
                 }});
 
         registerService(STATUS_BAR_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 19e4372..6653336 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6576,35 +6576,54 @@
 
         final String action = getAction();
         if (ACTION_SEND.equals(action)) {
-            final Uri stream;
+            Uri stream = null;
             try {
                 stream = getParcelableExtra(EXTRA_STREAM);
             } catch (ClassCastException e) {
-                return;
             }
-            if (stream != null) {
+            final CharSequence text = getCharSequenceExtra(EXTRA_TEXT);
+            final String htmlText = getStringExtra(EXTRA_HTML_TEXT);
+            if (stream != null || text != null || htmlText != null) {
                 final ClipData clipData = new ClipData(
-                        null, new String[] { getType() }, new ClipData.Item(stream));
-
+                        null, new String[] { getType() },
+                        new ClipData.Item(text, htmlText, null, stream));
                 setClipData(clipData);
                 addFlags(FLAG_GRANT_READ_URI_PERMISSION);
             }
 
         } else if (ACTION_SEND_MULTIPLE.equals(action)) {
-            final ArrayList<Uri> streams;
+            ArrayList<Uri> streams = null;
             try {
                 streams = getParcelableArrayListExtra(EXTRA_STREAM);
             } catch (ClassCastException e) {
-                return;
             }
-            if (streams != null && streams.size() > 0) {
-                final Uri firstStream = streams.get(0);
+            final ArrayList<CharSequence> texts = getCharSequenceArrayListExtra(EXTRA_TEXT);
+            final ArrayList<String> htmlTexts = getStringArrayListExtra(EXTRA_HTML_TEXT);
+            int num = -1;
+            if (streams != null) {
+                num = streams.size();
+            }
+            if (texts != null) {
+                if (num >= 0 && num != texts.size()) {
+                    // Wha...!  F- you.
+                    return;
+                }
+                num = texts.size();
+            }
+            if (htmlTexts != null) {
+                if (num >= 0 && num != htmlTexts.size()) {
+                    // Wha...!  F- you.
+                    return;
+                }
+                num = htmlTexts.size();
+            }
+            if (num > 0) {
                 final ClipData clipData = new ClipData(
-                        null, new String[] { getType() }, new ClipData.Item(firstStream));
+                        null, new String[] { getType() },
+                        makeClipItem(streams, texts, htmlTexts, 0));
 
-                final int size = streams.size();
-                for (int i = 1; i < size; i++) {
-                    clipData.addItem(new ClipData.Item(streams.get(i)));
+                for (int i = 1; i < num; i++) {
+                    clipData.addItem(makeClipItem(streams, texts, htmlTexts, i));
                 }
 
                 setClipData(clipData);
@@ -6612,4 +6631,12 @@
             }
         }
     }
+
+    private static ClipData.Item makeClipItem(ArrayList<Uri> streams, ArrayList<CharSequence> texts,
+            ArrayList<String> htmlTexts, int which) {
+        Uri uri = streams != null ? streams.get(which) : null;
+        CharSequence text = texts != null ? texts.get(which) : null;
+        String htmlText = htmlTexts != null ? htmlTexts.get(which) : null;
+        return new ClipData.Item(text, htmlText, null, uri);
+    }
 }
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 254f652..04f6377 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -711,6 +711,7 @@
             throw new IllegalArgumentException("sql must not be null.");
         }
 
+        int changedRows = 0;
         final int cookie = mRecentOperations.beginOperation("executeForChangedRowCount",
                 sql, bindArgs);
         try {
@@ -721,8 +722,9 @@
                 applyBlockGuardPolicy(statement);
                 attachCancellationSignal(cancellationSignal);
                 try {
-                    return nativeExecuteForChangedRowCount(
+                    changedRows = nativeExecuteForChangedRowCount(
                             mConnectionPtr, statement.mStatementPtr);
+                    return changedRows;
                 } finally {
                     detachCancellationSignal(cancellationSignal);
                 }
@@ -733,7 +735,9 @@
             mRecentOperations.failOperation(cookie, ex);
             throw ex;
         } finally {
-            mRecentOperations.endOperation(cookie);
+            if (mRecentOperations.endOperationDeferLog(cookie)) {
+                mRecentOperations.logOperation(cookie, "changedRows=" + changedRows);
+            }
         }
     }
 
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 63fb32d..3c70dc6 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -131,7 +131,6 @@
     private float   mResolution;
     private float   mPower;
     private int     mMinDelay;
-    private int     mLegacyType;
 
 
     Sensor() {
@@ -203,12 +202,4 @@
         mMaxRange = max;
         mResolution = res;
     }
-
-    void setLegacyType(int legacyType) {
-        mLegacyType = legacyType;
-    }
-
-    int getLegacyType() {
-        return mLegacyType;
-    }
 }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 3fdf246..d783db7 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -16,16 +16,10 @@
 
 package android.hardware;
 
-import android.os.Looper;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.Handler;
-import android.os.Message;
 import android.os.ServiceManager;
-import android.util.Log;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
 import android.view.IRotationWatcher;
 import android.view.IWindowManager;
 import android.view.Surface;
@@ -83,11 +77,21 @@
  * @see Sensor
  *
  */
-public class SensorManager
-{
-    private static final String TAG = "SensorManager";
+public abstract class SensorManager {
     private static final float[] mTempMatrix = new float[16];
 
+    private static boolean sInitialized;
+    private static IWindowManager sWindowManager;
+    private static int sRotation = Surface.ROTATION_0;
+
+    // List of legacy listeners.  Guarded by mLegacyListenersMap.
+    private final HashMap<SensorListener, LegacyListener> mLegacyListenersMap =
+            new HashMap<SensorListener, LegacyListener>();
+
+    // Cached lists of sensors by type.  Guarded by mSensorListByType.
+    private final SparseArray<List<Sensor>> mSensorListByType =
+            new SparseArray<List<Sensor>>();
+
     /* NOTE: sensor IDs must be a power of 2 */
 
     /**
@@ -353,287 +357,13 @@
     /** see {@link #remapCoordinateSystem} */
     public static final int AXIS_MINUS_Z = AXIS_Z | 0x80;
 
-    /*-----------------------------------------------------------------------*/
-
-    Looper mMainLooper;
-    @SuppressWarnings("deprecation")
-    private HashMap<SensorListener, LegacyListener> mLegacyListenersMap =
-        new HashMap<SensorListener, LegacyListener>();
-
-    /*-----------------------------------------------------------------------*/
-
-    private static final int SENSOR_DISABLE = -1;
-    private static boolean sSensorModuleInitialized = false;
-    private static ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
-    private static SparseArray<List<Sensor>> sSensorListByType = new SparseArray<List<Sensor>>();
-    private static IWindowManager sWindowManager;
-    private static int sRotation = Surface.ROTATION_0;
-    /* The thread and the sensor list are global to the process
-     * but the actual thread is spawned on demand */
-    private static SensorThread sSensorThread;
-    private static int sQueue;
-
-    // Used within this module from outside SensorManager, don't make private
-    static SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
-    static final ArrayList<ListenerDelegate> sListeners =
-        new ArrayList<ListenerDelegate>();
-
-    /*-----------------------------------------------------------------------*/
-
-    private class SensorEventPool {
-        private final int mPoolSize;
-        private final SensorEvent mPool[];
-        private int mNumItemsInPool;
-
-        private SensorEvent createSensorEvent() {
-            // maximal size for all legacy events is 3
-            return new SensorEvent(3);
-        }
-
-        SensorEventPool(int poolSize) {
-            mPoolSize = poolSize;
-            mNumItemsInPool = poolSize;
-            mPool = new SensorEvent[poolSize];
-        }
-
-        SensorEvent getFromPool() {
-            SensorEvent t = null;
-            synchronized (this) {
-                if (mNumItemsInPool > 0) {
-                    // remove the "top" item from the pool
-                    final int index = mPoolSize - mNumItemsInPool;
-                    t = mPool[index];
-                    mPool[index] = null;
-                    mNumItemsInPool--;
-                }
-            }
-            if (t == null) {
-                // the pool was empty or this item was removed from the pool for
-                // the first time. In any case, we need to create a new item.
-                t = createSensorEvent();
-            }
-            return t;
-        }
-
-        void returnToPool(SensorEvent t) {
-            synchronized (this) {
-                // is there space left in the pool?
-                if (mNumItemsInPool < mPoolSize) {
-                    // if so, return the item to the pool
-                    mNumItemsInPool++;
-                    final int index = mPoolSize - mNumItemsInPool;
-                    mPool[index] = t;
-                }
-            }
-        }
-    }
-
-    private static SensorEventPool sPool;
-
-    /*-----------------------------------------------------------------------*/
-
-    static private class SensorThread {
-
-        Thread mThread;
-        boolean mSensorsReady;
-
-        SensorThread() {
-        }
-
-        @Override
-        protected void finalize() {
-        }
-
-        // must be called with sListeners lock
-        boolean startLocked() {
-            try {
-                if (mThread == null) {
-                    mSensorsReady = false;
-                    SensorThreadRunnable runnable = new SensorThreadRunnable();
-                    Thread thread = new Thread(runnable, SensorThread.class.getName());
-                    thread.start();
-                    synchronized (runnable) {
-                        while (mSensorsReady == false) {
-                            runnable.wait();
-                        }
-                    }
-                    mThread = thread;
-                }
-            } catch (InterruptedException e) {
-            }
-            return mThread == null ? false : true;
-        }
-
-        private class SensorThreadRunnable implements Runnable {
-            SensorThreadRunnable() {
-            }
-
-            private boolean open() {
-                // NOTE: this cannot synchronize on sListeners, since
-                // it's held in the main thread at least until we
-                // return from here.
-                sQueue = sensors_create_queue();
-                return true;
-            }
-
-            public void run() {
-                //Log.d(TAG, "entering main sensor thread");
-                final float[] values = new float[3];
-                final int[] status = new int[1];
-                final long timestamp[] = new long[1];
-                Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
-                if (!open()) {
-                    return;
-                }
-
-                synchronized (this) {
-                    // we've open the driver, we're ready to open the sensors
-                    mSensorsReady = true;
-                    this.notify();
-                }
-
-                while (true) {
-                    // wait for an event
-                    final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
-
-                    int accuracy = status[0];
-                    synchronized (sListeners) {
-                        if (sensor == -1 || sListeners.isEmpty()) {
-                            // we lost the connection to the event stream. this happens
-                            // when the last listener is removed or if there is an error
-                            if (sensor == -1 && !sListeners.isEmpty()) {
-                                // log a warning in case of abnormal termination
-                                Log.e(TAG, "_sensors_data_poll() failed, we bail out: sensors=" + sensor);
-                            }
-                            // we have no more listeners or polling failed, terminate the thread
-                            sensors_destroy_queue(sQueue);
-                            sQueue = 0;
-                            mThread = null;
-                            break;
-                        }
-                        final Sensor sensorObject = sHandleToSensor.get(sensor);
-                        if (sensorObject != null) {
-                            // report the sensor event to all listeners that
-                            // care about it.
-                            final int size = sListeners.size();
-                            for (int i=0 ; i<size ; i++) {
-                                ListenerDelegate listener = sListeners.get(i);
-                                if (listener.hasSensor(sensorObject)) {
-                                    // this is asynchronous (okay to call
-                                    // with sListeners lock held).
-                                    listener.onSensorChangedLocked(sensorObject,
-                                            values, timestamp, accuracy);
-                                }
-                            }
-                        }
-                    }
-                }
-                //Log.d(TAG, "exiting main sensor thread");
-            }
-        }
-    }
-
-    /*-----------------------------------------------------------------------*/
-
-    private class ListenerDelegate {
-        private final SensorEventListener mSensorEventListener;
-        private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
-        private final Handler mHandler;
-        public SparseBooleanArray mSensors = new SparseBooleanArray();
-        public SparseBooleanArray mFirstEvent = new SparseBooleanArray();
-        public SparseIntArray mSensorAccuracies = new SparseIntArray();
-
-        ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) {
-            mSensorEventListener = listener;
-            Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
-            // currently we create one Handler instance per listener, but we could
-            // have one per looper (we'd need to pass the ListenerDelegate
-            // instance to handleMessage and keep track of them separately).
-            mHandler = new Handler(looper) {
-                @Override
-                public void handleMessage(Message msg) {
-                    final SensorEvent t = (SensorEvent)msg.obj;
-                    final int handle = t.sensor.getHandle();
-
-                    switch (t.sensor.getType()) {
-                        // Only report accuracy for sensors that support it.
-                        case Sensor.TYPE_MAGNETIC_FIELD:
-                        case Sensor.TYPE_ORIENTATION:
-                            // call onAccuracyChanged() only if the value changes
-                            final int accuracy = mSensorAccuracies.get(handle);
-                            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
-                                mSensorAccuracies.put(handle, t.accuracy);
-                                mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy);
-                            }
-                            break;
-                        default:
-                            // For other sensors, just report the accuracy once
-                            if (mFirstEvent.get(handle) == false) {
-                                mFirstEvent.put(handle, true);
-                                mSensorEventListener.onAccuracyChanged(
-                                        t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
-                            }
-                            break;
-                    }
-
-                    mSensorEventListener.onSensorChanged(t);
-                    sPool.returnToPool(t);
-                }
-            };
-            addSensor(sensor);
-        }
-
-        Object getListener() {
-            return mSensorEventListener;
-        }
-
-        void addSensor(Sensor sensor) {
-            mSensors.put(sensor.getHandle(), true);
-            mSensorList.add(sensor);
-        }
-        int removeSensor(Sensor sensor) {
-            mSensors.delete(sensor.getHandle());
-            mSensorList.remove(sensor);
-            return mSensors.size();
-        }
-        boolean hasSensor(Sensor sensor) {
-            return mSensors.get(sensor.getHandle());
-        }
-        List<Sensor> getSensors() {
-            return mSensorList;
-        }
-
-        void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) {
-            SensorEvent t = sPool.getFromPool();
-            final float[] v = t.values;
-            v[0] = values[0];
-            v[1] = values[1];
-            v[2] = values[2];
-            t.timestamp = timestamp[0];
-            t.accuracy = accuracy;
-            t.sensor = sensor;
-            Message msg = Message.obtain();
-            msg.what = 0;
-            msg.obj = t;
-            msg.setAsynchronous(true);
-            mHandler.sendMessage(msg);
-        }
-    }
 
     /**
      * {@hide}
      */
-    public SensorManager(Looper mainLooper) {
-        mMainLooper = mainLooper;
-
-
-        synchronized(sListeners) {
-            if (!sSensorModuleInitialized) {
-                sSensorModuleInitialized = true;
-
-                nativeClassInit();
-
+    public SensorManager() {
+        synchronized (SensorManager.class) {
+            if (!sInitialized) {
                 sWindowManager = IWindowManager.Stub.asInterface(
                         ServiceManager.getService("window"));
                 if (sWindowManager != null) {
@@ -650,43 +380,15 @@
                     } catch (RemoteException e) {
                     }
                 }
-
-                // initialize the sensor list
-                sensors_module_init();
-                final ArrayList<Sensor> fullList = sFullSensorsList;
-                int i = 0;
-                do {
-                    Sensor sensor = new Sensor();
-                    i = sensors_module_get_next_sensor(sensor, i);
-
-                    if (i>=0) {
-                        //Log.d(TAG, "found sensor: " + sensor.getName() +
-                        //        ", handle=" + sensor.getHandle());
-                        sensor.setLegacyType(getLegacySensorType(sensor.getType()));
-                        fullList.add(sensor);
-                        sHandleToSensor.append(sensor.getHandle(), sensor);
-                    }
-                } while (i>0);
-
-                sPool = new SensorEventPool( sFullSensorsList.size()*2 );
-                sSensorThread = new SensorThread();
             }
         }
     }
 
-    private int getLegacySensorType(int type) {
-        switch (type) {
-            case Sensor.TYPE_ACCELEROMETER:
-                return SENSOR_ACCELEROMETER;
-            case Sensor.TYPE_MAGNETIC_FIELD:
-                return SENSOR_MAGNETIC_FIELD;
-            case Sensor.TYPE_ORIENTATION:
-                return SENSOR_ORIENTATION_RAW;
-            case Sensor.TYPE_TEMPERATURE:
-                return SENSOR_TEMPERATURE;
-        }
-        return 0;
-    }
+    /**
+     * Gets the full list of sensors that are available.
+     * @hide
+     */
+    protected abstract List<Sensor> getFullSensorList();
 
     /**
      * @return available sensors.
@@ -696,7 +398,7 @@
     @Deprecated
     public int getSensors() {
         int result = 0;
-        final ArrayList<Sensor> fullList = sFullSensorsList;
+        final List<Sensor> fullList = getFullSensorList();
         for (Sensor i : fullList) {
             switch (i.getType()) {
                 case Sensor.TYPE_ACCELEROMETER:
@@ -731,9 +433,9 @@
     public List<Sensor> getSensorList(int type) {
         // cache the returned lists the first time
         List<Sensor> list;
-        final ArrayList<Sensor> fullList = sFullSensorsList;
-        synchronized(fullList) {
-            list = sSensorListByType.get(type);
+        final List<Sensor> fullList = getFullSensorList();
+        synchronized (mSensorListByType) {
+            list = mSensorListByType.get(type);
             if (list == null) {
                 if (type == Sensor.TYPE_ALL) {
                     list = fullList;
@@ -745,7 +447,7 @@
                     }
                 }
                 list = Collections.unmodifiableList(list);
-                sSensorListByType.append(type, list);
+                mSensorListByType.append(type, list);
             }
         }
         return list;
@@ -836,34 +538,37 @@
 
     @SuppressWarnings("deprecation")
     private boolean registerLegacyListener(int legacyType, int type,
-            SensorListener listener, int sensors, int rate)
-    {
-        if (listener == null) {
-            return false;
-        }
+            SensorListener listener, int sensors, int rate) {
         boolean result = false;
         // Are we activating this legacy sensor?
         if ((sensors & legacyType) != 0) {
             // if so, find a suitable Sensor
             Sensor sensor = getDefaultSensor(type);
             if (sensor != null) {
-                // If we don't already have one, create a LegacyListener
-                // to wrap this listener and process the events as
-                // they are expected by legacy apps.
-                LegacyListener legacyListener = null;
+                // We do all of this work holding the legacy listener lock to ensure
+                // that the invariants around listeners are maintained.  This is safe
+                // because neither registerLegacyListener nor unregisterLegacyListener
+                // are called reentrantly while sensors are being registered or unregistered.
                 synchronized (mLegacyListenersMap) {
-                    legacyListener = mLegacyListenersMap.get(listener);
+                    // If we don't already have one, create a LegacyListener
+                    // to wrap this listener and process the events as
+                    // they are expected by legacy apps.
+                    LegacyListener legacyListener = mLegacyListenersMap.get(listener);
                     if (legacyListener == null) {
                         // we didn't find a LegacyListener for this client,
                         // create one, and put it in our list.
                         legacyListener = new LegacyListener(listener);
                         mLegacyListenersMap.put(listener, legacyListener);
                     }
+
+                    // register this legacy sensor with this legacy listener
+                    if (legacyListener.registerSensor(legacyType)) {
+                        // and finally, register the legacy listener with the new apis
+                        result = registerListener(legacyListener, sensor, rate);
+                    } else {
+                        result = true; // sensor already enabled
+                    }
                 }
-                // register this legacy sensor with this legacy listener
-                legacyListener.registerSensor(legacyType);
-                // and finally, register the legacy listener with the new apis
-                result = registerListener(legacyListener, sensor, rate);
             }
         }
         return result;
@@ -884,6 +589,9 @@
      */
     @Deprecated
     public void unregisterListener(SensorListener listener, int sensors) {
+        if (listener == null) {
+            return;
+        }
         unregisterLegacyListener(SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER,
                 listener, sensors);
         unregisterLegacyListener(SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD,
@@ -896,51 +604,6 @@
                 listener, sensors);
     }
 
-    @SuppressWarnings("deprecation")
-    private void unregisterLegacyListener(int legacyType, int type,
-            SensorListener listener, int sensors)
-    {
-        if (listener == null) {
-            return;
-        }
-        // do we know about this listener?
-        LegacyListener legacyListener = null;
-        synchronized (mLegacyListenersMap) {
-            legacyListener = mLegacyListenersMap.get(listener);
-        }
-        if (legacyListener != null) {
-            // Are we deactivating this legacy sensor?
-            if ((sensors & legacyType) != 0) {
-                // if so, find the corresponding Sensor
-                Sensor sensor = getDefaultSensor(type);
-                if (sensor != null) {
-                    // unregister this legacy sensor and if we don't
-                    // need the corresponding Sensor, unregister it too
-                    if (legacyListener.unregisterSensor(legacyType)) {
-                        // corresponding sensor not needed, unregister
-                        unregisterListener(legacyListener, sensor);
-                        // finally check if we still need the legacyListener
-                        // in our mapping, if not, get rid of it too.
-                        synchronized(sListeners) {
-                            boolean found = false;
-                            for (ListenerDelegate i : sListeners) {
-                                if (i.getListener() == legacyListener) {
-                                    found = true;
-                                    break;
-                                }
-                            }
-                            if (!found) {
-                                synchronized (mLegacyListenersMap) {
-                                    mLegacyListenersMap.remove(listener);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     /**
      * Unregisters a listener for all sensors.
      *
@@ -956,6 +619,40 @@
         unregisterListener(listener, SENSOR_ALL | SENSOR_ORIENTATION_RAW);
     }
 
+    @SuppressWarnings("deprecation")
+    private void unregisterLegacyListener(int legacyType, int type,
+            SensorListener listener, int sensors) {
+        // Are we deactivating this legacy sensor?
+        if ((sensors & legacyType) != 0) {
+            // if so, find the corresponding Sensor
+            Sensor sensor = getDefaultSensor(type);
+            if (sensor != null) {
+                // We do all of this work holding the legacy listener lock to ensure
+                // that the invariants around listeners are maintained.  This is safe
+                // because neither registerLegacyListener nor unregisterLegacyListener
+                // are called re-entrantly while sensors are being registered or unregistered.
+                synchronized (mLegacyListenersMap) {
+                    // do we know about this listener?
+                    LegacyListener legacyListener = mLegacyListenersMap.get(listener);
+                    if (legacyListener != null) {
+                        // unregister this legacy sensor and if we don't
+                        // need the corresponding Sensor, unregister it too
+                        if (legacyListener.unregisterSensor(legacyType)) {
+                            // corresponding sensor not needed, unregister
+                            unregisterListener(legacyListener, sensor);
+
+                            // finally check if we still need the legacyListener
+                            // in our mapping, if not, get rid of it too.
+                            if (!legacyListener.hasSensors()) {
+                                mLegacyListenersMap.remove(listener);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Unregisters a listener for the sensors with which it is registered.
      *
@@ -970,7 +667,11 @@
      *
      */
     public void unregisterListener(SensorEventListener listener, Sensor sensor) {
-        unregisterListener((Object)listener, sensor);
+        if (listener == null || sensor == null) {
+            return;
+        }
+
+        unregisterListenerImpl(listener, sensor);
     }
 
     /**
@@ -984,9 +685,16 @@
      *
      */
     public void unregisterListener(SensorEventListener listener) {
-        unregisterListener((Object)listener);
+        if (listener == null) {
+            return;
+        }
+
+        unregisterListenerImpl(listener, null);
     }
 
+    /** @hide */
+    protected abstract void unregisterListenerImpl(SensorEventListener listener, Sensor sensor);
+
     /**
      * Registers a {@link android.hardware.SensorEventListener
      * SensorEventListener} for the given sensor.
@@ -1019,31 +727,6 @@
         return registerListener(listener, sensor, rate, null);
     }
 
-    private boolean enableSensorLocked(Sensor sensor, int delay) {
-        boolean result = false;
-        for (ListenerDelegate i : sListeners) {
-            if (i.hasSensor(sensor)) {
-                String name = sensor.getName();
-                int handle = sensor.getHandle();
-                result = sensors_enable_sensor(sQueue, name, handle, delay);
-                break;
-            }
-        }
-        return result;
-    }
-
-    private boolean disableSensorLocked(Sensor sensor) {
-        for (ListenerDelegate i : sListeners) {
-            if (i.hasSensor(sensor)) {
-                // not an error, it's just that this sensor is still in use
-                return true;
-            }
-        }
-        String name = sensor.getName();
-        int handle = sensor.getHandle();
-        return sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
-    }
-
     /**
      * Registers a {@link android.hardware.SensorEventListener
      * SensorEventListener} for the given sensor.
@@ -1081,7 +764,7 @@
         if (listener == null || sensor == null) {
             return false;
         }
-        boolean result = true;
+
         int delay = -1;
         switch (rate) {
             case SENSOR_DELAY_FASTEST:
@@ -1101,92 +784,12 @@
                 break;
         }
 
-        synchronized (sListeners) {
-            // look for this listener in our list
-            ListenerDelegate l = null;
-            for (ListenerDelegate i : sListeners) {
-                if (i.getListener() == listener) {
-                    l = i;
-                    break;
-                }
-            }
-
-            // if we don't find it, add it to the list
-            if (l == null) {
-                l = new ListenerDelegate(listener, sensor, handler);
-                sListeners.add(l);
-                // if the list is not empty, start our main thread
-                if (!sListeners.isEmpty()) {
-                    if (sSensorThread.startLocked()) {
-                        if (!enableSensorLocked(sensor, delay)) {
-                            // oops. there was an error
-                            sListeners.remove(l);
-                            result = false;
-                        }
-                    } else {
-                        // there was an error, remove the listener
-                        sListeners.remove(l);
-                        result = false;
-                    }
-                } else {
-                    // weird, we couldn't add the listener
-                    result = false;
-                }
-            } else {
-                l.addSensor(sensor);
-                if (!enableSensorLocked(sensor, delay)) {
-                    // oops. there was an error
-                    l.removeSensor(sensor);
-                    result = false;
-                }
-            }
-        }
-
-        return result;
+        return registerListenerImpl(listener, sensor, delay, handler);
     }
 
-    private void unregisterListener(Object listener, Sensor sensor) {
-        if (listener == null || sensor == null) {
-            return;
-        }
-
-        synchronized (sListeners) {
-            final int size = sListeners.size();
-            for (int i=0 ; i<size ; i++) {
-                ListenerDelegate l = sListeners.get(i);
-                if (l.getListener() == listener) {
-                    if (l.removeSensor(sensor) == 0) {
-                        // if we have no more sensors enabled on this listener,
-                        // take it off the list.
-                        sListeners.remove(i);
-                    }
-                    break;
-                }
-            }
-            disableSensorLocked(sensor);
-        }
-    }
-
-    private void unregisterListener(Object listener) {
-        if (listener == null) {
-            return;
-        }
-
-        synchronized (sListeners) {
-            final int size = sListeners.size();
-            for (int i=0 ; i<size ; i++) {
-                ListenerDelegate l = sListeners.get(i);
-                if (l.getListener() == listener) {
-                    sListeners.remove(i);
-                    // disable all sensors for this listener
-                    for (Sensor sensor : l.getSensors()) {
-                        disableSensorLocked(sensor);
-                    }
-                    break;
-                }
-            }
-        }
-    }
+    /** @hide */
+    protected abstract boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
+            int delay, Handler handler);
 
     /**
      * <p>
@@ -1653,228 +1256,11 @@
      * @param p atmospheric pressure
      * @return Altitude in meters
      */
-   public static float getAltitude(float p0, float p) {
+    public static float getAltitude(float p0, float p) {
         final float coef = 1.0f / 5.255f;
         return 44330.0f * (1.0f - (float)Math.pow(p/p0, coef));
     }
 
-
-   /**
-     * {@hide}
-     */
-    public void onRotationChanged(int rotation) {
-        synchronized(sListeners) {
-            sRotation  = rotation;
-        }
-    }
-
-    static int getRotation() {
-        synchronized(sListeners) {
-            return sRotation;
-        }
-    }
-
-    private class LegacyListener implements SensorEventListener {
-        private float mValues[] = new float[6];
-        @SuppressWarnings("deprecation")
-        private SensorListener mTarget;
-        private int mSensors;
-        private final LmsFilter mYawfilter = new LmsFilter();
-
-        @SuppressWarnings("deprecation")
-        LegacyListener(SensorListener target) {
-            mTarget = target;
-            mSensors = 0;
-        }
-
-        void registerSensor(int legacyType) {
-            mSensors |= legacyType;
-        }
-
-        boolean unregisterSensor(int legacyType) {
-            mSensors &= ~legacyType;
-            int mask = SENSOR_ORIENTATION|SENSOR_ORIENTATION_RAW;
-            if (((legacyType&mask)!=0) && ((mSensors&mask)!=0)) {
-                return false;
-            }
-            return true;
-        }
-
-        @SuppressWarnings("deprecation")
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            try {
-                mTarget.onAccuracyChanged(sensor.getLegacyType(), accuracy);
-            } catch (AbstractMethodError e) {
-                // old app that doesn't implement this method
-                // just ignore it.
-            }
-        }
-
-        @SuppressWarnings("deprecation")
-        public void onSensorChanged(SensorEvent event) {
-            final float v[] = mValues;
-            v[0] = event.values[0];
-            v[1] = event.values[1];
-            v[2] = event.values[2];
-            int legacyType = event.sensor.getLegacyType();
-            mapSensorDataToWindow(legacyType, v, SensorManager.getRotation());
-            if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
-                if ((mSensors & SENSOR_ORIENTATION_RAW)!=0) {
-                    mTarget.onSensorChanged(SENSOR_ORIENTATION_RAW, v);
-                }
-                if ((mSensors & SENSOR_ORIENTATION)!=0) {
-                    v[0] = mYawfilter.filter(event.timestamp, v[0]);
-                    mTarget.onSensorChanged(SENSOR_ORIENTATION, v);
-                }
-            } else {
-                mTarget.onSensorChanged(legacyType, v);
-            }
-        }
-
-        /*
-         * Helper function to convert the specified sensor's data to the windows's
-         * coordinate space from the device's coordinate space.
-         *
-         * output: 3,4,5: values in the old API format
-         *         0,1,2: transformed values in the old API format
-         *
-         */
-        private void mapSensorDataToWindow(int sensor,
-                float[] values, int orientation) {
-            float x = values[0];
-            float y = values[1];
-            float z = values[2];
-
-            switch (sensor) {
-                case SensorManager.SENSOR_ORIENTATION:
-                case SensorManager.SENSOR_ORIENTATION_RAW:
-                    z = -z;
-                    break;
-                case SensorManager.SENSOR_ACCELEROMETER:
-                    x = -x;
-                    y = -y;
-                    z = -z;
-                    break;
-                case SensorManager.SENSOR_MAGNETIC_FIELD:
-                    x = -x;
-                    y = -y;
-                    break;
-            }
-            values[0] = x;
-            values[1] = y;
-            values[2] = z;
-            values[3] = x;
-            values[4] = y;
-            values[5] = z;
-
-            if ((orientation & Surface.ROTATION_90) != 0) {
-                // handles 90 and 270 rotation
-                switch (sensor) {
-                    case SENSOR_ACCELEROMETER:
-                    case SENSOR_MAGNETIC_FIELD:
-                        values[0] =-y;
-                        values[1] = x;
-                        values[2] = z;
-                        break;
-                    case SENSOR_ORIENTATION:
-                    case SENSOR_ORIENTATION_RAW:
-                        values[0] = x + ((x < 270) ? 90 : -270);
-                        values[1] = z;
-                        values[2] = y;
-                        break;
-                }
-            }
-            if ((orientation & Surface.ROTATION_180) != 0) {
-                x = values[0];
-                y = values[1];
-                z = values[2];
-                // handles 180 (flip) and 270 (flip + 90) rotation
-                switch (sensor) {
-                    case SENSOR_ACCELEROMETER:
-                    case SENSOR_MAGNETIC_FIELD:
-                        values[0] =-x;
-                        values[1] =-y;
-                        values[2] = z;
-                        break;
-                    case SENSOR_ORIENTATION:
-                    case SENSOR_ORIENTATION_RAW:
-                        values[0] = (x >= 180) ? (x - 180) : (x + 180);
-                        values[1] =-y;
-                        values[2] =-z;
-                        break;
-                }
-            }
-        }
-    }
-
-    class LmsFilter {
-        private static final int SENSORS_RATE_MS = 20;
-        private static final int COUNT = 12;
-        private static final float PREDICTION_RATIO = 1.0f/3.0f;
-        private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
-        private float mV[] = new float[COUNT*2];
-        private float mT[] = new float[COUNT*2];
-        private int mIndex;
-
-        public LmsFilter() {
-            mIndex = COUNT;
-        }
-
-        public float filter(long time, float in) {
-            float v = in;
-            final float ns = 1.0f / 1000000000.0f;
-            final float t = time*ns;
-            float v1 = mV[mIndex];
-            if ((v-v1) > 180) {
-                v -= 360;
-            } else if ((v1-v) > 180) {
-                v += 360;
-            }
-            /* Manage the circular buffer, we write the data twice spaced
-             * by COUNT values, so that we don't have to copy the array
-             * when it's full
-             */
-            mIndex++;
-            if (mIndex >= COUNT*2)
-                mIndex = COUNT;
-            mV[mIndex] = v;
-            mT[mIndex] = t;
-            mV[mIndex-COUNT] = v;
-            mT[mIndex-COUNT] = t;
-
-            float A, B, C, D, E;
-            float a, b;
-            int i;
-
-            A = B = C = D = E = 0;
-            for (i=0 ; i<COUNT-1 ; i++) {
-                final int j = mIndex - 1 - i;
-                final float Z = mV[j];
-                final float T = 0.5f*(mT[j] + mT[j+1]) - t;
-                float dT = mT[j] - mT[j+1];
-                dT *= dT;
-                A += Z*dT;
-                B += T*(T*dT);
-                C +=   (T*dT);
-                D += Z*(T*dT);
-                E += dT;
-            }
-            b = (A*B + C*D) / (E*B + C*C);
-            a = (E*b - A) / C;
-            float f = b + PREDICTION_TIME*a;
-
-            // Normalize
-            f *= (1.0f / 360.0f);
-            if (((f>=0)?f:-f) >= 0.5f)
-                f = f - (float)Math.ceil(f + 0.5f) + 1.0f;
-            if (f < 0)
-                f += 1.0f;
-            f *= 360.0f;
-            return f;
-        }
-    }
-
-
     /** Helper function to compute the angle change between two rotation matrices.
      *  Given a current rotation matrix (R) and a previous rotation matrix
      *  (prevR) computes the rotation around the x,y, and z axes which
@@ -2060,14 +1446,300 @@
         Q[3] = rv[2];
     }
 
-    private static native void nativeClassInit();
+    static void onRotationChanged(int rotation) {
+        synchronized (SensorManager.class) {
+            sRotation  = rotation;
+        }
+    }
 
-    private static native int sensors_module_init();
-    private static native int sensors_module_get_next_sensor(Sensor sensor, int next);
+    static int getRotation() {
+        synchronized (SensorManager.class) {
+            return sRotation;
+        }
+    }
 
-    // Used within this module from outside SensorManager, don't make private
-    static native int sensors_create_queue();
-    static native void sensors_destroy_queue(int queue);
-    static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable);
-    static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp);
+    private static final class LegacyListener implements SensorEventListener {
+        private float mValues[] = new float[6];
+        @SuppressWarnings("deprecation")
+        private SensorListener mTarget;
+        private int mSensors;
+        private final LmsFilter mYawfilter = new LmsFilter();
+
+        @SuppressWarnings("deprecation")
+        LegacyListener(SensorListener target) {
+            mTarget = target;
+            mSensors = 0;
+        }
+
+        boolean registerSensor(int legacyType) {
+            if ((mSensors & legacyType) != 0) {
+                return false;
+            }
+            boolean alreadyHasOrientationSensor = hasOrientationSensor(mSensors);
+            mSensors |= legacyType;
+            if (alreadyHasOrientationSensor && hasOrientationSensor(legacyType)) {
+                return false; // don't need to re-register the orientation sensor
+            }
+            return true;
+        }
+
+        boolean unregisterSensor(int legacyType) {
+            if ((mSensors & legacyType) == 0) {
+                return false;
+            }
+            mSensors &= ~legacyType;
+            if (hasOrientationSensor(legacyType) && hasOrientationSensor(mSensors)) {
+                return false; // can't unregister the orientation sensor just yet
+            }
+            return true;
+        }
+
+        boolean hasSensors() {
+            return mSensors != 0;
+        }
+
+        private static boolean hasOrientationSensor(int sensors) {
+            return (sensors & (SENSOR_ORIENTATION | SENSOR_ORIENTATION_RAW)) != 0;
+        }
+
+        @SuppressWarnings("deprecation")
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            try {
+                mTarget.onAccuracyChanged(getLegacySensorType(sensor.getType()), accuracy);
+            } catch (AbstractMethodError e) {
+                // old app that doesn't implement this method
+                // just ignore it.
+            }
+        }
+
+        @SuppressWarnings("deprecation")
+        public void onSensorChanged(SensorEvent event) {
+            final float v[] = mValues;
+            v[0] = event.values[0];
+            v[1] = event.values[1];
+            v[2] = event.values[2];
+            int type = event.sensor.getType();
+            int legacyType = getLegacySensorType(type);
+            mapSensorDataToWindow(legacyType, v, SensorManager.getRotation());
+            if (type == Sensor.TYPE_ORIENTATION) {
+                if ((mSensors & SENSOR_ORIENTATION_RAW)!=0) {
+                    mTarget.onSensorChanged(SENSOR_ORIENTATION_RAW, v);
+                }
+                if ((mSensors & SENSOR_ORIENTATION)!=0) {
+                    v[0] = mYawfilter.filter(event.timestamp, v[0]);
+                    mTarget.onSensorChanged(SENSOR_ORIENTATION, v);
+                }
+            } else {
+                mTarget.onSensorChanged(legacyType, v);
+            }
+        }
+
+        /*
+         * Helper function to convert the specified sensor's data to the windows's
+         * coordinate space from the device's coordinate space.
+         *
+         * output: 3,4,5: values in the old API format
+         *         0,1,2: transformed values in the old API format
+         *
+         */
+        private void mapSensorDataToWindow(int sensor,
+                float[] values, int orientation) {
+            float x = values[0];
+            float y = values[1];
+            float z = values[2];
+
+            switch (sensor) {
+                case SensorManager.SENSOR_ORIENTATION:
+                case SensorManager.SENSOR_ORIENTATION_RAW:
+                    z = -z;
+                    break;
+                case SensorManager.SENSOR_ACCELEROMETER:
+                    x = -x;
+                    y = -y;
+                    z = -z;
+                    break;
+                case SensorManager.SENSOR_MAGNETIC_FIELD:
+                    x = -x;
+                    y = -y;
+                    break;
+            }
+            values[0] = x;
+            values[1] = y;
+            values[2] = z;
+            values[3] = x;
+            values[4] = y;
+            values[5] = z;
+
+            if ((orientation & Surface.ROTATION_90) != 0) {
+                // handles 90 and 270 rotation
+                switch (sensor) {
+                    case SENSOR_ACCELEROMETER:
+                    case SENSOR_MAGNETIC_FIELD:
+                        values[0] =-y;
+                        values[1] = x;
+                        values[2] = z;
+                        break;
+                    case SENSOR_ORIENTATION:
+                    case SENSOR_ORIENTATION_RAW:
+                        values[0] = x + ((x < 270) ? 90 : -270);
+                        values[1] = z;
+                        values[2] = y;
+                        break;
+                }
+            }
+            if ((orientation & Surface.ROTATION_180) != 0) {
+                x = values[0];
+                y = values[1];
+                z = values[2];
+                // handles 180 (flip) and 270 (flip + 90) rotation
+                switch (sensor) {
+                    case SENSOR_ACCELEROMETER:
+                    case SENSOR_MAGNETIC_FIELD:
+                        values[0] =-x;
+                        values[1] =-y;
+                        values[2] = z;
+                        break;
+                    case SENSOR_ORIENTATION:
+                    case SENSOR_ORIENTATION_RAW:
+                        values[0] = (x >= 180) ? (x - 180) : (x + 180);
+                        values[1] =-y;
+                        values[2] =-z;
+                        break;
+                }
+            }
+        }
+
+        private static int getLegacySensorType(int type) {
+            switch (type) {
+                case Sensor.TYPE_ACCELEROMETER:
+                    return SENSOR_ACCELEROMETER;
+                case Sensor.TYPE_MAGNETIC_FIELD:
+                    return SENSOR_MAGNETIC_FIELD;
+                case Sensor.TYPE_ORIENTATION:
+                    return SENSOR_ORIENTATION_RAW;
+                case Sensor.TYPE_TEMPERATURE:
+                    return SENSOR_TEMPERATURE;
+            }
+            return 0;
+        }
+    }
+
+    private static final class LmsFilter {
+        private static final int SENSORS_RATE_MS = 20;
+        private static final int COUNT = 12;
+        private static final float PREDICTION_RATIO = 1.0f/3.0f;
+        private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
+        private float mV[] = new float[COUNT*2];
+        private float mT[] = new float[COUNT*2];
+        private int mIndex;
+
+        public LmsFilter() {
+            mIndex = COUNT;
+        }
+
+        public float filter(long time, float in) {
+            float v = in;
+            final float ns = 1.0f / 1000000000.0f;
+            final float t = time*ns;
+            float v1 = mV[mIndex];
+            if ((v-v1) > 180) {
+                v -= 360;
+            } else if ((v1-v) > 180) {
+                v += 360;
+            }
+            /* Manage the circular buffer, we write the data twice spaced
+             * by COUNT values, so that we don't have to copy the array
+             * when it's full
+             */
+            mIndex++;
+            if (mIndex >= COUNT*2)
+                mIndex = COUNT;
+            mV[mIndex] = v;
+            mT[mIndex] = t;
+            mV[mIndex-COUNT] = v;
+            mT[mIndex-COUNT] = t;
+
+            float A, B, C, D, E;
+            float a, b;
+            int i;
+
+            A = B = C = D = E = 0;
+            for (i=0 ; i<COUNT-1 ; i++) {
+                final int j = mIndex - 1 - i;
+                final float Z = mV[j];
+                final float T = 0.5f*(mT[j] + mT[j+1]) - t;
+                float dT = mT[j] - mT[j+1];
+                dT *= dT;
+                A += Z*dT;
+                B += T*(T*dT);
+                C +=   (T*dT);
+                D += Z*(T*dT);
+                E += dT;
+            }
+            b = (A*B + C*D) / (E*B + C*C);
+            a = (E*b - A) / C;
+            float f = b + PREDICTION_TIME*a;
+
+            // Normalize
+            f *= (1.0f / 360.0f);
+            if (((f>=0)?f:-f) >= 0.5f)
+                f = f - (float)Math.ceil(f + 0.5f) + 1.0f;
+            if (f < 0)
+                f += 1.0f;
+            f *= 360.0f;
+            return f;
+        }
+    }
+
+    /**
+     * Sensor event pool implementation.
+     * @hide
+     */
+    protected static final class SensorEventPool {
+        private final int mPoolSize;
+        private final SensorEvent mPool[];
+        private int mNumItemsInPool;
+
+        private SensorEvent createSensorEvent() {
+            // maximal size for all legacy events is 3
+            return new SensorEvent(3);
+        }
+
+        SensorEventPool(int poolSize) {
+            mPoolSize = poolSize;
+            mNumItemsInPool = poolSize;
+            mPool = new SensorEvent[poolSize];
+        }
+
+        SensorEvent getFromPool() {
+            SensorEvent t = null;
+            synchronized (this) {
+                if (mNumItemsInPool > 0) {
+                    // remove the "top" item from the pool
+                    final int index = mPoolSize - mNumItemsInPool;
+                    t = mPool[index];
+                    mPool[index] = null;
+                    mNumItemsInPool--;
+                }
+            }
+            if (t == null) {
+                // the pool was empty or this item was removed from the pool for
+                // the first time. In any case, we need to create a new item.
+                t = createSensorEvent();
+            }
+            return t;
+        }
+
+        void returnToPool(SensorEvent t) {
+            synchronized (this) {
+                // is there space left in the pool?
+                if (mNumItemsInPool < mPoolSize) {
+                    // if so, return the item to the pool
+                    mNumItemsInPool++;
+                    final int index = mPoolSize - mNumItemsInPool;
+                    mPool[index] = t;
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
new file mode 100644
index 0000000..7763405
--- /dev/null
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2012 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 android.hardware;
+
+import android.os.Looper;
+import android.os.Process;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Sensor manager implementation that communicates with the built-in
+ * system sensors.
+ *
+ * @hide
+ */
+public class SystemSensorManager extends SensorManager {
+    private static final String TAG = "SensorManager";
+
+    /*-----------------------------------------------------------------------*/
+
+    final Looper mMainLooper;
+
+    /*-----------------------------------------------------------------------*/
+
+    private static final int SENSOR_DISABLE = -1;
+    private static boolean sSensorModuleInitialized = false;
+    private static ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
+    /* The thread and the sensor list are global to the process
+     * but the actual thread is spawned on demand */
+    private static SensorThread sSensorThread;
+    private static int sQueue;
+
+    // Used within this module from outside SensorManager, don't make private
+    static SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
+    static final ArrayList<ListenerDelegate> sListeners =
+        new ArrayList<ListenerDelegate>();
+
+    /*-----------------------------------------------------------------------*/
+
+    private static SensorEventPool sPool;
+
+    /*-----------------------------------------------------------------------*/
+
+    static private class SensorThread {
+
+        Thread mThread;
+        boolean mSensorsReady;
+
+        SensorThread() {
+        }
+
+        @Override
+        protected void finalize() {
+        }
+
+        // must be called with sListeners lock
+        boolean startLocked() {
+            try {
+                if (mThread == null) {
+                    mSensorsReady = false;
+                    SensorThreadRunnable runnable = new SensorThreadRunnable();
+                    Thread thread = new Thread(runnable, SensorThread.class.getName());
+                    thread.start();
+                    synchronized (runnable) {
+                        while (mSensorsReady == false) {
+                            runnable.wait();
+                        }
+                    }
+                    mThread = thread;
+                }
+            } catch (InterruptedException e) {
+            }
+            return mThread == null ? false : true;
+        }
+
+        private class SensorThreadRunnable implements Runnable {
+            SensorThreadRunnable() {
+            }
+
+            private boolean open() {
+                // NOTE: this cannot synchronize on sListeners, since
+                // it's held in the main thread at least until we
+                // return from here.
+                sQueue = sensors_create_queue();
+                return true;
+            }
+
+            public void run() {
+                //Log.d(TAG, "entering main sensor thread");
+                final float[] values = new float[3];
+                final int[] status = new int[1];
+                final long timestamp[] = new long[1];
+                Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
+
+                if (!open()) {
+                    return;
+                }
+
+                synchronized (this) {
+                    // we've open the driver, we're ready to open the sensors
+                    mSensorsReady = true;
+                    this.notify();
+                }
+
+                while (true) {
+                    // wait for an event
+                    final int sensor = sensors_data_poll(sQueue, values, status, timestamp);
+
+                    int accuracy = status[0];
+                    synchronized (sListeners) {
+                        if (sensor == -1 || sListeners.isEmpty()) {
+                            // we lost the connection to the event stream. this happens
+                            // when the last listener is removed or if there is an error
+                            if (sensor == -1 && !sListeners.isEmpty()) {
+                                // log a warning in case of abnormal termination
+                                Log.e(TAG, "_sensors_data_poll() failed, we bail out: sensors=" + sensor);
+                            }
+                            // we have no more listeners or polling failed, terminate the thread
+                            sensors_destroy_queue(sQueue);
+                            sQueue = 0;
+                            mThread = null;
+                            break;
+                        }
+                        final Sensor sensorObject = sHandleToSensor.get(sensor);
+                        if (sensorObject != null) {
+                            // report the sensor event to all listeners that
+                            // care about it.
+                            final int size = sListeners.size();
+                            for (int i=0 ; i<size ; i++) {
+                                ListenerDelegate listener = sListeners.get(i);
+                                if (listener.hasSensor(sensorObject)) {
+                                    // this is asynchronous (okay to call
+                                    // with sListeners lock held).
+                                    listener.onSensorChangedLocked(sensorObject,
+                                            values, timestamp, accuracy);
+                                }
+                            }
+                        }
+                    }
+                }
+                //Log.d(TAG, "exiting main sensor thread");
+            }
+        }
+    }
+
+    /*-----------------------------------------------------------------------*/
+
+    private class ListenerDelegate {
+        private final SensorEventListener mSensorEventListener;
+        private final ArrayList<Sensor> mSensorList = new ArrayList<Sensor>();
+        private final Handler mHandler;
+        public SparseBooleanArray mSensors = new SparseBooleanArray();
+        public SparseBooleanArray mFirstEvent = new SparseBooleanArray();
+        public SparseIntArray mSensorAccuracies = new SparseIntArray();
+
+        ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) {
+            mSensorEventListener = listener;
+            Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
+            // currently we create one Handler instance per listener, but we could
+            // have one per looper (we'd need to pass the ListenerDelegate
+            // instance to handleMessage and keep track of them separately).
+            mHandler = new Handler(looper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    final SensorEvent t = (SensorEvent)msg.obj;
+                    final int handle = t.sensor.getHandle();
+
+                    switch (t.sensor.getType()) {
+                        // Only report accuracy for sensors that support it.
+                        case Sensor.TYPE_MAGNETIC_FIELD:
+                        case Sensor.TYPE_ORIENTATION:
+                            // call onAccuracyChanged() only if the value changes
+                            final int accuracy = mSensorAccuracies.get(handle);
+                            if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+                                mSensorAccuracies.put(handle, t.accuracy);
+                                mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy);
+                            }
+                            break;
+                        default:
+                            // For other sensors, just report the accuracy once
+                            if (mFirstEvent.get(handle) == false) {
+                                mFirstEvent.put(handle, true);
+                                mSensorEventListener.onAccuracyChanged(
+                                        t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
+                            }
+                            break;
+                    }
+
+                    mSensorEventListener.onSensorChanged(t);
+                    sPool.returnToPool(t);
+                }
+            };
+            addSensor(sensor);
+        }
+
+        Object getListener() {
+            return mSensorEventListener;
+        }
+
+        void addSensor(Sensor sensor) {
+            mSensors.put(sensor.getHandle(), true);
+            mSensorList.add(sensor);
+        }
+        int removeSensor(Sensor sensor) {
+            mSensors.delete(sensor.getHandle());
+            mSensorList.remove(sensor);
+            return mSensors.size();
+        }
+        boolean hasSensor(Sensor sensor) {
+            return mSensors.get(sensor.getHandle());
+        }
+        List<Sensor> getSensors() {
+            return mSensorList;
+        }
+
+        void onSensorChangedLocked(Sensor sensor, float[] values, long[] timestamp, int accuracy) {
+            SensorEvent t = sPool.getFromPool();
+            final float[] v = t.values;
+            v[0] = values[0];
+            v[1] = values[1];
+            v[2] = values[2];
+            t.timestamp = timestamp[0];
+            t.accuracy = accuracy;
+            t.sensor = sensor;
+            Message msg = Message.obtain();
+            msg.what = 0;
+            msg.obj = t;
+            msg.setAsynchronous(true);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public SystemSensorManager(Looper mainLooper) {
+        mMainLooper = mainLooper;
+
+        synchronized(sListeners) {
+            if (!sSensorModuleInitialized) {
+                sSensorModuleInitialized = true;
+
+                nativeClassInit();
+
+                // initialize the sensor list
+                sensors_module_init();
+                final ArrayList<Sensor> fullList = sFullSensorsList;
+                int i = 0;
+                do {
+                    Sensor sensor = new Sensor();
+                    i = sensors_module_get_next_sensor(sensor, i);
+
+                    if (i>=0) {
+                        //Log.d(TAG, "found sensor: " + sensor.getName() +
+                        //        ", handle=" + sensor.getHandle());
+                        fullList.add(sensor);
+                        sHandleToSensor.append(sensor.getHandle(), sensor);
+                    }
+                } while (i>0);
+
+                sPool = new SensorEventPool( sFullSensorsList.size()*2 );
+                sSensorThread = new SensorThread();
+            }
+        }
+    }
+
+    /** @hide */
+    @Override
+    protected List<Sensor> getFullSensorList() {
+        return sFullSensorsList;
+    }
+
+    private boolean enableSensorLocked(Sensor sensor, int delay) {
+        boolean result = false;
+        for (ListenerDelegate i : sListeners) {
+            if (i.hasSensor(sensor)) {
+                String name = sensor.getName();
+                int handle = sensor.getHandle();
+                result = sensors_enable_sensor(sQueue, name, handle, delay);
+                break;
+            }
+        }
+        return result;
+    }
+
+    private boolean disableSensorLocked(Sensor sensor) {
+        for (ListenerDelegate i : sListeners) {
+            if (i.hasSensor(sensor)) {
+                // not an error, it's just that this sensor is still in use
+                return true;
+            }
+        }
+        String name = sensor.getName();
+        int handle = sensor.getHandle();
+        return sensors_enable_sensor(sQueue, name, handle, SENSOR_DISABLE);
+    }
+
+    /** @hide */
+    protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
+            int delay, Handler handler) {
+        boolean result = true;
+        synchronized (sListeners) {
+            // look for this listener in our list
+            ListenerDelegate l = null;
+            for (ListenerDelegate i : sListeners) {
+                if (i.getListener() == listener) {
+                    l = i;
+                    break;
+                }
+            }
+
+            // if we don't find it, add it to the list
+            if (l == null) {
+                l = new ListenerDelegate(listener, sensor, handler);
+                sListeners.add(l);
+                // if the list is not empty, start our main thread
+                if (!sListeners.isEmpty()) {
+                    if (sSensorThread.startLocked()) {
+                        if (!enableSensorLocked(sensor, delay)) {
+                            // oops. there was an error
+                            sListeners.remove(l);
+                            result = false;
+                        }
+                    } else {
+                        // there was an error, remove the listener
+                        sListeners.remove(l);
+                        result = false;
+                    }
+                } else {
+                    // weird, we couldn't add the listener
+                    result = false;
+                }
+            } else if (!l.hasSensor(sensor)) {
+                l.addSensor(sensor);
+                if (!enableSensorLocked(sensor, delay)) {
+                    // oops. there was an error
+                    l.removeSensor(sensor);
+                    result = false;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /** @hide */
+    @Override
+    protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
+        synchronized (sListeners) {
+            final int size = sListeners.size();
+            for (int i=0 ; i<size ; i++) {
+                ListenerDelegate l = sListeners.get(i);
+                if (l.getListener() == listener) {
+                    if (sensor == null) {
+                        sListeners.remove(i);
+                        // disable all sensors for this listener
+                        for (Sensor s : l.getSensors()) {
+                            disableSensorLocked(s);
+                        }
+                    } else if (l.removeSensor(sensor) == 0) {
+                        // if we have no more sensors enabled on this listener,
+                        // take it off the list.
+                        sListeners.remove(i);
+                        disableSensorLocked(sensor);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    private static native void nativeClassInit();
+
+    private static native int sensors_module_init();
+    private static native int sensors_module_get_next_sensor(Sensor sensor, int next);
+
+    // Used within this module from outside SensorManager, don't make private
+    static native int sensors_create_queue();
+    static native void sensors_destroy_queue(int queue);
+    static native boolean sensors_enable_sensor(int queue, String name, int sensor, int enable);
+    static native int sensors_data_poll(int queue, float[] values, int[] status, long[] timestamp);
+}
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 53ce32d..c579e6e 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -26,12 +26,18 @@
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
+import android.text.TextUtils;
+import android.text.method.WordIterator;
 import android.util.Log;
 import android.view.textservice.SentenceSuggestionsInfo;
 import android.view.textservice.SuggestionsInfo;
 import android.view.textservice.TextInfo;
+import android.widget.SpellChecker;
 
 import java.lang.ref.WeakReference;
+import java.text.BreakIterator;
+import java.util.ArrayList;
+import java.util.Locale;
 
 /**
  * SpellCheckerService provides an abstract base class for a spell checker.
@@ -92,6 +98,7 @@
      */
     public static abstract class Session {
         private InternalISpellCheckerSession mInternalSession;
+        private volatile SentenceLevelAdapter mSentenceLevelAdapter;
 
         /**
          * @hide
@@ -142,8 +149,8 @@
 
         /**
          * Get sentence suggestions for specified texts in an array of TextInfo.
-         * The default implementation returns an array of SentenceSuggestionsInfo by simply
-         * calling onGetSuggestions.
+         * The default implementation splits the input text to words and returns
+         * {@link SentenceSuggestionsInfo} which contains suggestions for each word.
          * This function will run on the incoming IPC thread.
          * So, this is not called on the main thread,
          * but will be called in series on another thread.
@@ -156,14 +163,41 @@
          */
         public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
                 int suggestionsLimit) {
-            final int length = textInfos.length;
-            final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length];
-            for (int i = 0; i < length; ++i) {
-                final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit);
-                si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence());
-                final int N = textInfos[i].getText().length();
-                retval[i] = new SentenceSuggestionsInfo(
-                        new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N});
+            if (textInfos == null || textInfos.length == 0) {
+                return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
+            }
+            if (DBG) {
+                Log.d(TAG, "onGetSentenceSuggestionsMultiple: + " + textInfos.length + ", "
+                        + suggestionsLimit);
+            }
+            if (mSentenceLevelAdapter == null) {
+                synchronized(this) {
+                    if (mSentenceLevelAdapter == null) {
+                        final String localeStr = getLocale();
+                        if (!TextUtils.isEmpty(localeStr)) {
+                            mSentenceLevelAdapter = new SentenceLevelAdapter(new Locale(localeStr));
+                        }
+                    }
+                }
+            }
+            if (mSentenceLevelAdapter == null) {
+                return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
+            }
+            final int infosSize = textInfos.length;
+            final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[infosSize];
+            for (int i = 0; i < infosSize; ++i) {
+                final SentenceLevelAdapter.SentenceTextInfoParams textInfoParams =
+                        mSentenceLevelAdapter.getSplitWords(textInfos[i]);
+                final ArrayList<SentenceLevelAdapter.SentenceWordItem> mItems =
+                        textInfoParams.mItems;
+                final int itemsSize = mItems.size();
+                final TextInfo[] splitTextInfos = new TextInfo[itemsSize];
+                for (int j = 0; j < itemsSize; ++j) {
+                    splitTextInfos[j] = mItems.get(j).mTextInfo;
+                }
+                retval[i] = SentenceLevelAdapter.reconstructSuggestions(
+                        textInfoParams, onGetSuggestionsMultiple(
+                                splitTextInfos, suggestionsLimit, true));
             }
             return retval;
         }
@@ -290,4 +324,135 @@
             return internalSession;
         }
     }
+
+    /**
+     * Adapter class to accommodate word level spell checking APIs to sentence level spell checking
+     * APIs used in
+     * {@link SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)}
+     */
+    private static class SentenceLevelAdapter {
+        public static final SentenceSuggestionsInfo[] EMPTY_SENTENCE_SUGGESTIONS_INFOS =
+                new SentenceSuggestionsInfo[] {};
+        private static final SuggestionsInfo EMPTY_SUGGESTIONS_INFO = new SuggestionsInfo(0, null);
+        /**
+         * Container for split TextInfo parameters
+         */
+        public static class SentenceWordItem {
+            public final TextInfo mTextInfo;
+            public final int mStart;
+            public final int mLength;
+            public SentenceWordItem(TextInfo ti, int start, int end) {
+                mTextInfo = ti;
+                mStart = start;
+                mLength = end - start;
+            }
+        }
+
+        /**
+         * Container for originally queried TextInfo and parameters
+         */
+        public static class SentenceTextInfoParams {
+            final TextInfo mOriginalTextInfo;
+            final ArrayList<SentenceWordItem> mItems;
+            final int mSize;
+            public SentenceTextInfoParams(TextInfo ti, ArrayList<SentenceWordItem> items) {
+                mOriginalTextInfo = ti;
+                mItems = items;
+                mSize = items.size();
+            }
+        }
+
+        private final WordIterator mWordIterator;
+        public SentenceLevelAdapter(Locale locale) {
+            mWordIterator = new WordIterator(locale);
+        }
+
+        private SentenceTextInfoParams getSplitWords(TextInfo originalTextInfo) {
+            final WordIterator wordIterator = mWordIterator;
+            final CharSequence originalText = originalTextInfo.getText();
+            final int cookie = originalTextInfo.getCookie();
+            final int start = 0;
+            final int end = originalText.length();
+            final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>();
+            wordIterator.setCharSequence(originalText, 0, originalText.length());
+            int wordEnd = wordIterator.following(start);
+            int wordStart = wordIterator.getBeginning(wordEnd);
+            if (DBG) {
+                Log.d(TAG, "iterator: break: ---- 1st word start = " + wordStart + ", end = "
+                        + wordEnd + "\n" + originalText);
+            }
+            while (wordStart <= end && wordEnd != BreakIterator.DONE
+                    && wordStart != BreakIterator.DONE) {
+                if (wordEnd >= start && wordEnd > wordStart) {
+                    final String query = originalText.subSequence(wordStart, wordEnd).toString();
+                    final TextInfo ti = new TextInfo(query, cookie, query.hashCode());
+                    wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
+                    if (DBG) {
+                        Log.d(TAG, "Adapter: word (" + (wordItems.size() - 1) + ") " + query);
+                    }
+                }
+                wordEnd = wordIterator.following(wordEnd);
+                if (wordEnd == BreakIterator.DONE) {
+                    break;
+                }
+                wordStart = wordIterator.getBeginning(wordEnd);
+            }
+            if (originalText.length() >= SpellChecker.WORD_ITERATOR_INTERVAL
+                    && wordItems.size() >= 2) {
+                if (DBG) {
+                    Log.w(TAG, "Remove possibly divided word: "
+                            + wordItems.get(0).mTextInfo.getText());
+                }
+                wordItems.remove(0);
+            }
+            return new SentenceTextInfoParams(originalTextInfo, wordItems);
+        }
+
+        public static SentenceSuggestionsInfo reconstructSuggestions(
+                SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) {
+            if (results == null || results.length == 0) {
+                return null;
+            }
+            if (DBG) {
+                Log.w(TAG, "Adapter: onGetSuggestions: got " + results.length);
+            }
+            if (originalTextInfoParams == null) {
+                if (DBG) {
+                    Log.w(TAG, "Adapter: originalTextInfoParams is null.");
+                }
+                return null;
+            }
+            final int originalCookie = originalTextInfoParams.mOriginalTextInfo.getCookie();
+            final int originalSequence =
+                    originalTextInfoParams.mOriginalTextInfo.getSequence();
+
+            final int querySize = originalTextInfoParams.mSize;
+            final int[] offsets = new int[querySize];
+            final int[] lengths = new int[querySize];
+            final SuggestionsInfo[] reconstructedSuggestions = new SuggestionsInfo[querySize];
+            for (int i = 0; i < querySize; ++i) {
+                final SentenceWordItem item = originalTextInfoParams.mItems.get(i);
+                SuggestionsInfo result = null;
+                for (int j = 0; j < results.length; ++j) {
+                    final SuggestionsInfo cur = results[j];
+                    if (cur != null && cur.getSequence() == item.mTextInfo.getSequence()) {
+                        result = cur;
+                        result.setCookieAndSequence(originalCookie, originalSequence);
+                        break;
+                    }
+                }
+                offsets[i] = item.mStart;
+                lengths[i] = item.mLength;
+                reconstructedSuggestions[i] = result != null ? result : EMPTY_SUGGESTIONS_INFO;
+                if (DBG) {
+                    final int size = reconstructedSuggestions[i].getSuggestionsCount();
+                    Log.w(TAG, "reconstructedSuggestions(" + i + ")" + size + ", first = "
+                            + (size > 0 ? reconstructedSuggestions[i].getSuggestionAt(0)
+                                    : "<none>") + ", offset = " + offsets[i] + ", length = "
+                            + lengths[i]);
+                }
+            }
+            return new SentenceSuggestionsInfo(reconstructedSuggestions, offsets, lengths);
+        }
+    }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8fe8e40..b70d7b5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -80,6 +80,8 @@
     void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
     int getPendingAppTransition();
     void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim);
+    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
+            int startHeight);
     void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
             IRemoteCallback startedCallback);
     void executeAppTransition();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d569ba1..0ded5f9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5814,18 +5814,11 @@
      *
      * @see #FOCUSABLES_ALL
      * @see #FOCUSABLES_TOUCH_MODE
-     * @see #FOCUSABLES_ACCESSIBILITY
      */
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
         if (views == null) {
             return;
         }
-        if ((focusableMode & FOCUSABLE_IN_TOUCH_MODE) == FOCUSABLE_IN_TOUCH_MODE) {
-            if (isFocusable() && (!isInTouchMode() || isFocusableInTouchMode())) {
-                views.add(this);
-                return;
-            }
-        }
         if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
             if (AccessibilityManager.getInstance(mContext).isEnabled()
                     && includeForAccessibility()) {
@@ -5833,14 +5826,14 @@
                 return;
             }
         }
-        if ((focusableMode & FOCUSABLES_ALL) == FOCUSABLES_ALL) {
-            if (isFocusable()) {
-                views.add(this);
-                return;
-            }
-        } else {
-            throw new IllegalArgumentException("Unknow focusable mode: " + focusableMode);
+        if (!isFocusable()) {
+            return;
         }
+        if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
+                && isInTouchMode() && !isFocusableInTouchMode()) {
+            return;
+        }
+        views.add(this);
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 5e02b49..0b973e5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4449,8 +4449,7 @@
      * Returns the basis of alignment during the layout of this view group:
      * either {@link #COMPONENT_BOUNDS} or {@link #LAYOUT_BOUNDS}.
      *
-     * @return whether or not this view group should use the component or layout bounds during
-     * layout operations
+     * @return the layout mode to use during layout operations
      *
      * @see #setLayoutMode(int)
      */
@@ -4479,8 +4478,7 @@
      * value is {@link #LAYOUT_BOUNDS} for target SDK's greater than JellyBean,
      * {@link #LAYOUT_BOUNDS} otherwise.
      *
-     * @return whether or not this view group should use the component or layout bounds during
-     * layout operations
+     * @param layoutMode the layout mode to use during layout operations
      *
      * @see #getLayoutMode()
      */
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
index d05c1af..2167862 100644
--- a/core/java/android/view/textservice/SpellCheckerInfo.java
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -45,7 +45,6 @@
     private final ResolveInfo mService;
     private final String mId;
     private final int mLabel;
-    private final boolean mSupportsSentenceSpellCheck;
 
     /**
      * The spell checker setting activity's name, used by the system settings to
@@ -98,9 +97,6 @@
             label = sa.getResourceId(com.android.internal.R.styleable.SpellChecker_label, 0);
             settingsActivityComponent = sa.getString(
                     com.android.internal.R.styleable.SpellChecker_settingsActivity);
-            mSupportsSentenceSpellCheck = sa.getBoolean(
-                    com.android.internal.R.styleable.SpellChecker_supportsSentenceSpellCheck,
-                    false);
             sa.recycle();
 
             final int depth = parser.getDepth();
@@ -142,7 +138,6 @@
      */
     public SpellCheckerInfo(Parcel source) {
         mLabel = source.readInt();
-        mSupportsSentenceSpellCheck = source.readInt() != 0;
         mId = source.readString();
         mSettingsActivityName = source.readString();
         mService = ResolveInfo.CREATOR.createFromParcel(source);
@@ -158,13 +153,6 @@
     }
 
     /**
-     * @hide
-     */
-    public boolean isSentenceSpellCheckSupported() {
-        return mSupportsSentenceSpellCheck;
-    }
-
-    /**
      * Return the component of the service that implements.
      */
     public ComponentName getComponent() {
@@ -188,7 +176,6 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mLabel);
-        dest.writeInt(mSupportsSentenceSpellCheck ? 1 : 0);
         dest.writeString(mId);
         dest.writeString(mSettingsActivityName);
         mService.writeToParcel(dest, flags);
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 9dc05e4..628da3c 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -91,8 +91,6 @@
      * This meta-data must reference an XML resource.
      **/
     public static final String SERVICE_META_DATA = "android.view.textservice.scs";
-    private static final String SUPPORT_SENTENCE_SPELL_CHECK = "SupportSentenceSpellCheck";
-
 
     private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
     private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;
@@ -191,7 +189,9 @@
      * Get candidate strings for a substring of the specified text.
      * @param textInfo text metadata for a spell checker
      * @param suggestionsLimit the maximum number of suggestions that will be returned
+     * @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead
      */
+    @Deprecated
     public void getSuggestions(TextInfo textInfo, int suggestionsLimit) {
         getSuggestions(new TextInfo[] {textInfo}, suggestionsLimit, false);
     }
@@ -201,13 +201,14 @@
      * @param textInfos an array of text metadata for a spell checker
      * @param suggestionsLimit the maximum number of suggestions that will be returned
      * @param sequentialWords true if textInfos can be treated as sequential words.
+     * @deprecated use {@link SpellCheckerSession#getSentenceSuggestions(TextInfo[], int)} instead
      */
+    @Deprecated
     public void getSuggestions(
             TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
         if (DBG) {
             Log.w(TAG, "getSuggestions from " + mSpellCheckerInfo.getId());
         }
-        // TODO: Handle multiple words suggestions by using WordBreakIterator
         mSpellCheckerSessionListenerImpl.getSuggestionsMultiple(
                 textInfos, suggestionsLimit, sequentialWords);
     }
@@ -281,7 +282,7 @@
                         break;
                     case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
                         if (DBG) {
-                            Log.w(TAG, "Get suggestions from the spell checker.");
+                            Log.w(TAG, "Get sentence suggestions from the spell checker.");
                         }
                         try {
                             session.onGetSentenceSuggestionsMultiple(
@@ -492,11 +493,4 @@
     public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
         return mSpellCheckerSessionListenerImpl;
     }
-
-    /**
-     * @return true if the spell checker supports sentence level spell checking APIs
-     */
-    public boolean isSentenceSpellCheckSupported() {
-        return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK);
-    }
 }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 105285c..ba48da1 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -190,15 +190,19 @@
     }
 
     /**
-     * Sets whether the WebView should use its built-in zoom mechanisms, as
-     * opposed to separate zoom controls. The built-in zoom mechanisms comprise
-     * on-screen zoom controls, which are displayed over the WebView's content,
-     * and the use of a pinch gesture to control zooming. Whether or not these
-     * on-screen controls are displayed can be set with {@link #setDisplayZoomControls}.
-     * The separate zoom controls are no longer supported, so it is recommended
-     * that this setting is always enabled.
-     * @param enabled Whether the WebView should use the built-in zoom mechanism.
+     * Sets whether the WebView should use its built-in zoom mechanisms. The
+     * built-in zoom mechanisms comprise on-screen zoom controls, which are
+     * displayed over the WebView's content, and the use of a pinch gesture to
+     * control zooming. Whether or not these on-screen controls are displayed
+     * can be set with {@link #setDisplayZoomControls}.
+     * <p>
+     * The built-in mechanisms are the only currently supported zoom
+     * mechanisms, so it is recommended that this setting is always enabled.
+     * @param enabled Whether the WebView should use its built-in zoom mechanisms.
      */
+    // This method was intended to select between the built-in zoom mechanisms
+    // and the separate zoom controls. The latter were obtained using
+    // {@link WebView#getZoomControls}, which is now hidden.
     public void setBuiltInZoomControls(boolean enabled) {
         throw new MustOverrideException();
     }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5498622..bd10cca 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1530,17 +1530,15 @@
     }
 
     /**
-     * Returns a view containing zoom controls i.e. +/- buttons. The caller is
-     * in charge of installing this view to the view hierarchy. This view will
-     * become visible when the user starts scrolling via touch and fade away if
-     * the user does not interact with it.
+     * Gets the zoom controls for the WebView, as a separate View. The caller is
+     * responsible for inserting this View into the layout hierarchy.
      * <p/>
-     * API version 3 introduces a built-in zoom mechanism that is shown
-     * automatically by the MapView. This is the preferred approach for
-     * showing the zoom UI.
+     * API Level 3 introduced built-in zoom mechanisms for the WebView, as
+     * opposed to these separate zoom controls. The built-in mechanisms are
+     * preferred and can be enabled using
+     * {@link WebSettings#setBuiltInZoomControls}.
      *
-     * @deprecated The built-in zoom mechanism is preferred, see
-     *             {@link WebSettings#setBuiltInZoomControls(boolean)}.
+     * @deprecated The built-in zoom mechanisms are preferred.
      * @hide since API version 16.
      */
     @Deprecated
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index c725b64..e188207 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -116,7 +116,7 @@
                     null /* Bundle not currently used by the textServicesManager */,
                     mCurrentLocale, this,
                     false /* means any available languages from current spell checker */);
-            mIsSentenceSpellCheckSupported = mSpellCheckerSession.isSentenceSpellCheckSupported();
+            mIsSentenceSpellCheckSupported = true;
         }
 
         // Restore SpellCheckSpans in pool
diff --git a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
new file mode 100644
index 0000000..adfd3dc
--- /dev/null
+++ b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2012 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.widget;
+
+import com.android.internal.R;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.widget.RemoteViews.RemoteView;
+
+/**
+ * A layout that switches between its children based on the requested layout height.
+ * Each child specifies its minimum and maximum valid height.  Results are undefined
+ * if children specify overlapping ranges.  A child may specify the maximum height
+ * as 'unbounded' to indicate that it is willing to be displayed arbitrarily tall.
+ *
+ * <p>
+ * See {@link SizeAdaptiveLayout.LayoutParams} for a full description of the
+ * layout parameters used by SizeAdaptiveLayout.
+ */
+@RemoteView
+public class SizeAdaptiveLayout extends ViewGroup {
+
+    private static final String TAG = "SizeAdaptiveLayout";
+    private static final boolean DEBUG = false;
+    private static final long CROSSFADE_TIME = 250;
+
+    // TypedArray indices
+    private static final int MIN_VALID_HEIGHT =
+            R.styleable.SizeAdaptiveLayout_Layout_layout_minHeight;
+    private static final int MAX_VALID_HEIGHT =
+            R.styleable.SizeAdaptiveLayout_Layout_layout_maxHeight;
+
+    // view state
+    private View mActiveChild;
+    private View mLastActive;
+
+    // animation state
+    private AnimatorSet mTransitionAnimation;
+    private AnimatorListener mAnimatorListener;
+    private ObjectAnimator mFadePanel;
+    private ObjectAnimator mFadeView;
+    private int mCanceledAnimationCount;
+    private View mEnteringView;
+    private View mLeavingView;
+    // View used to hide larger views under smaller ones to create a uniform crossfade
+    private View mModestyPanel;
+    private int mModestyPanelTop;
+
+    public SizeAdaptiveLayout(Context context) {
+        super(context);
+        initialize();
+    }
+
+    public SizeAdaptiveLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initialize();
+    }
+
+    public SizeAdaptiveLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initialize();
+    }
+
+    private void initialize() {
+        mModestyPanel = new View(getContext());
+        // If the SizeAdaptiveLayout has a solid background, use it as a transition hint.
+        if (getBackground() instanceof ColorDrawable) {
+            mModestyPanel.setBackgroundDrawable(getBackground());
+        } else {
+            mModestyPanel.setBackgroundColor(Color.BLACK);
+        }
+        SizeAdaptiveLayout.LayoutParams layout =
+                new SizeAdaptiveLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                                    ViewGroup.LayoutParams.MATCH_PARENT);
+        mModestyPanel.setLayoutParams(layout);
+        addView(mModestyPanel);
+        mFadePanel = ObjectAnimator.ofFloat(mModestyPanel, "alpha", 0f);
+        mFadeView = ObjectAnimator.ofFloat(null, "alpha", 0f);
+        mAnimatorListener = new BringToFrontOnEnd();
+        mTransitionAnimation = new AnimatorSet();
+        mTransitionAnimation.play(mFadeView).with(mFadePanel);
+        mTransitionAnimation.setDuration(CROSSFADE_TIME);
+        mTransitionAnimation.addListener(mAnimatorListener);
+    }
+
+    /**
+     * Visible for testing
+     * @hide
+     */
+    public Animator getTransitionAnimation() {
+        return mTransitionAnimation;
+    }
+
+    /**
+     * Visible for testing
+     * @hide
+     */
+    public View getModestyPanel() {
+        return mModestyPanel;
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        mLastActive = null;
+        // make sure all views start off invisible.
+        for (int i = 0; i < getChildCount(); i++) {
+            getChildAt(i).setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (DEBUG) Log.d(TAG, this + " measure spec: " +
+                         MeasureSpec.toString(heightMeasureSpec));
+        View model = selectActiveChild(heightMeasureSpec);
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) model.getLayoutParams();
+        if (DEBUG) Log.d(TAG, "active min: " + lp.minHeight + " max: " + lp.maxHeight);
+        measureChild(model, widthMeasureSpec, heightMeasureSpec);
+        int childHeight = model.getMeasuredHeight();
+        int childWidth = model.getMeasuredHeight();
+        int childState = combineMeasuredStates(0, model.getMeasuredState());
+        if (DEBUG) Log.d(TAG, "measured child at: " + childHeight);
+        int resolvedWidth = resolveSizeAndState(childWidth, widthMeasureSpec, childState);
+        int resolvedheight = resolveSizeAndState(childHeight, heightMeasureSpec, childState);
+        setMeasuredDimension(resolvedWidth, resolvedheight);
+        if (DEBUG) Log.d(TAG, "resolved to: " + resolvedheight);
+    }
+
+    //TODO extend to width and height
+    private View selectActiveChild(int heightMeasureSpec) {
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+        View unboundedView = null;
+        View tallestView = null;
+        int tallestViewSize = 0;
+        View smallestView = null;
+        int smallestViewSize = Integer.MAX_VALUE;
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            if (child != mModestyPanel) {
+                SizeAdaptiveLayout.LayoutParams lp =
+                    (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
+                if (DEBUG) Log.d(TAG, "looking at " + i +
+                                 " with min: " + lp.minHeight +
+                                 " max: " +  lp.maxHeight);
+                if (lp.maxHeight == SizeAdaptiveLayout.LayoutParams.UNBOUNDED &&
+                    unboundedView == null) {
+                    unboundedView = child;
+                }
+                if (lp.maxHeight > tallestViewSize) {
+                    tallestViewSize = lp.maxHeight;
+                    tallestView = child;
+                }
+                if (lp.minHeight < smallestViewSize) {
+                    smallestViewSize = lp.minHeight;
+                    smallestView = child;
+                }
+                if (heightMode != MeasureSpec.UNSPECIFIED &&
+                    heightSize >= lp.minHeight && heightSize <= lp.maxHeight) {
+                    if (DEBUG) Log.d(TAG, "  found exact match, finishing early");
+                    return child;
+                }
+            }
+        }
+        if (unboundedView != null) {
+            tallestView = unboundedView;
+        }
+        if (heightMode == MeasureSpec.UNSPECIFIED) {
+            return tallestView;
+        }
+        if (heightSize > tallestViewSize) {
+            return tallestView;
+        }
+        return smallestView;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (DEBUG) Log.d(TAG, this + " onlayout height: " + (bottom - top));
+        mLastActive = mActiveChild;
+        int measureSpec = View.MeasureSpec.makeMeasureSpec(bottom - top,
+                                                           View.MeasureSpec.EXACTLY);
+        mActiveChild = selectActiveChild(measureSpec);
+        mActiveChild.setVisibility(View.VISIBLE);
+
+        if (mLastActive != mActiveChild && mLastActive != null) {
+            if (DEBUG) Log.d(TAG, this + " changed children from: " + mLastActive +
+                    " to: " + mActiveChild);
+
+            mEnteringView = mActiveChild;
+            mLeavingView = mLastActive;
+
+            mEnteringView.setAlpha(1f);
+
+            mModestyPanel.setAlpha(1f);
+            mModestyPanel.bringToFront();
+            mModestyPanelTop = mLeavingView.getHeight();
+            mModestyPanel.setVisibility(View.VISIBLE);
+            // TODO: mModestyPanel background should be compatible with mLeavingView
+
+            mLeavingView.bringToFront();
+
+            if (mTransitionAnimation.isRunning()) {
+                mTransitionAnimation.cancel();
+            }
+            mFadeView.setTarget(mLeavingView);
+            mFadeView.setFloatValues(0f);
+            mFadePanel.setFloatValues(0f);
+            mTransitionAnimation.setupStartValues();
+            mTransitionAnimation.start();
+        }
+        final int childWidth = mActiveChild.getMeasuredWidth();
+        final int childHeight = mActiveChild.getMeasuredHeight();
+        // TODO investigate setting LAYER_TYPE_HARDWARE on mLastActive
+        mActiveChild.layout(0, 0, 0 + childWidth, 0 + childHeight);
+
+        if (DEBUG) Log.d(TAG, "got modesty offset of " + mModestyPanelTop);
+        mModestyPanel.layout(0, mModestyPanelTop, 0 + childWidth, mModestyPanelTop + childHeight);
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        if (DEBUG) Log.d(TAG, "generate layout from attrs");
+        return new SizeAdaptiveLayout.LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        if (DEBUG) Log.d(TAG, "generate default layout from viewgroup");
+        return new SizeAdaptiveLayout.LayoutParams(p);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        if (DEBUG) Log.d(TAG, "generate default layout from null");
+        return new SizeAdaptiveLayout.LayoutParams();
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof SizeAdaptiveLayout.LayoutParams;
+    }
+
+    /**
+     * Per-child layout information associated with ViewSizeAdaptiveLayout.
+     *
+     * TODO extend to width and height
+     *
+     * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_minHeight
+     * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_maxHeight
+     */
+    public static class LayoutParams extends ViewGroup.LayoutParams {
+
+        /**
+         * Indicates the minimum valid height for the child.
+         */
+        @ViewDebug.ExportedProperty(category = "layout")
+        public int minHeight;
+
+        /**
+         * Indicates the maximum valid height for the child.
+         */
+        @ViewDebug.ExportedProperty(category = "layout")
+        public int maxHeight;
+
+        /**
+         * Constant value for maxHeight that indicates there is not maximum height.
+         */
+        public static final int UNBOUNDED = -1;
+
+        /**
+         * {@inheritDoc}
+         */
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            if (DEBUG) {
+                Log.d(TAG, "construct layout from attrs");
+                for (int i = 0; i < attrs.getAttributeCount(); i++) {
+                    Log.d(TAG, " " + attrs.getAttributeName(i) + " = " +
+                          attrs.getAttributeValue(i));
+                }
+            }
+            TypedArray a =
+                    c.obtainStyledAttributes(attrs,
+                            R.styleable.SizeAdaptiveLayout_Layout);
+
+            minHeight = a.getDimensionPixelSize(MIN_VALID_HEIGHT, 0);
+            if (DEBUG) Log.d(TAG, "got minHeight of: " + minHeight);
+
+            try {
+                maxHeight = a.getLayoutDimension(MAX_VALID_HEIGHT, UNBOUNDED);
+                if (DEBUG) Log.d(TAG, "got maxHeight of: " + maxHeight);
+            } catch (Exception e) {
+                if (DEBUG) Log.d(TAG, "caught exception looking for maxValidHeight " + e);
+            }
+
+            a.recycle();
+        }
+
+        /**
+         * Creates a new set of layout parameters with the specified width, height
+         * and valid height bounds.
+         *
+         * @param width the width, either {@link #MATCH_PARENT},
+         *        {@link #WRAP_CONTENT} or a fixed size in pixels
+         * @param height the height, either {@link #MATCH_PARENT},
+         *        {@link #WRAP_CONTENT} or a fixed size in pixels
+         * @param minHeight the minimum height of this child
+         * @param maxHeight the maximum height of this child
+         *        or {@link #UNBOUNDED} if the child can grow forever
+         */
+        public LayoutParams(int width, int height, int minHeight, int maxHeight) {
+            super(width, height);
+            this.minHeight = minHeight;
+            this.maxHeight = maxHeight;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public LayoutParams(int width, int height) {
+            this(width, height, UNBOUNDED, UNBOUNDED);
+        }
+
+        /**
+         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
+         */
+        public LayoutParams() {
+            this(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public LayoutParams(ViewGroup.LayoutParams p) {
+            super(p);
+            minHeight = UNBOUNDED;
+            maxHeight = UNBOUNDED;
+        }
+
+        public String debug(String output) {
+            return output + "SizeAdaptiveLayout.LayoutParams={" +
+                    ", max=" + maxHeight +
+                    ", max=" + minHeight + "}";
+        }
+    }
+
+    class BringToFrontOnEnd implements AnimatorListener {
+        @Override
+            public void onAnimationEnd(Animator animation) {
+            if (mCanceledAnimationCount == 0) {
+                mLeavingView.setVisibility(View.GONE);
+                mModestyPanel.setVisibility(View.GONE);
+                mEnteringView.bringToFront();
+                mEnteringView = null;
+                mLeavingView = null;
+            } else {
+                mCanceledAnimationCount--;
+            }
+        }
+
+        @Override
+            public void onAnimationCancel(Animator animation) {
+            mCanceledAnimationCount++;
+        }
+
+        @Override
+            public void onAnimationRepeat(Animator animation) {
+            if (DEBUG) Log.d(TAG, "fade animation repeated: should never happen.");
+            assert(false);
+        }
+
+        @Override
+            public void onAnimationStart(Animator animation) {
+        }
+    }
+}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 202abf6..9abfb3a 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -179,6 +179,6 @@
 
 int register_android_hardware_SensorManager(JNIEnv *env)
 {
-    return jniRegisterNativeMethods(env, "android/hardware/SensorManager",
+    return jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager",
             gMethods, NELEM(gMethods));
 }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index de24d10..6f489d4 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2367,8 +2367,6 @@
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. -->
         <attr name="settingsActivity"/>
-        <!-- Set true when the spell checker supports sentence level spell checking. -->
-        <attr name="supportsSentenceSpellCheck" format="boolean" />
     </declare-styleable>
 
     <!-- This is the subtype of the spell checker. Subtype can describe locales (e.g. en_US, fr_FR...) -->
@@ -5382,6 +5380,21 @@
     </declare-styleable>
 
     <!-- =============================== -->
+    <!-- SizeAdaptiveLayout class attributes -->
+    <!-- =============================== -->
+    <eat-comment />
+    <declare-styleable name="SizeAdaptiveLayout_Layout">
+      <!-- The maximum valid height for this item. -->
+      <attr name="layout_maxHeight" format="dimension">
+        <!-- Indicates that the view may be resized arbitrarily large. -->
+        <enum name="unbounded" value="-1" />
+      </attr>
+      <!-- The minimum valid height for this item. -->
+      <attr name="layout_minHeight" format="dimension" />
+    </declare-styleable>
+    <declare-styleable name="SizeAdaptiveLayout" />
+
+    <!-- =============================== -->
     <!-- LockPatternView class attributes -->
     <!-- =============================== -->
     <eat-comment />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4a5e442..8ac94fb 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3588,8 +3588,6 @@
 
   <public type="attr" name="parentActivityName" />
 
-  <public type="attr" name="supportsSentenceSpellCheck" />
-
   <public type="attr" name="importantForAccessibility"/>
 
 </resources>
diff --git a/core/tests/coretests/res/drawable/abe.jpg b/core/tests/coretests/res/drawable/abe.jpg
new file mode 100644
index 0000000..1f978a9
--- /dev/null
+++ b/core/tests/coretests/res/drawable/abe.jpg
Binary files differ
diff --git a/core/tests/coretests/res/drawable/gettysburg.png b/core/tests/coretests/res/drawable/gettysburg.png
new file mode 100644
index 0000000..7a2d596
--- /dev/null
+++ b/core/tests/coretests/res/drawable/gettysburg.png
Binary files differ
diff --git a/core/tests/coretests/res/layout/size_adaptive.xml b/core/tests/coretests/res/layout/size_adaptive.xml
new file mode 100644
index 0000000..03d0574
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<com.android.internal.widget.SizeAdaptiveLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/multi1"
+    android:layout_width="match_parent"
+    android:layout_height="64dp" >
+
+    <include
+        android:id="@+id/one_u"
+        layout="@layout/size_adaptive_one_u"
+        android:layout_width="fill_parent"
+        android:layout_height="64dp"
+        internal:layout_minHeight="64dp"
+        internal:layout_maxHeight="64dp"
+        />
+
+    <include
+        android:id="@+id/four_u"
+        layout="@layout/size_adaptive_four_u"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        internal:layout_minHeight="65dp"
+        internal:layout_maxHeight="unbounded"/>
+
+</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_color.xml b/core/tests/coretests/res/layout/size_adaptive_color.xml
new file mode 100644
index 0000000..cdb7a59
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_color.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<com.android.internal.widget.SizeAdaptiveLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+    android:background="#ffffff"
+    android:id="@+id/multi1"
+    android:layout_width="match_parent"
+    android:layout_height="64dp" >
+
+    <include
+        android:id="@+id/one_u"
+        layout="@layout/size_adaptive_one_u"
+        android:layout_width="fill_parent"
+        android:layout_height="64dp"
+        internal:layout_minHeight="64dp"
+        internal:layout_maxHeight="64dp"
+        />
+
+    <include
+        android:id="@+id/four_u"
+        layout="@layout/size_adaptive_four_u"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        internal:layout_minHeight="65dp"
+        internal:layout_maxHeight="unbounded"/>
+
+</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_four_u.xml b/core/tests/coretests/res/layout/size_adaptive_four_u.xml
new file mode 100644
index 0000000..232b921
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_four_u.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/gridLayout4"
+    android:layout_width="match_parent"
+    android:layout_height="256dp"
+    android:background="#000000"
+    android:columnCount="2"
+    android:padding="1dp" >
+
+    <ImageView
+        android:id="@+id/actor"
+        android:layout_width="62dp"
+        android:layout_height="62dp"
+        android:layout_row="0"
+        android:layout_column="0"
+        android:layout_rowSpan="2"
+        android:contentDescription="@string/actor"
+        android:src="@drawable/abe" />
+
+    <TextView
+        android:layout_width="0dp"
+        android:id="@+id/name"
+        android:layout_row="0"
+        android:layout_column="1"
+        android:layout_gravity="fill_horizontal"
+        android:padding="3dp"
+        android:text="@string/actor"
+        android:textColor="#ffffff"
+        android:textStyle="bold" />
+
+    <ImageView
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_row="1"
+        android:layout_column="1"
+        android:layout_gravity="fill_horizontal"
+        android:padding="5dp"
+        android:adjustViewBounds="true"
+        android:background="#555555"
+        android:scaleType="centerCrop"
+        android:src="@drawable/gettysburg"
+        android:contentDescription="@string/caption" />
+
+    <TextView
+        android:layout_width="0dp"
+        android:id="@+id/note"
+        android:layout_row="2"
+        android:layout_column="1"
+        android:layout_gravity="fill_horizontal"
+        android:padding="3dp"
+        android:singleLine="true"
+        android:text="@string/first"
+        android:textColor="#ffffff" />
+</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml b/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml
new file mode 100644
index 0000000..93a10de
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/gridLayout4"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="#000000"
+    android:columnCount="2"
+    android:padding="1dp"
+    android:orientation="horizontal" >
+
+    <ImageView
+        android:id="@+id/actor"
+        android:layout_width="62dp"
+        android:layout_height="62dp"
+        android:layout_row="0"
+        android:layout_column="0"
+        android:layout_rowSpan="2"
+        android:contentDescription="@string/actor"
+        android:src="@drawable/abe" />
+
+    <TextView
+        android:layout_width="0dp"
+        android:id="@+id/name"
+        android:layout_row="0"
+        android:layout_column="1"
+        android:layout_gravity="fill_horizontal"
+        android:padding="3dp"
+        android:text="@string/actor"
+        android:textColor="#ffffff"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/note"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_column="1"
+        android:layout_gravity="fill_horizontal"
+        android:layout_marginTop="5dp"
+        android:layout_row="1"
+        android:padding="3dp"
+        android:singleLine="false"
+        android:text="@string/first"
+        android:textColor="#ffffff" />
+
+    </GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_gappy.xml b/core/tests/coretests/res/layout/size_adaptive_gappy.xml
new file mode 100644
index 0000000..d5e3b41
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_gappy.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<com.android.internal.widget.SizeAdaptiveLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/multi_with_gap"
+    android:layout_width="match_parent"
+    android:layout_height="64dp" >
+
+    <include
+        android:id="@+id/one_u"
+        layout="@layout/size_adaptive_one_u"
+        android:layout_width="fill_parent"
+        android:layout_height="64dp"
+        internal:layout_minHeight="64dp"
+        internal:layout_maxHeight="64dp"
+        />
+
+    <include
+        android:id="@+id/four_u"
+        layout="@layout/size_adaptive_four_u"
+        android:layout_width="fill_parent"
+        android:layout_height="256dp"
+        internal:layout_minHeight="128dp"
+        internal:layout_maxHeight="unbounded"/>
+
+</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_large_only.xml b/core/tests/coretests/res/layout/size_adaptive_large_only.xml
new file mode 100644
index 0000000..cf58265
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_large_only.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<com.android.internal.widget.SizeAdaptiveLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/large_only_multi"
+    android:layout_width="match_parent"
+    android:layout_height="64dp" >
+
+    <include
+        android:id="@+id/four_u"
+        layout="@layout/size_adaptive_four_u"
+        android:layout_width="fill_parent"
+        android:layout_height="256dp"
+        internal:layout_minHeight="65dp"
+        internal:layout_maxHeight="unbounded"/>
+
+</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_one_u.xml b/core/tests/coretests/res/layout/size_adaptive_one_u.xml
new file mode 100644
index 0000000..b6fe4a0
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_one_u.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/gridLayout1"
+    android:layout_width="match_parent"
+    android:layout_height="64dp"
+    android:background="#000000"
+    android:columnCount="3"
+    android:padding="1dp"
+    android:rowCount="2" >
+
+    <ImageView
+        android:id="@+id/actor"
+        android:layout_width="62dp"
+        android:layout_height="62dp"
+        android:layout_column="0"
+        android:layout_row="0"
+        android:layout_rowSpan="2"
+        android:contentDescription="@string/actor"
+        android:src="@drawable/abe" />
+
+    <TextView
+        android:id="@+id/name"
+        android:layout_gravity="fill"
+        android:padding="3dp"
+        android:text="@string/actor"
+        android:textColor="#ffffff"
+        android:textStyle="bold" />
+
+    <ImageView
+        android:layout_width="62dp"
+        android:layout_height="62dp"
+        android:layout_gravity="fill_vertical"
+        android:layout_rowSpan="2"
+        android:adjustViewBounds="true"
+        android:background="#555555"
+        android:padding="2dp"
+        android:scaleType="fitXY"
+        android:src="@drawable/gettysburg"
+        android:contentDescription="@string/caption" />
+
+    <TextView
+        android:id="@+id/note"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_gravity="fill"
+        android:layout_marginTop="5dp"
+        android:padding="3dp"
+        android:singleLine="true"
+        android:text="@string/first"
+        android:textColor="#ffffff" />
+
+</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml b/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml
new file mode 100644
index 0000000..df54eb6
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/gridLayout1"
+    android:layout_width="match_parent"
+    android:layout_height="64dp"
+    android:background="#000000"
+    android:columnCount="2"
+    android:padding="1dp"
+    android:rowCount="2" >
+
+    <ImageView
+        android:id="@+id/actor"
+        android:layout_width="62dp"
+        android:layout_height="62dp"
+        android:layout_column="0"
+        android:layout_row="0"
+        android:layout_rowSpan="2"
+        android:contentDescription="@string/actor"
+        android:src="@drawable/abe" />
+
+    <TextView
+        android:id="@+id/name"
+        android:layout_gravity="fill"
+        android:padding="3dp"
+        android:text="@string/actor"
+        android:textColor="#ffffff"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/note"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_gravity="fill"
+        android:layout_marginTop="5dp"
+        android:padding="3dp"
+        android:singleLine="true"
+        android:text="@string/first"
+        android:textColor="#ffffff" />
+
+</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_overlapping.xml b/core/tests/coretests/res/layout/size_adaptive_overlapping.xml
new file mode 100644
index 0000000..4abe8b0
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_overlapping.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<com.android.internal.widget.SizeAdaptiveLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/multi_with_overlap"
+    android:layout_width="match_parent"
+    android:layout_height="64dp" >
+
+    <include
+        android:id="@+id/one_u"
+        layout="@layout/size_adaptive_one_u"
+        android:layout_width="fill_parent"
+        android:layout_height="64dp"
+        internal:layout_minHeight="64dp"
+        internal:layout_maxHeight="64dp"
+        />
+
+    <include
+        android:id="@+id/four_u"
+        layout="@layout/size_adaptive_four_u"
+        android:layout_width="fill_parent"
+        android:layout_height="256dp"
+        internal:layout_minHeight="64dp"
+        internal:layout_maxHeight="256dp"/>
+
+</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_singleton.xml b/core/tests/coretests/res/layout/size_adaptive_singleton.xml
new file mode 100644
index 0000000..eba387f
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_singleton.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<com.android.internal.widget.SizeAdaptiveLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="64dp" >
+
+    <include
+        android:id="@+id/one_u"
+        layout="@layout/size_adaptive_one_u_text"
+        android:layout_width="fill_parent"
+        android:layout_height="64dp"
+        internal:layout_minHeight="64dp"
+        internal:layout_maxHeight="64dp"
+        />
+
+</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_text.xml b/core/tests/coretests/res/layout/size_adaptive_text.xml
new file mode 100644
index 0000000..a9f0ba9
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_text.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<com.android.internal.widget.SizeAdaptiveLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/multi1"
+    android:layout_width="match_parent"
+    android:layout_height="64dp" >
+
+    <include
+        android:id="@+id/one_u"
+        layout="@layout/size_adaptive_one_u_text"
+        android:layout_width="fill_parent"
+        android:layout_height="64dp"
+        internal:layout_minHeight="64dp"
+        internal:layout_maxHeight="64dp"
+        />
+
+    <include
+        android:id="@+id/four_u"
+        layout="@layout/size_adaptive_four_u_text"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        internal:layout_minHeight="65dp"
+        internal:layout_maxHeight="unbounded"/>
+
+</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_three_way.xml b/core/tests/coretests/res/layout/size_adaptive_three_way.xml
new file mode 100644
index 0000000..1eb5396
--- /dev/null
+++ b/core/tests/coretests/res/layout/size_adaptive_three_way.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<com.android.internal.widget.SizeAdaptiveLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/three_way_multi"
+    android:layout_width="match_parent"
+    android:layout_height="64dp" >
+
+    <include
+        android:id="@+id/one_u"
+        layout="@layout/size_adaptive_one_u"
+        android:layout_width="fill_parent"
+        android:layout_height="64dp"
+        internal:layout_minHeight="64dp"
+        internal:layout_maxHeight="64dp"
+        />
+
+    <include
+        android:id="@+id/two_u"
+        layout="@layout/size_adaptive_four_u"
+        android:layout_width="fill_parent"
+        android:layout_height="128dp"
+        internal:layout_minHeight="65dp"
+        internal:layout_maxHeight="128dp"/>
+
+    <include
+        android:id="@+id/four_u"
+        layout="@layout/size_adaptive_four_u"
+        android:layout_width="fill_parent"
+        android:layout_height="256dp"
+        internal:layout_minHeight="129dp"
+        internal:layout_maxHeight="unbounded"/>
+
+</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 71f3520..ce0d9a2 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -131,4 +131,8 @@
 
     <string name="textview_hebrew_text">&#x05DD;&#x05DE;ab?!</string>
 
+    <!-- SizeAdaptiveLayout -->
+    <string name="first">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.</string>
+    <string name="actor">Abe Lincoln</string>
+    <string name="caption">Lincoln adressing the crowd at Gettysburgh</string>
 </resources>
diff --git a/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java
new file mode 100644
index 0000000..fc83e4a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2012 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.widget;
+
+import com.android.frameworks.coretests.R;
+
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.widget.SizeAdaptiveLayout;
+
+
+public class SizeAdaptiveLayoutTest extends AndroidTestCase {
+
+    private LayoutInflater mInflater;
+    private int mOneU;
+    private int mFourU;
+    private SizeAdaptiveLayout mSizeAdaptiveLayout;
+    private View mSmallView;
+    private View mMediumView;
+    private View mLargeView;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // inflate the layout
+        final Context context = getContext();
+        mInflater = LayoutInflater.from(context);
+        mOneU = 64;
+        mFourU = 4 * mOneU;
+    }
+
+    private void inflate(int resource){
+        mSizeAdaptiveLayout = (SizeAdaptiveLayout) mInflater.inflate(resource, null);
+        mSizeAdaptiveLayout.onAttachedToWindow();
+
+        mSmallView = mSizeAdaptiveLayout.findViewById(R.id.one_u);
+        mMediumView = mSizeAdaptiveLayout.findViewById(R.id.two_u);
+        mLargeView = mSizeAdaptiveLayout.findViewById(R.id.four_u);
+    }
+
+    /**
+     * The name 'test preconditions' is a convention to signal that if this
+     * test doesn't pass, the test case was not set up properly and it might
+     * explain any and all failures in other tests.  This is not guaranteed
+     * to run before other tests, as junit uses reflection to find the tests.
+     */
+    @SmallTest
+    public void testPreconditions() {
+        assertNotNull(mInflater);
+
+        inflate(R.layout.size_adaptive);
+        assertNotNull(mSizeAdaptiveLayout);
+        assertNotNull(mSmallView);
+        assertNotNull(mLargeView);
+    }
+
+    @SmallTest
+    public void testOpenLarge() {
+        inflate(R.layout.size_adaptive);
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        int height = (int) lp.minHeight + 10;
+
+        measureAndLayout(height);
+
+        assertEquals("4U should be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+        assertEquals("1U should be gone",
+                View.GONE,
+                mSmallView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenSmall() {
+        inflate(R.layout.size_adaptive);
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        int height = (int) lp.minHeight;
+
+        measureAndLayout(height);
+
+        assertEquals("1U should be visible",
+                View.VISIBLE,
+                mSmallView.getVisibility());
+        assertEquals("4U should be gone",
+                View.GONE,
+                mLargeView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenTooSmall() {
+        inflate(R.layout.size_adaptive);
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        int height = (int) lp.minHeight - 10;
+
+        measureAndLayout(height);
+
+        assertEquals("1U should be visible",
+                View.VISIBLE,
+                mSmallView.getVisibility());
+        assertEquals("4U should be gone",
+                View.GONE,
+                mLargeView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenTooBig() {
+        inflate(R.layout.size_adaptive);
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        lp.maxHeight = 500;
+        mLargeView.setLayoutParams(lp);
+        int height = (int) (lp.minHeight + 10);
+
+        measureAndLayout(height);
+
+        assertEquals("4U should be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+        assertEquals("1U should be gone",
+                View.GONE,
+                mSmallView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenWrapContent() {
+        inflate(R.layout.size_adaptive_text);
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        int height = (int) lp.minHeight + 10;
+
+        // manually measure it, and lay it out
+        int measureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
+        mSizeAdaptiveLayout.measure(500, measureSpec);
+        assertTrue("should not be forced to 4U",
+                mSizeAdaptiveLayout.getMeasuredHeight() < mFourU);
+    }
+
+    @SmallTest
+    public void testOpenOneUOnlySmall() {
+        inflate(R.layout.size_adaptive_singleton);
+        assertNull("largeView should be NULL in the singleton layout", mLargeView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        int height = (int) lp.minHeight - 10;
+
+        measureAndLayout(height);
+
+        assertEquals("1U should be visible",
+                View.VISIBLE,
+                mSmallView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenOneUOnlyLarge() {
+        inflate(R.layout.size_adaptive_singleton);
+        assertNull("largeView should be NULL in the singleton layout", mLargeView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        int height = (int) lp.maxHeight + 10;
+
+        measureAndLayout(height);
+
+        assertEquals("1U should be visible",
+                View.VISIBLE,
+                mSmallView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenOneUOnlyJustRight() {
+        inflate(R.layout.size_adaptive_singleton);
+        assertNull("largeView should be NULL in the singleton layout", mLargeView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        int height = (int) lp.minHeight;
+
+        measureAndLayout(height);
+
+        assertEquals("1U should be visible",
+                View.VISIBLE,
+                mSmallView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenFourUOnlySmall() {
+        inflate(R.layout.size_adaptive_large_only);
+        assertNull("smallView should be NULL in the singleton layout", mSmallView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        int height = (int) lp.minHeight - 10;
+
+        measureAndLayout(height);
+
+        assertEquals("4U should be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenFourUOnlyLarge() {
+        inflate(R.layout.size_adaptive_large_only);
+        assertNull("smallView should be NULL in the singleton layout", mSmallView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        int height = (int) lp.maxHeight + 10;
+
+        measureAndLayout(height);
+
+        assertEquals("4U should be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenFourUOnlyJustRight() {
+        inflate(R.layout.size_adaptive_large_only);
+        assertNull("smallView should be NULL in the singleton layout", mSmallView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        int height = (int) lp.minHeight;
+
+        measureAndLayout(height);
+
+        assertEquals("4U should be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenIntoAGap() {
+        inflate(R.layout.size_adaptive_gappy);
+
+        SizeAdaptiveLayout.LayoutParams smallParams =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        SizeAdaptiveLayout.LayoutParams largeParams =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        assertTrue("gappy layout should have a gap",
+                smallParams.maxHeight + 10 < largeParams.minHeight);
+        int height = (int) smallParams.maxHeight + 10;
+
+        measureAndLayout(height);
+
+        assertTrue("one and only one view should be visible",
+                mLargeView.getVisibility() != mSmallView.getVisibility());
+        // behavior is undefined in this case.
+    }
+
+    @SmallTest
+    public void testOpenIntoAnOverlap() {
+        inflate(R.layout.size_adaptive_overlapping);
+
+        SizeAdaptiveLayout.LayoutParams smallParams =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        SizeAdaptiveLayout.LayoutParams largeParams =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        assertEquals("overlapping layout should overlap",
+                smallParams.minHeight,
+                largeParams.minHeight);
+        int height = (int) smallParams.maxHeight;
+
+        measureAndLayout(height);
+
+        assertTrue("one and only one view should be visible",
+                mLargeView.getVisibility() != mSmallView.getVisibility());
+        assertEquals("1U should get priority in an overlap because it is first",
+                View.VISIBLE,
+                mSmallView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenThreeWayViewSmall() {
+        inflate(R.layout.size_adaptive_three_way);
+        assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        int height = (int) lp.minHeight;
+
+        measureAndLayout(height);
+
+        assertEquals("1U should be visible",
+                View.VISIBLE,
+                mSmallView.getVisibility());
+        assertEquals("2U should be gone",
+                View.GONE,
+                mMediumView.getVisibility());
+        assertEquals("4U should be gone",
+                View.GONE,
+                mLargeView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenThreeWayViewMedium() {
+        inflate(R.layout.size_adaptive_three_way);
+        assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mMediumView.getLayoutParams();
+        int height = (int) lp.minHeight;
+
+        measureAndLayout(height);
+
+        assertEquals("1U should be gone",
+                View.GONE,
+                mSmallView.getVisibility());
+        assertEquals("2U should be visible",
+                View.VISIBLE,
+                mMediumView.getVisibility());
+        assertEquals("4U should be gone",
+                View.GONE,
+                mLargeView.getVisibility());
+    }
+
+    @SmallTest
+    public void testOpenThreeWayViewLarge() {
+        inflate(R.layout.size_adaptive_three_way);
+        assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
+
+        SizeAdaptiveLayout.LayoutParams lp =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        int height = (int) lp.minHeight;
+
+        measureAndLayout(height);
+
+        assertEquals("1U should be gone",
+                View.GONE,
+                mSmallView.getVisibility());
+        assertEquals("2U should be gone",
+                View.GONE,
+                mMediumView.getVisibility());
+        assertEquals("4U should be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+    }
+
+    @SmallTest
+    public void testResizeWithoutAnimation() {
+        inflate(R.layout.size_adaptive);
+
+        SizeAdaptiveLayout.LayoutParams largeParams =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        int startHeight = (int) largeParams.minHeight + 10;
+        int endHeight = (int) largeParams.minHeight + 10;
+
+        measureAndLayout(startHeight);
+
+        assertEquals("4U should be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+        assertFalse("There should be no animation on initial rendering.",
+                    mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
+
+        measureAndLayout(endHeight);
+
+        assertEquals("4U should still be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+        assertFalse("There should be no animation on scale within a view.",
+                    mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
+    }
+
+    @SmallTest
+    public void testResizeWithAnimation() {
+        inflate(R.layout.size_adaptive);
+
+        SizeAdaptiveLayout.LayoutParams smallParams =
+          (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
+        SizeAdaptiveLayout.LayoutParams largeParams =
+          (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
+        int startHeight = (int) largeParams.minHeight + 10;
+        int endHeight = (int) smallParams.maxHeight;
+
+        measureAndLayout(startHeight);
+
+        assertEquals("4U should be visible",
+                View.VISIBLE,
+                mLargeView.getVisibility());
+        assertFalse("There should be no animation on initial rendering.",
+                    mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
+
+        measureAndLayout(endHeight);
+
+        assertEquals("1U should now be visible",
+                View.VISIBLE,
+                mSmallView.getVisibility());
+        assertTrue("There should be an animation on scale between views.",
+                   mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
+    }
+
+    @SmallTest
+    public void testModestyPanelChangesColorWhite() {
+        inflate(R.layout.size_adaptive_color);
+        View panel = mSizeAdaptiveLayout.getModestyPanel();
+        assertTrue("ModestyPanel should have a ColorDrawable background",
+                   panel.getBackground() instanceof ColorDrawable);
+        ColorDrawable panelColor = (ColorDrawable) panel.getBackground();
+        ColorDrawable salColor = (ColorDrawable) mSizeAdaptiveLayout.getBackground();
+        assertEquals("ModestyPanel color should match the SizeAdaptiveLayout",
+                     panelColor.getColor(), salColor.getColor());
+    }
+
+    @SmallTest
+    public void testModestyPanelHasDefault() {
+        inflate(R.layout.size_adaptive);
+        View panel = mSizeAdaptiveLayout.getModestyPanel();
+        assertNull("SizeAdaptiveLayout should have no background for this test",
+                     mSizeAdaptiveLayout.getBackground());
+        assertTrue("ModestyPanel should have a ColorDrawable background",
+                   panel.getBackground() instanceof ColorDrawable);
+    }
+
+    private void measureAndLayout(int height) {
+        // manually measure it, and lay it out
+        int measureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
+        mSizeAdaptiveLayout.measure(500, measureSpec);
+        mSizeAdaptiveLayout.layout(0, 0, 500, height);
+    }
+}
diff --git a/docs/html/shareables/training/LocationAware.zip b/docs/html/shareables/training/LocationAware.zip
index 46970cd..8ed97cb 100644
--- a/docs/html/shareables/training/LocationAware.zip
+++ b/docs/html/shareables/training/LocationAware.zip
Binary files differ
diff --git a/docs/html/training/location/locationmanager.jd b/docs/html/training/location/locationmanager.jd
index 5da1205..61abcbd 100644
--- a/docs/html/training/location/locationmanager.jd
+++ b/docs/html/training/location/locationmanager.jd
@@ -18,6 +18,7 @@
   <li><a href="locationmanager.html#TaskDeclarePermissions">Declare Proper Permissions in Android Manifest</a></li>
   <li><a href="locationmanager.html#TaskGetLocationManagerRef">Get a Reference to LocationManager</a></li>
   <li><a href="locationmanager.html#TaskPickLocationProvider">Pick a Location Provider</a></li>
+  <li><a href="locationmanager.html#TaskVerifyProvider">Verify the Location Provider is Enabled</a></li>
 </ol>
 
 <h2>You should also read</h2>
@@ -88,3 +89,32 @@
    ...
 }
 </pre>
+
+<h2 id="TaskVerifyProvider">Verify the Location Provider is Enabled</h2>
+
+<p>Some location providers such as the GPS can be disabled in Settings.  It is good practice to check whether the desired location provider is currently enabled by calling the {@link android.location.LocationManager#isProviderEnabled(java.lang.String) isProviderEnabled()} method.  If the location provider is disabled, you can offer the user an opportunity to enable it in Settings by firing an {@link android.content.Intent} with the {@link android.provider.Settings#ACTION_LOCATION_SOURCE_SETTINGS} action.</p>
+
+<pre>
+&#64;Override
+protected void onStart() {
+    super.onStart();
+
+    // This verification should be done during onStart() because the system calls
+    // this method when the user returns to the activity, which ensures the desired
+    // location provider is enabled each time the activity resumes from the stopped state.
+    LocationManager locationManager =
+            (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+    final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
+
+    if (!gpsEnabled) {
+        // Build an alert dialog here that requests that the user enable
+        // the location services, then when the user clicks the "OK" button,
+        // call enableLocationSettings()
+    }
+}
+
+private void enableLocationSettings() {
+    Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+    startActivity(settingsIntent);
+}
+</pre>
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 2a4d59b..8acbae3 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -1298,7 +1298,9 @@
                             }
 
                             // Update the pause state.
+                            boolean pausing = false;
                             if (mPaused != mRequestPaused) {
+                                pausing = mRequestPaused;
                                 mPaused = mRequestPaused;
                                 sGLThreadManager.notifyAll();
                                 if (LOG_PAUSE_RESUME) {
@@ -1324,12 +1326,16 @@
                                 lostEglContext = false;
                             }
 
-                            // Do we need to release the EGL surface?
-                            if (mHaveEglSurface && mPaused) {
+                            // When pausing, release the EGL surface:
+                            if (pausing && mHaveEglSurface) {
                                 if (LOG_SURFACE) {
                                     Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
                                 }
                                 stopEglSurfaceLocked();
+                            }
+
+                            // When pausing, optionally release the EGL Context:
+                            if (pausing && mHaveEglContext) {
                                 GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                                 boolean preserveEglContextOnPause = view == null ?
                                         false : view.mPreserveEGLContextOnPause;
@@ -1339,6 +1345,10 @@
                                         Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
                                     }
                                 }
+                            }
+
+                            // When pausing, optionally terminate EGL:
+                            if (pausing) {
                                 if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
                                     mEglHelper.finish();
                                     if (LOG_SURFACE) {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2d2a881..9a371c6 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -37,6 +37,7 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.SystemSensorManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -2946,7 +2947,7 @@
     }
 
     void systemReady() {
-        mSensorManager = new SensorManager(mHandlerThread.getLooper());
+        mSensorManager = new SystemSensorManager(mHandlerThread.getLooper());
         mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
         // don't bother with the light sensor if auto brightness is handled in hardware
         if (mUseSoftwareAutoBrightness) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index e2a2c83..bb38cd9 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1202,6 +1202,8 @@
         pw.println();
         pw.println("WifiWatchdogStateMachine dump");
         mWifiWatchdogStateMachine.dump(pw);
+        pw.println("WifiStateMachine dump");
+        mWifiStateMachine.dump(fd, pw, args);
     }
 
     private class WifiLock extends DeathRecipient {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 754a4dd..ed2a6c0 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1004,14 +1004,16 @@
             mNotificationTimeout = info.notificationTimeout;
             mIsDefault = (info.flags & DEFAULT) != 0;
 
-            final int targetSdkVersion =
-                info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion;
-            // TODO: Uncomment this line and remove the line below when JellyBean
-            // SDK version is finalized.
-            // if (targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
-            if (targetSdkVersion > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
-                mIncludeNotImportantViews =
-                    (info.flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+            if (!mIsAutomation) {
+                final int targetSdkVersion =
+                    info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion;
+                // TODO: Uncomment this line and remove the line below when JellyBean
+                // SDK version is finalized.
+                // if (targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+                if (targetSdkVersion > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+                    mIncludeNotImportantViews =
+                        (info.flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+                }
             }
 
             synchronized (mLock) {
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index a098f18..cce8e7a 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -558,6 +558,11 @@
                             pendingOptions.getCustomEnterResId(),
                             pendingOptions.getCustomExitResId());
                     break;
+                case ActivityOptions.ANIM_SCALE_UP:
+                    service.mWindowManager.overridePendingAppTransitionScaleUp(
+                            pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getStartWidth(), pendingOptions.getStartHeight());
+                    break;
                 case ActivityOptions.ANIM_THUMBNAIL:
                     service.mWindowManager.overridePendingAppTransitionThumb(
                             pendingOptions.getThumbnail(),
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 0fe6921..0458a67 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -50,6 +50,7 @@
 
 import android.Manifest;
 import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
 import android.app.IActivityManager;
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
@@ -497,6 +498,7 @@
     // mOpeningApps and mClosingApps are the lists of tokens that will be
     // made visible or hidden at the next transition.
     int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
+    int mNextAppTransitionType = ActivityOptions.ANIM_NONE;
     String mNextAppTransitionPackage;
     Bitmap mNextAppTransitionThumbnail;
     IRemoteCallback mNextAppTransitionCallback;
@@ -504,6 +506,8 @@
     int mNextAppTransitionExit;
     int mNextAppTransitionStartX;
     int mNextAppTransitionStartY;
+    int mNextAppTransitionStartWidth;
+    int mNextAppTransitionStartHeight;
     boolean mAppTransitionReady = false;
     boolean mAppTransitionRunning = false;
     boolean mAppTransitionTimeout = false;
@@ -3072,6 +3076,50 @@
         return null;
     }
 
+    private Animation createScaleUpAnimationLocked(int transit, boolean enter) {
+        Animation a;
+        // Pick the desired duration.  If this is an inter-activity transition,
+        // it  is the standard duration for that.  Otherwise we use the longer
+        // task transition duration.
+        int duration;
+        switch (transit) {
+            case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+            case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+                duration = mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_shortAnimTime);
+                break;
+            default:
+                duration = 500;
+                break;
+        }
+        if (enter) {
+            // Entering app zooms out from the center of the initial rect.
+            Animation scale = new ScaleAnimation(
+                    mNextAppTransitionStartWidth/mAppDisplayWidth, 1,
+                    mNextAppTransitionStartHeight/mAppDisplayHeight, 1,
+                    mNextAppTransitionStartX + mNextAppTransitionStartWidth/2,
+                    mNextAppTransitionStartY + mNextAppTransitionStartHeight/2);
+            AnimationSet set = new AnimationSet(true);
+            Animation alpha = new AlphaAnimation(0, 1);
+            scale.setDuration(duration);
+            set.addAnimation(scale);
+            alpha.setDuration(duration);
+            set.addAnimation(alpha);
+            a = set;
+        } else {
+            // Exiting app just holds in place.
+            a = new AlphaAnimation(1, 1);
+            a.setDuration(duration);
+        }
+        a.setFillAfter(true);
+        final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
+                com.android.internal.R.interpolator.decelerate_quint);
+        a.setInterpolator(interpolator);
+        a.initialize(mAppDisplayWidth, mAppDisplayHeight,
+                mAppDisplayWidth, mAppDisplayHeight);
+        return a;
+    }
+
     private Animation createThumbnailAnimationLocked(int transit,
             boolean enter, boolean thumb) {
         Animation a;
@@ -3090,7 +3138,6 @@
             default:
                 duration = 500;
                 break;
-            
         }
         if (thumb) {
             // Animation for zooming thumbnail from its initial size to
@@ -3138,12 +3185,15 @@
         if (okToDisplay()) {
             Animation a;
             boolean initialized = false;
-            if (mNextAppTransitionThumbnail != null) {
-                a = createThumbnailAnimationLocked(transit, enter, false);
-                initialized = true;
-            } else if (mNextAppTransitionPackage != null) {
+            if (mNextAppTransitionType == ActivityOptions.ANIM_CUSTOM) {
                 a = loadAnimation(mNextAppTransitionPackage, enter ?
                         mNextAppTransitionEnter : mNextAppTransitionExit);
+            } else if (mNextAppTransitionType == ActivityOptions.ANIM_SCALE_UP) {
+                a = createScaleUpAnimationLocked(transit, enter);
+                initialized = true;
+            } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL) {
+                a = createThumbnailAnimationLocked(transit, enter, false);
+                initialized = true;
             } else {
                 int animAttr = 0;
                 switch (transit) {
@@ -3738,6 +3788,7 @@
     public void overridePendingAppTransition(String packageName,
             int enterAnim, int exitAnim) {
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+            mNextAppTransitionType = ActivityOptions.ANIM_CUSTOM;
             mNextAppTransitionPackage = packageName;
             mNextAppTransitionThumbnail = null;
             mNextAppTransitionEnter = enterAnim;
@@ -3745,9 +3796,23 @@
         }
     }
 
+    public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
+            int startHeight) {
+        if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+            mNextAppTransitionType = ActivityOptions.ANIM_SCALE_UP;
+            mNextAppTransitionPackage = null;
+            mNextAppTransitionThumbnail = null;
+            mNextAppTransitionStartX = startX;
+            mNextAppTransitionStartY = startY;
+            mNextAppTransitionStartWidth = startWidth;
+            mNextAppTransitionStartHeight = startHeight;
+        }
+    }
+
     public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX,
             int startY, IRemoteCallback startedCallback) {
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+            mNextAppTransitionType = ActivityOptions.ANIM_THUMBNAIL;
             mNextAppTransitionPackage = null;
             mNextAppTransitionThumbnail = srcThumb;
             mNextAppTransitionStartX = startX;
@@ -7845,6 +7910,7 @@
                 }
             }
 
+            mNextAppTransitionType = ActivityOptions.ANIM_NONE;
             mNextAppTransitionPackage = null;
             mNextAppTransitionThumbnail = null;
             if (mNextAppTransitionCallback != null) {
@@ -8810,6 +8876,7 @@
 
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
             mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
+            mNextAppTransitionType = ActivityOptions.ANIM_NONE;
             mNextAppTransitionPackage = null;
             mNextAppTransitionThumbnail = null;
             mAppTransitionReady = true;
@@ -9350,20 +9417,34 @@
                     pw.print(Integer.toHexString(mNextAppTransition));
                     pw.print(" mAppTransitionReady="); pw.println(mAppTransitionReady);
             pw.print("  mAppTransitionRunning="); pw.print(mAppTransitionRunning);
-                    pw.print(" mAppTransitionTimeout="); pw.println( mAppTransitionTimeout);
-            if (mNextAppTransitionPackage != null) {
-                pw.print("  mNextAppTransitionPackage=");
-                    pw.print(mNextAppTransitionPackage);
-                    pw.print(" mNextAppTransitionEnter=0x");
-                    pw.print(Integer.toHexString(mNextAppTransitionEnter));
-                    pw.print(" mNextAppTransitionExit=0x");
-                    pw.print(Integer.toHexString(mNextAppTransitionExit));
+                    pw.print(" mAppTransitionTimeout="); pw.println(mAppTransitionTimeout);
+            if (mNextAppTransitionType != ActivityOptions.ANIM_NONE) {
+                pw.print("  mNextAppTransitionType="); pw.println(mNextAppTransitionType);
             }
-            if (mNextAppTransitionThumbnail != null) {
-                pw.print("  mNextAppTransitionThumbnail=");
-                    pw.print(mNextAppTransitionThumbnail);
-                    pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
-                    pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY);
+            switch (mNextAppTransitionType) {
+                case ActivityOptions.ANIM_CUSTOM:
+                    pw.print("  mNextAppTransitionPackage=");
+                            pw.print(mNextAppTransitionPackage);
+                            pw.print(" mNextAppTransitionEnter=0x");
+                            pw.print(Integer.toHexString(mNextAppTransitionEnter));
+                            pw.print(" mNextAppTransitionExit=0x");
+                            pw.print(Integer.toHexString(mNextAppTransitionExit));
+                    break;
+                case ActivityOptions.ANIM_SCALE_UP:
+                    pw.print("  mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
+                            pw.print(" mNextAppTransitionStartY=");
+                            pw.println(mNextAppTransitionStartY);
+                    pw.print("  mNextAppTransitionStartWidth=");
+                            pw.print(mNextAppTransitionStartWidth);
+                            pw.print(" mNextAppTransitionStartHeight=");
+                            pw.println(mNextAppTransitionStartHeight);
+                    break;
+                case ActivityOptions.ANIM_THUMBNAIL:
+                    pw.print("  mNextAppTransitionThumbnail=");
+                            pw.print(mNextAppTransitionThumbnail);
+                            pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
+                            pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY);
+                    break;
             }
             pw.print("  mStartingIconInTransition="); pw.print(mStartingIconInTransition);
                     pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 8cfdb79..183beb1 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -64,8 +64,8 @@
     /*
      * Calling Line Identification Restriction (CLIR)
      */
-    private static final String CLIR_ON = "*31#+";
-    private static final String CLIR_OFF = "#31#+";
+    private static final String CLIR_ON = "*31#";
+    private static final String CLIR_OFF = "#31#";
 
     /*
      * TOA = TON + NPI
@@ -213,23 +213,26 @@
 
         int len = phoneNumber.length();
         StringBuilder ret = new StringBuilder(len);
-        boolean firstCharAdded = false;
 
         for (int i = 0; i < len; i++) {
             char c = phoneNumber.charAt(i);
-            if (isDialable(c) && (c != '+' || !firstCharAdded)) {
-                firstCharAdded = true;
+            // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+            int digit = Character.digit(c, 10);
+            if (digit != -1) {
+                ret.append(digit);
+            } else if (c == '+') {
+                // Allow '+' as first character or after CLIR MMI prefix
+                String prefix = ret.toString();
+                if (prefix.length() == 0 || prefix.equals(CLIR_ON) || prefix.equals(CLIR_OFF)) {
+                    ret.append(c);
+                }
+            } else if (isDialable(c)) {
                 ret.append(c);
             } else if (isStartsPostDial (c)) {
                 break;
             }
         }
 
-        int pos = addPlusChar(phoneNumber);
-        if (pos >= 0 && ret.length() > pos) {
-            ret.insert(pos, '+');
-        }
-
         return ret.toString();
     }
 
@@ -283,7 +286,11 @@
 
         for (int i = 0; i < len; i++) {
             char c = phoneNumber.charAt(i);
-            if (isNonSeparator(c)) {
+            // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+            int digit = Character.digit(c, 10);
+            if (digit != -1) {
+                ret.append(digit);
+            } else if (isNonSeparator(c)) {
                 ret.append(c);
             }
         }
@@ -371,28 +378,6 @@
         }
     }
 
-    /** GSM codes
-     *  Finds if a GSM code includes the international prefix (+).
-     *
-     * @param number the number to dial.
-     *
-     * @return the position where the + char will be inserted, -1 if the GSM code was not found.
-     */
-    private static int
-    addPlusChar(String number) {
-        int pos = -1;
-
-        if (number.startsWith(CLIR_OFF)) {
-            pos = CLIR_OFF.length() - 1;
-        }
-
-        if (number.startsWith(CLIR_ON)) {
-            pos = CLIR_ON.length() - 1;
-        }
-
-        return pos;
-    }
-
     /**
      * Extracts the post-dial sequence of DTMF control digits, pauses, and
      * waits. Strips separators. This string may be empty, but will not be null
@@ -1504,7 +1489,11 @@
         int len = phoneNumber.length();
         for (int i = 0; i < len; i++) {
             char c = phoneNumber.charAt(i);
-            if ((i == 0 && c == '+') || PhoneNumberUtils.isISODigit(c)) {
+            // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+            int digit = Character.digit(c, 10);
+            if (digit != -1) {
+                sb.append(digit);
+            } else if (i == 0 && c == '+') {
                 sb.append(c);
             } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
                 return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
@@ -1513,6 +1502,27 @@
         return sb.toString();
     }
 
+    /**
+     * Replace arabic/unicode digits with decimal digits.
+     * @param number
+     *            the number to be normalized.
+     * @return the replaced number.
+     *
+     * @hide
+     */
+    public static String replaceUnicodeDigits(String number) {
+        StringBuilder normalizedDigits = new StringBuilder(number.length());
+        for (char c : number.toCharArray()) {
+            int digit = Character.digit(c, 10);
+            if (digit != -1) {
+                normalizedDigits.append(digit);
+            } else {
+                normalizedDigits.append(c);
+            }
+        }
+        return normalizedDigits.toString();
+    }
+
     // Three and four digit phone numbers for either special services,
     // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
     // not match.
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 5d1f758..b1a5872 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.database.Cursor;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.location.CountryDetector;
 import android.net.Uri;
@@ -107,10 +108,33 @@
     /**
      * Drawable representing the caller image.  This is essentially
      * a cache for the image data tied into the connection /
-     * callerinfo object.  The isCachedPhotoCurrent flag indicates
-     * if the image data needs to be reloaded.
+     * callerinfo object.
+     *
+     * This might be a high resolution picture which is more suitable
+     * for full-screen image view than for smaller icons used in some
+     * kinds of notifications.
+     *
+     * The {@link #isCachedPhotoCurrent} flag indicates if the image
+     * data needs to be reloaded.
      */
     public Drawable cachedPhoto;
+    /**
+     * Bitmap representing the caller image which has possibly lower
+     * resolution than {@link #cachedPhoto} and thus more suitable for
+     * icons (like notification icons).
+     *
+     * In usual cases this is just down-scaled image of {@link #cachedPhoto}.
+     * If the down-scaling fails, this will just become null.
+     *
+     * The {@link #isCachedPhotoCurrent} flag indicates if the image
+     * data needs to be reloaded.
+     */
+    public Bitmap cachedPhotoIcon;
+    /**
+     * Boolean which indicates if {@link #cachedPhoto} and
+     * {@link #cachedPhotoIcon} is fresh enough. If it is false,
+     * those images aren't pointing to valid objects.
+     */
     public boolean isCachedPhotoCurrent;
 
     private boolean mIsEmergency;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index cbd591f..a4473c8 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1192,10 +1192,14 @@
             if (targetSdk < 4) {
                 if (!hasWriteExternalStoragePermission) {
                     printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
+                    printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
+                            "'targetSdkVersion < 4'\n");
                     hasWriteExternalStoragePermission = true;
                 }
                 if (!hasReadPhoneStatePermission) {
                     printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
+                    printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
+                            "'targetSdkVersion < 4'\n");
                 }
             }
 
@@ -1203,15 +1207,21 @@
             // force them to always take READ_EXTERNAL_STORAGE as well.
             if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
                 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
+                printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
+                        "'requested WRITE_EXTERNAL_STORAGE'\n");
             }
 
             // Pre-JellyBean call log permission compatibility.
             if (targetSdk < 16) {
                 if (!hasReadCallLogPermission && hasReadContactsPermission) {
                     printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
+                    printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
+                            "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
                 }
                 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
                     printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
+                    printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
+                            "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
                 }
             }
 
@@ -1223,10 +1233,18 @@
              */
             // Camera-related back-compatibility logic
             if (!specCameraFeature) {
-                if (reqCameraFlashFeature || reqCameraAutofocusFeature) {
+                if (reqCameraFlashFeature) {
                     // if app requested a sub-feature (autofocus or flash) and didn't
                     // request the base camera feature, we infer that it meant to
                     printf("uses-feature:'android.hardware.camera'\n");
+                    printf("uses-implied-feature:'android.hardware.camera'," \
+                            "'requested android.hardware.camera.flash feature'\n");
+                } else if (reqCameraAutofocusFeature) {
+                    // if app requested a sub-feature (autofocus or flash) and didn't
+                    // request the base camera feature, we infer that it meant to
+                    printf("uses-feature:'android.hardware.camera'\n");
+                    printf("uses-implied-feature:'android.hardware.camera'," \
+                            "'requested android.hardware.camera.autofocus feature'\n");
                 } else if (hasCameraPermission) {
                     // if app wants to use camera but didn't request the feature, we infer 
                     // that it meant to, and further that it wants autofocus
@@ -1234,6 +1252,8 @@
                     printf("uses-feature:'android.hardware.camera'\n");
                     if (!specCameraAutofocusFeature) {
                         printf("uses-feature:'android.hardware.camera.autofocus'\n");
+                        printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
+                                "'requested android.permission.CAMERA permission'\n");
                     }
                 }
             }
@@ -1245,16 +1265,22 @@
                 // if app either takes a location-related permission or requests one of the
                 // sub-features, we infer that it also meant to request the base location feature
                 printf("uses-feature:'android.hardware.location'\n");
+                printf("uses-implied-feature:'android.hardware.location'," \
+                        "'requested a location access permission'\n");
             }
             if (!specGpsFeature && hasGpsPermission) {
                 // if app takes GPS (FINE location) perm but does not request the GPS
                 // feature, we infer that it meant to
                 printf("uses-feature:'android.hardware.location.gps'\n");
+                printf("uses-implied-feature:'android.hardware.location.gps'," \
+                        "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
             }
             if (!specNetworkLocFeature && hasCoarseLocPermission) {
                 // if app takes Network location (COARSE location) perm but does not request the
                 // network location feature, we infer that it meant to
                 printf("uses-feature:'android.hardware.location.network'\n");
+                printf("uses-implied-feature:'android.hardware.location.network'," \
+                        "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
             }
 
             // Bluetooth-related compatibility logic
@@ -1262,6 +1288,9 @@
                 // if app takes a Bluetooth permission but does not request the Bluetooth
                 // feature, we infer that it meant to
                 printf("uses-feature:'android.hardware.bluetooth'\n");
+                printf("uses-implied-feature:'android.hardware.bluetooth'," \
+                        "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
+                        "permission and targetSdkVersion > 4'\n");
             }
 
             // Microphone-related compatibility logic
@@ -1269,6 +1298,8 @@
                 // if app takes the record-audio permission but does not request the microphone
                 // feature, we infer that it meant to
                 printf("uses-feature:'android.hardware.microphone'\n");
+                printf("uses-implied-feature:'android.hardware.microphone'," \
+                        "'requested android.permission.RECORD_AUDIO permission'\n");
             }
 
             // WiFi-related compatibility logic
@@ -1276,6 +1307,10 @@
                 // if app takes one of the WiFi permissions but does not request the WiFi
                 // feature, we infer that it meant to
                 printf("uses-feature:'android.hardware.wifi'\n");
+                printf("uses-implied-feature:'android.hardware.wifi'," \
+                        "'requested android.permission.ACCESS_WIFI_STATE, " \
+                        "android.permission.CHANGE_WIFI_STATE, or " \
+                        "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
             }
 
             // Telephony-related compatibility logic
@@ -1283,6 +1318,8 @@
                 // if app takes one of the telephony permissions or requests a sub-feature but
                 // does not request the base telephony feature, we infer that it meant to
                 printf("uses-feature:'android.hardware.telephony'\n");
+                printf("uses-implied-feature:'android.hardware.telephony'," \
+                        "'requested a telephony-related permission or feature'\n");
             }
 
             // Touchscreen-related back-compatibility logic
@@ -1292,11 +1329,15 @@
                 // Note that specTouchscreenFeature is true if the tag is present, regardless
                 // of whether its value is true or false, so this is safe
                 printf("uses-feature:'android.hardware.touchscreen'\n");
+                printf("uses-implied-feature:'android.hardware.touchscreen'," \
+                        "'assumed you require a touch screen unless explicitly made optional'\n");
             }
             if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
                 // if app takes one of the telephony permissions or requests a sub-feature but
                 // does not request the base telephony feature, we infer that it meant to
                 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
+                printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
+                        "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
             }
 
             // Landscape/portrait-related compatibility logic
@@ -1306,9 +1347,13 @@
                 // orientation is required.
                 if (reqScreenLandscapeFeature) {
                     printf("uses-feature:'android.hardware.screen.landscape'\n");
+                    printf("uses-implied-feature:'android.hardware.screen.landscape'," \
+                            "'one or more activities have specified a landscape orientation'\n");
                 }
                 if (reqScreenPortraitFeature) {
                     printf("uses-feature:'android.hardware.screen.portrait'\n");
+                    printf("uses-implied-feature:'android.hardware.screen.portrait'," \
+                            "'one or more activities have specified a portrait orientation'\n");
                 }
             }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index e6c9351..44d28fa 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -229,6 +229,12 @@
     }
 
     @Override
+    public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
+            int startHeight) throws RemoteException {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
     public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
             IRemoteCallback startedCallback) throws RemoteException {
         // TODO Auto-generated method stub
diff --git a/wifi/java/android/net/wifi/StateChangeResult.java b/wifi/java/android/net/wifi/StateChangeResult.java
index 8ab5982..b15c4a6 100644
--- a/wifi/java/android/net/wifi/StateChangeResult.java
+++ b/wifi/java/android/net/wifi/StateChangeResult.java
@@ -23,12 +23,15 @@
  * @hide
  */
 public class StateChangeResult {
-    StateChangeResult(int networkId, String BSSID, SupplicantState state) {
+    StateChangeResult(int networkId, String SSID, String BSSID, SupplicantState state) {
         this.state = state;
+        this.SSID = SSID;
         this.BSSID = BSSID;
         this.networkId = networkId;
     }
+
     int networkId;
+    String SSID;
     String BSSID;
     SupplicantState state;
 }
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 3bd03f5..730e833 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -637,6 +637,9 @@
          * id=network-id state=new-state
          */
         private void handleSupplicantStateChange(String dataString) {
+            String SSID = null;
+            int index = dataString.indexOf("SSID=");
+            if (index != -1) SSID = dataString.substring(index);
             String[] dataTokens = dataString.split(" ");
 
             String BSSID = null;
@@ -657,7 +660,6 @@
                 try {
                     value = Integer.parseInt(nameValue[1]);
                 } catch (NumberFormatException e) {
-                    Log.w(TAG, "STATE-CHANGE non-integer parameter: " + token);
                     continue;
                 }
 
@@ -680,7 +682,7 @@
             if (newSupplicantState == SupplicantState.INVALID) {
                 Log.w(TAG, "Invalid supplicant state: " + newState);
             }
-            notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
+            notifySupplicantStateChange(networkId, SSID, BSSID, newSupplicantState);
         }
     }
 
@@ -729,11 +731,13 @@
      * Send the state machine a notification that the state of the supplicant
      * has changed.
      * @param networkId the configured network on which the state change occurred
+     * @param SSID network name
+     * @param BSSID network address
      * @param newState the new {@code SupplicantState}
      */
-    void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
+    void notifySupplicantStateChange(int networkId, String SSID, String BSSID, SupplicantState newState) {
         mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
-                new StateChangeResult(networkId, BSSID, newState)));
+                new StateChangeResult(networkId, SSID, BSSID, newState)));
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index cc0df52..2f14098 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -669,6 +669,7 @@
 
         setInitialState(mInitialState);
 
+        setProcessedMessagesSize(100);
         if (DBG) setDbg(true);
 
         //start the state machine
@@ -1121,6 +1122,37 @@
         return sb.toString();
     }
 
+    @Override
+    protected boolean recordProcessedMessage(Message msg) {
+        //Ignore screen on/off & common messages when driver has started
+        if (getCurrentState() == mConnectedState || getCurrentState() == mDisconnectedState) {
+            switch (msg.what) {
+                case CMD_LOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_START_DRIVER:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_SUSPEND_OPTIMIZATIONS:
+                case CMD_CLEAR_SUSPEND_OPTIMIZATIONS:
+                case CMD_ENABLE_BACKGROUND_SCAN:
+                case CMD_ENABLE_ALL_NETWORKS:
+                return false;
+            }
+        }
+
+        switch (msg.what) {
+            case CMD_START_SCAN:
+            case CMD_ENABLE_RSSI_POLL:
+            case CMD_RSSI_POLL:
+            case CMD_DELAYED_STOP_DRIVER:
+            case WifiMonitor.SCAN_RESULTS_EVENT:
+            case WifiWatchdogStateMachine.RSSI_FETCH:
+                return false;
+            default:
+                return true;
+        }
+    }
+
     /*********************************************************
      * Internal private functions
      ********************************************************/
@@ -1409,23 +1441,6 @@
         mScanResults = scanList;
     }
 
-    private String fetchSSID() {
-        String status = mWifiNative.status();
-        if (status == null) {
-            return null;
-        }
-        // extract ssid from a series of "name=value"
-        String[] lines = status.split("\n");
-        for (String line : lines) {
-            String[] prop = line.split(" *= *");
-            if (prop.length < 2) continue;
-            String name = prop[0];
-            String value = prop[1];
-            if (name.equalsIgnoreCase("ssid")) return value;
-        }
-        return null;
-    }
-
     /*
      * Fetch RSSI and linkspeed on current connection
      */
@@ -1586,6 +1601,7 @@
             /* BSSID is valid only in ASSOCIATING state */
             mWifiInfo.setBSSID(stateChangeResult.BSSID);
         }
+        mWifiInfo.setSSID(stateChangeResult.SSID);
 
         mSupplicantStateTracker.sendMessage(Message.obtain(message));
 
@@ -2015,7 +2031,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2068,7 +2083,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2148,7 +2162,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2169,7 +2182,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2250,7 +2262,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2274,7 +2285,6 @@
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
             WifiConfiguration config;
-            boolean eventLoggingEnabled = true;
             switch(message.what) {
                 case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
                     transitionTo(mSupplicantStoppingState);
@@ -2291,7 +2301,6 @@
                     sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     break;
                 case WifiMonitor.SCAN_RESULTS_EVENT:
-                    eventLoggingEnabled = false;
                     setScanResults(mWifiNative.scanResults());
                     sendScanResultsAvailableBroadcast();
                     mScanResultIsPending = false;
@@ -2381,9 +2390,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            if (eventLoggingEnabled) {
-                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-            }
             return HANDLED;
         }
 
@@ -2459,7 +2465,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2505,7 +2510,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2564,14 +2568,12 @@
         @Override
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
-            boolean eventLoggingEnabled = true;
             switch(message.what) {
                case CMD_SET_SCAN_TYPE:
                     mSetScanActive = (message.arg1 == SCAN_ACTIVE);
                     mWifiNative.setScanMode(mSetScanActive);
                     break;
                 case CMD_START_SCAN:
-                    eventLoggingEnabled = false;
                     boolean forceActive = (message.arg1 == SCAN_ACTIVE);
                     if (forceActive && !mSetScanActive) {
                         mWifiNative.setScanMode(forceActive);
@@ -2681,9 +2683,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            if (eventLoggingEnabled) {
-                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-            }
             return HANDLED;
         }
         @Override
@@ -2731,7 +2730,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2764,7 +2762,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2801,7 +2798,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2923,8 +2919,6 @@
                     mLastNetworkId = message.arg1;
                     mLastBssid = (String) message.obj;
 
-                    //TODO: make supplicant modification to push this in events
-                    mWifiInfo.setSSID(fetchSSID());
                     mWifiInfo.setBSSID(mLastBssid);
                     mWifiInfo.setNetworkId(mLastNetworkId);
                     /* send event to CM & network change broadcast */
@@ -2940,7 +2934,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -2959,7 +2952,6 @@
         @Override
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
-            boolean eventLoggingEnabled = true;
             switch (message.what) {
               case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                   handlePreDhcpSetup();
@@ -2988,7 +2980,6 @@
                     }
                     break;
                 case CMD_START_SCAN:
-                    eventLoggingEnabled = false;
                     /* When the network is connected, re-scanning can trigger
                      * a reconnection. Put it in scan-only mode during scan.
                      * When scan results are received, the mode is switched
@@ -3031,7 +3022,6 @@
                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
                     break;
                 case CMD_RSSI_POLL:
-                    eventLoggingEnabled = false;
                     if (message.arg1 == mRssiPollToken) {
                         // Get Info and continue polling
                         fetchRssiAndLinkSpeedNative();
@@ -3052,7 +3042,6 @@
                     }
                     break;
                 case WifiWatchdogStateMachine.RSSI_FETCH:
-                    eventLoggingEnabled = false;
                     fetchRssiAndLinkSpeedNative();
                     replyToMessage(message, WifiWatchdogStateMachine.RSSI_FETCH_SUCCEEDED,
                             mWifiInfo.getRssi());
@@ -3061,9 +3050,6 @@
                     return NOT_HANDLED;
             }
 
-            if (eventLoggingEnabled) {
-                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-            }
             return HANDLED;
         }
 
@@ -3131,7 +3117,6 @@
               default:
                   return NOT_HANDLED;
           }
-          EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
           return HANDLED;
       }
     }
@@ -3168,7 +3153,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -3202,7 +3186,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
         @Override
@@ -3240,7 +3223,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -3343,7 +3325,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
 
@@ -3432,7 +3413,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
 
@@ -3504,7 +3484,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -3548,7 +3527,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -3598,7 +3576,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -3629,7 +3606,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }
@@ -3692,7 +3668,6 @@
                 default:
                     return NOT_HANDLED;
             }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
     }