Merge "Set initial view scale to be at least overview scale."
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5c4f57a..c0714e3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -870,12 +870,6 @@
                             (dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ",
                             (dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ",
                             dbStats.cache, dbStats.dbName);
-                    if (dbStats.dataDump != null) {
-                        int size = dbStats.dataDump.size();
-                        for (int dumpIndex = 0; dumpIndex < size; dumpIndex++) {
-                            printRow(pw, "%s", dbStats.dataDump.get(dumpIndex));
-                        }
-                    }
                 }
             }
 
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 87f55d2..7efb7fd 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2507,7 +2507,7 @@
                     if (pageCount > 0) {
                         dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
                                 lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
-                                db.getCachesize(), getDataDump(db)));
+                                db.getCachesize()));
                     }
                 }
                 // if there are pooled connections, return the cache stats for them also.
@@ -2518,7 +2518,7 @@
                     for (SQLiteDatabase pDb : connPool.getConnectionList()) {
                         dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
                                 + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
-                                pDb.getCacheMissNum(), pDb.getCachesize(), null));
+                                pDb.getCacheMissNum(), pDb.getCachesize()));
                     }
                 }
             } catch (SQLiteException e) {
@@ -2529,44 +2529,6 @@
         return dbStatsList;
     }
 
-    private static ArrayList<String> getDataDump(SQLiteDatabase db) {
-        // create database dump of certain data from certain databases for debugging purposes
-        if (db.getPath().equalsIgnoreCase(
-                "/data/data/com.android.providers.downloads/databases/downloads.db")) {
-            String sql =
-                    "select * from downloads " +
-                    " where notificationpackage = 'com.google.android.gsf'" + 
-                    " or status >= 400";
-            Cursor cursor = db.rawQuery(sql, null);
-            try {
-                int count = cursor.getCount();
-                if (count == 0) {
-                    return null;
-                }
-                ArrayList<String> buff = new ArrayList<String>();
-                buff.add("  Data from downloads.db");
-                int columnCount = cursor.getColumnCount();
-                for (int i =0; i < count && cursor.moveToNext(); i++) {
-                    buff.add("    Row#" + i + "");
-                    for (int j = 0; j < columnCount; j++) {
-                        String colName = cursor.getColumnName(j);
-                        String value = cursor.getString(j);
-                        buff.add("      " + colName + " = " + value);
-                    }
-                }
-                for (String s : buff)  Log.i("vnoritag", s);
-                return buff;
-            } catch (SQLiteException e) {
-                Log.w(TAG, "exception in executing the sql: " + sql, e);
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-        return null;
-    }
-
     /**
      * Returns list of full pathnames of all attached databases including the main database
      * by executing 'pragma database_list' on the database.
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 72377f0..94960791 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -121,31 +121,27 @@
      */
     public static class DbStats {
         /** name of the database */
-        public final String dbName;
+        public String dbName;
 
         /** the page size for the database */
-        public final long pageSize;
+        public long pageSize;
 
         /** the database size */
-        public final long dbSize;
+        public long dbSize;
 
         /** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
-        public final int lookaside;
+        public int lookaside;
 
         /** statement cache stats: hits/misses/cachesize */
-        public final String cache;
-
-        /** database dump of 'useful info for debugging only */
-        public final ArrayList<String> dataDump;
+        public String cache;
 
         public DbStats(String dbName, long pageCount, long pageSize, int lookaside,
-            int hits, int misses, int cachesize, ArrayList<String> data) {
+            int hits, int misses, int cachesize) {
             this.dbName = dbName;
             this.pageSize = pageSize / 1024;
             dbSize = (pageCount * pageSize) / 1024;
             this.lookaside = lookaside;
             this.cache = hits + "/" + misses + "/" + cachesize;
-            this.dataDump = data;
         }
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 88d3f7a..45c46db 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7103,7 +7103,7 @@
                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
             }
 
-            if (isTextEditable()) {
+            if (isTextEditable() || mTextIsSelectable) {
                 if (mScrollX != oldScrollX || mScrollY != oldScrollY) {
                     // Hide insertion anchor while scrolling. Leave selection.
                     hideInsertionPointCursorController();
@@ -7153,7 +7153,7 @@
         }
 
         mInsertionControllerEnabled = windowSupportsHandles && isTextEditable() && mCursorVisible &&
-                mLayout != null && !mTextIsSelectable;
+                mLayout != null;
         mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() &&
                 mLayout != null;
 
@@ -7172,8 +7172,7 @@
      * a selectable TextView.
      */
     private boolean isTextEditable() {
-        return (mText instanceof Editable && onCheckIsTextEditor() && isEnabled())
-                || mTextIsSelectable;
+        return mText instanceof Editable && onCheckIsTextEditor() && isEnabled();
     }
 
     /**
@@ -7748,7 +7747,7 @@
                         }
                     }
                     if (clip != null) {
-                        clipboard.setPrimaryClip(clip);
+                        setPrimaryClip(clip);
                     }
                 }
                 return true;
@@ -7839,26 +7838,29 @@
             return true;
         }
 
-        if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) && mInsertionControllerEnabled) {
+        if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) &&
+                mInsertionControllerEnabled) {
+            // Long press in empty space moves cursor and shows the Paste affordance if available.
             final int offset = getOffset(mLastDownPositionX, mLastDownPositionY);
             Selection.setSelection((Spannable)mText, offset);
-            if (canPaste()) {
-                getInsertionController().showWithPaste();
-                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-            } else {
-                getInsertionController().show();
-            }
+            getInsertionController().show(0);
             mEatTouchRelease = true;
             return true;
         }
 
-        if (mSelectionActionMode != null && touchPositionIsInSelection()) {
-            final int start = getSelectionStart();
-            final int end = getSelectionEnd();
-            CharSequence selectedText = mTransformed.subSequence(start, end);
-            ClipData data = ClipData.newPlainText(null, null, selectedText);
-            startDrag(data, getTextThumbnailBuilder(selectedText), false);
-            stopSelectionActionMode();
+        if (mSelectionActionMode != null) {
+            if (touchPositionIsInSelection()) {
+                // Start a drag
+                final int start = getSelectionStart();
+                final int end = getSelectionEnd();
+                CharSequence selectedText = mTransformed.subSequence(start, end);
+                ClipData data = ClipData.newPlainText(null, null, selectedText);
+                startDrag(data, getTextThumbnailBuilder(selectedText), false);
+                stopSelectionActionMode();
+            } else {
+                selectCurrentWord();
+                getSelectionController().show();
+            }
             performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
             mEatTouchRelease = true;
             return true;
@@ -7950,10 +7952,10 @@
 
     /**
      * Paste clipboard content between min and max positions.
-     *
-     * @param clipboard getSystemService(Context.CLIPBOARD_SERVICE)
      */
-    private void paste(ClipboardManager clipboard, int min, int max) {
+    private void paste(int min, int max) {
+        ClipboardManager clipboard =
+            (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
         ClipData clip = clipboard.getPrimaryClip();
         if (clip != null) {
             boolean didfirst = false;
@@ -7973,9 +7975,17 @@
                 }
             }
             stopSelectionActionMode();
+            sLastCutOrCopyTime = 0;
         }
     }
 
+    private void setPrimaryClip(ClipData clip) {
+        ClipboardManager clipboard = (ClipboardManager) getContext().
+                getSystemService(Context.CLIPBOARD_SERVICE);
+        clipboard.setPrimaryClip(clip);
+        sLastCutOrCopyTime = SystemClock.uptimeMillis();
+    }
+
     private class SelectionActionModeCallback implements ActionMode.Callback {
 
         @Override
@@ -8061,9 +8071,6 @@
                 return true;
             }
 
-            ClipboardManager clipboard = (ClipboardManager) getContext().
-                    getSystemService(Context.CLIPBOARD_SERVICE);
-
             int min = 0;
             int max = mText.length();
 
@@ -8077,18 +8084,18 @@
 
             switch (item.getItemId()) {
                 case ID_PASTE:
-                    paste(clipboard, min, max);
+                    paste(min, max);
                     return true;
 
                 case ID_CUT:
-                    clipboard.setPrimaryClip(ClipData.newPlainText(null, null,
+                    setPrimaryClip(ClipData.newPlainText(null, null,
                             mTransformed.subSequence(min, max)));
                     ((Editable) mText).delete(min, max);
                     stopSelectionActionMode();
                     return true;
 
                 case ID_COPY:
-                    clipboard.setPrimaryClip(ClipData.newPlainText(null, null,
+                    setPrimaryClip(ClipData.newPlainText(null, null,
                             mTransformed.subSequence(min, max)));
                     stopSelectionActionMode();
                     return true;
@@ -8211,9 +8218,7 @@
         @Override
         public void onClick(View v) {
             if (canPaste()) {
-                ClipboardManager clipboard =
-                    (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
-                paste(clipboard, getSelectionStart(), getSelectionEnd());
+                paste(getSelectionStart(), getSelectionEnd());
             }
             hide();
         }
@@ -8502,6 +8507,9 @@
             }
             case MotionEvent.ACTION_UP:
                 if (mPastePopupWindow != null) {
+                    // Will show the paste popup after a delay.
+                    mController.show();
+                    /* TEMP USER TEST: Display Paste as soon as handle is draggged
                     long delay = SystemClock.uptimeMillis() - mTouchTimer;
                     if (delay < ViewConfiguration.getTapTimeout()) {
                         final float touchOffsetX = ev.getRawX() - mPositionX;
@@ -8515,7 +8523,7 @@
                         if (distanceSquared < slopSquared) {
                             showPastePopupWindow();
                         }
-                    }
+                    }*/
                 }
                 mIsDragging = false;
                 break;
@@ -8561,6 +8569,8 @@
 
     private class InsertionPointCursorController implements CursorController {
         private static final int DELAY_BEFORE_FADE_OUT = 4100;
+        private static final int DELAY_BEFORE_PASTE = 2000;
+        private static final int RECENT_CUT_COPY_DURATION = 15 * 1000;
 
         // The cursor controller image. Lazily created.
         private HandleView mHandle;
@@ -8571,14 +8581,27 @@
             }
         };
 
+        private final Runnable mPastePopupShower = new Runnable() {
+            public void run() {
+                getHandle().showPastePopupWindow();
+            }
+        };
+
         public void show() {
-            updatePosition();
-            getHandle().show();
+            show(DELAY_BEFORE_PASTE);
         }
 
-        void showWithPaste() {
-            show();
-            getHandle().showPastePopupWindow();
+        public void show(int delayBeforePaste) {
+            updatePosition();
+            hideDelayed();
+            getHandle().show();
+            removeCallbacks(mPastePopupShower);
+            if (canPaste()) {
+                final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime;
+                if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)
+                    delayBeforePaste = 0;
+                postDelayed(mPastePopupShower, delayBeforePaste);
+            }
         }
 
         public void hide() {
@@ -8586,6 +8609,7 @@
                 mHandle.hide();
             }
             removeCallbacks(mHider);
+            removeCallbacks(mPastePopupShower);
         }
 
         private void hideDelayed() {
@@ -8618,7 +8642,6 @@
                 return;
             }
 
-            // updatePosition is called only when isShowing. Handle has been created at this point.
             getHandle().positionAtCursor(offset, true);
         }
 
@@ -8681,6 +8704,7 @@
             mEndHandle.show();
 
             hideInsertionPointCursorController();
+            hideDelayed();
         }
 
         public void hide() {
@@ -8735,6 +8759,7 @@
 
             Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
             updatePosition();
+            hideDelayed();
         }
 
         public void updatePosition() {
@@ -8755,13 +8780,12 @@
             // The handles have been created since the controller isShowing().
             mStartHandle.positionAtCursor(selectionStart, true);
             mEndHandle.positionAtCursor(selectionEnd, true);
-            hideDelayed();
         }
 
         public boolean onTouchEvent(MotionEvent event) {
             // This is done even when the View does not have focus, so that long presses can start
             // selection and tap can move cursor from this tap position.
-            if (isTextEditable()) {
+            if (isTextEditable() || mTextIsSelectable) {
                 switch (event.getActionMasked()) {
                     case MotionEvent.ACTION_DOWN:
                         final int x = (int) event.getX();
@@ -9143,4 +9167,6 @@
     private InputFilter[] mFilters = NO_FILTERS;
     private static final Spanned EMPTY_SPANNED = new SpannedString("");
     private static int DRAG_THUMBNAIL_MAX_TEXT_LENGTH = 20;
+    // System wide time for last cut or copy action.
+    private static long sLastCutOrCopyTime;
 }
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 040dac3..fad9539 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -63,7 +63,8 @@
     }
 
     if (!window->initBuffer(localOnly)) {
-        jniThrowException(env, "java/lang/IllegalStateException", "Couldn't init cursor window");
+        jniThrowException(env, "java/lang/RuntimeException",
+                "Memory couldn't be allocated for 1MB CursorWindow object.");
         delete window;
         return;
     }
@@ -82,11 +83,13 @@
 
     CursorWindow * window = new CursorWindow();
     if (!window) {
-        jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
+        jniThrowException(env, "java/lang/RuntimeException",
+                "CursorWindow of size 1MB couldn't be created. No memory?");
         return;
     }
     if (!window->setMemory(memory)) {
-        jniThrowException(env, "java/lang/RuntimeException", "No memory in memObj");
+        jniThrowException(env, "java/lang/RuntimeException",
+                "Memory couldn't be initialized for 1MB CursorWindow object.");
         delete window;
         return;
     }
@@ -131,8 +134,9 @@
 
 static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column)
 {
-    char buf[100];
-    snprintf(buf, sizeof(buf), "get field slot from row %d col %d failed", row, column);
+    char buf[200];
+    snprintf(buf, sizeof(buf), "Couldn't read row %d, col %d from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it",
+            row, column);
     jniThrowException(env, "java/lang/IllegalStateException", buf);
 }
 
diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp
index fbba281..47bbd04 100644
--- a/libs/binder/CursorWindow.cpp
+++ b/libs/binder/CursorWindow.cpp
@@ -219,7 +219,8 @@
 field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
 {
   if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
-      LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+      LOGE("Failed to read row# %d, column# from a CursorWindow which has %d rows, %d columns.",
+              row, column, mHeader->numRows, mHeader->numColumns);
       return NULL;
   }        
   row_slot_t * rowSlot = getRowSlot(row);
@@ -238,7 +239,8 @@
 uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut)
 {
     if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
-        LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+        LOGE("Can't read row# %d, col# %d from CursorWindow. Make sure your Cursor is initialized correctly.",
+                row, column);
         return -1;
     }        
     row_slot_t * rowSlot = getRowSlot(row);
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index cc01bc5..069e1b8 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -110,7 +110,7 @@
     public static final int EVENT_CLEAN_UP_CONNECTION = 34;
     protected static final int EVENT_CDMA_OTA_PROVISION = 35;
     protected static final int EVENT_RESTART_RADIO = 36;
-    protected static final int EVENT_SET_MASTER_DATA_ENABLE = 37;
+    protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = 37;
     protected static final int EVENT_RESET_DONE = 38;
 
     /***** Constants *****/
@@ -126,8 +126,9 @@
     protected static final int DISABLED = 0;
     protected static final int ENABLED = 1;
 
-    // responds to the setDataEnabled call - used independently from the APN requests
-    protected boolean mMasterDataEnabled = true;
+    // responds to the setInternalDataEnabled call - used internally to turn off data
+    // for example during emergency calls
+    protected boolean mInternalDataEnabled = true;
 
     protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
 
@@ -489,9 +490,9 @@
                 onCleanUpConnection(tearDown, (String) msg.obj);
                 break;
 
-            case EVENT_SET_MASTER_DATA_ENABLE:
+            case EVENT_SET_INTERNAL_DATA_ENABLE:
                 boolean enabled = (msg.arg1 == ENABLED) ? true : false;
-                onSetDataEnabled(enabled);
+                onSetInternalDataEnabled(enabled);
                 break;
 
             case EVENT_RESET_DONE:
@@ -505,23 +506,13 @@
     }
 
     /**
-     * Report the current state of data connectivity (enabled or disabled)
-     *
-     * @return {@code false} if data connectivity has been explicitly disabled,
-     *         {@code true} otherwise.
-     */
-    public synchronized boolean getDataEnabled() {
-        return (mMasterDataEnabled && dataEnabled[APN_DEFAULT_ID]);
-    }
-
-    /**
      * Report on whether data connectivity is enabled
      *
      * @return {@code false} if data connectivity has been explicitly disabled,
      *         {@code true} otherwise.
      */
     public synchronized boolean getAnyDataEnabled() {
-        return (mMasterDataEnabled && (enabledCount != 0));
+        return (mInternalDataEnabled && (enabledCount != 0));
     }
 
     protected abstract void startNetStatPoll();
@@ -676,7 +667,7 @@
      */
     protected boolean isDataPossible() {
         boolean possible = (isDataAllowed()
-                && !(getDataEnabled() && (mState == State.FAILED || mState == State.IDLE)));
+                && !(getAnyDataEnabled() && (mState == State.FAILED || mState == State.IDLE)));
         if (!possible && DBG && isDataAllowed()) {
             log("Data not possible.  No coverage: dataState = " + mState);
         }
@@ -847,20 +838,20 @@
      *            {@code false}) data
      * @return {@code true} if the operation succeeded
      */
-    public boolean setDataEnabled(boolean enable) {
+    public boolean setInternalDataEnabled(boolean enable) {
         if (DBG)
-            log("setDataEnabled(" + enable + ")");
+            log("setInternalDataEnabled(" + enable + ")");
 
-        Message msg = obtainMessage(EVENT_SET_MASTER_DATA_ENABLE);
+        Message msg = obtainMessage(EVENT_SET_INTERNAL_DATA_ENABLE);
         msg.arg1 = (enable ? ENABLED : DISABLED);
         sendMessage(msg);
         return true;
     }
 
-    protected void onSetDataEnabled(boolean enable) {
-        if (mMasterDataEnabled != enable) {
+    protected void onSetInternalDataEnabled(boolean enable) {
+        if (mInternalDataEnabled != enable) {
             synchronized (this) {
-                mMasterDataEnabled = enable;
+                mInternalDataEnabled = enable;
             }
             if (enable) {
                 mRetryMgr.resetRetryCount();
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 69b7de6..25ad48d 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -1325,36 +1325,6 @@
     SimulatedRadioControl getSimulatedRadioControl();
 
     /**
-     * Allow mobile data connections.
-     * @return {@code true} if the operation started successfully
-     * <br/>{@code false} if it
-     * failed immediately.<br/>
-     * Even in the {@code true} case, it may still fail later
-     * during setup, in which case an asynchronous indication will
-     * be supplied.
-     */
-    boolean enableDataConnectivity();
-
-    /**
-     * Disallow mobile data connections, and terminate any that
-     * are in progress.
-     * @return {@code true} if the operation started successfully
-     * <br/>{@code false} if it
-     * failed immediately.<br/>
-     * Even in the {@code true} case, it may still fail later
-     * during setup, in which case an asynchronous indication will
-     * be supplied.
-     */
-    boolean disableDataConnectivity();
-
-    /**
-     * Report the current state of data connectivity (enabled or disabled)
-     * @return {@code false} if data connectivity has been explicitly disabled,
-     * {@code true} otherwise.
-     */
-    boolean isDataConnectivityEnabled();
-
-    /**
      * Enables the specified APN type. Only works for "special" APN types,
      * i.e., not the default APN.
      * @param type The desired APN type. Cannot be {@link #APN_TYPE_DEFAULT}.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index fe4fdb3..fce7b9b 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -941,10 +941,6 @@
          logUnexpectedCdmaMethodCall("unsetOnEcbModeExitResponse");
      }
 
-    public boolean isDataConnectivityEnabled() {
-        return mDataConnection.getDataEnabled();
-    }
-
     public String[] getActiveApnTypes() {
         return mDataConnection.getActiveApnTypes();
     }
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 2e79762..219efbb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -650,14 +650,6 @@
         return mActivePhone.getSimulatedRadioControl();
     }
 
-    public boolean enableDataConnectivity() {
-        return mActivePhone.enableDataConnectivity();
-    }
-
-    public boolean disableDataConnectivity() {
-        return mActivePhone.disableDataConnectivity();
-    }
-
     public int enableApnType(String type) {
         return mActivePhone.enableApnType(type);
     }
@@ -666,10 +658,6 @@
         return mActivePhone.disableApnType(type);
     }
 
-    public boolean isDataConnectivityEnabled() {
-        return mActivePhone.isDataConnectivityEnabled();
-    }
-
     public boolean isDataConnectivityPossible() {
         return mActivePhone.isDataConnectivityPossible();
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 099bc30..1e77589 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -478,10 +478,6 @@
         return mSST.cellLoc;
     }
 
-    public boolean disableDataConnectivity() {
-        return mDataConnection.setDataEnabled(false);
-    }
-
     public CdmaCall getForegroundCall() {
         return mCT.foregroundCall;
     }
@@ -761,21 +757,6 @@
         return ret;
     }
 
-    public boolean enableDataConnectivity() {
-
-        // block data activities when phone is in emergency callback mode
-        if (mIsPhoneInEcmState) {
-            Intent intent = new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS);
-            ActivityManagerNative.broadcastStickyIntent(intent, null);
-            return false;
-        } else if ((mCT.state == Phone.State.OFFHOOK) && mCT.isInEmergencyCall()) {
-            // Do not allow data call to be enabled when emergency call is going on
-            return false;
-        } else {
-            return mDataConnection.setDataEnabled(true);
-        }
-    }
-
     public boolean getIccRecordsLoaded() {
         return mRuimRecords.getRecordsLoaded();
     }
@@ -921,7 +902,7 @@
             // send an Intent
             sendEmergencyCallbackModeChange();
             // Re-initiate data connection
-            mDataConnection.setDataEnabled(true);
+            mDataConnection.setInternalDataEnabled(true);
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index 325c2e1..a89f783 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -1058,7 +1058,7 @@
         if (PhoneNumberUtils.isEmergencyNumber(dialString)) {
             if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
             mIsInEmergencyCall = true;
-            phone.disableDataConnectivity();
+            phone.mDataConnection.setInternalDataEnabled(false);
         }
     }
 
@@ -1075,8 +1075,7 @@
             }
             if (inEcm.compareTo("false") == 0) {
                 // Re-initiate data connection
-                // TODO - can this be changed to phone.enableDataConnectivity();
-                phone.mDataConnection.setDataEnabled(true);
+                phone.mDataConnection.setInternalDataEnabled(true);
             }
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index f7664ca5..b005cd3 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -204,7 +204,7 @@
                     (mCdmaPhone.mSST.isConcurrentVoiceAndData() ||
                             mPhone.getState() == Phone.State.IDLE) &&
                     !roaming &&
-                    mMasterDataEnabled &&
+                    mInternalDataEnabled &&
                     desiredPowerState &&
                     !mPendingRestartRadio &&
                     !mCdmaPhone.needsOtaServiceProvisioning();
@@ -222,7 +222,7 @@
                 reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
             }
             if (roaming) reason += " - Roaming";
-            if (!mMasterDataEnabled) reason += " - mMasterDataEnabled= false";
+            if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
             if (!desiredPowerState) reason += " - desiredPowerState= false";
             if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true";
             if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning";
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index fc03d1a..628f11a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -1084,14 +1084,6 @@
         mDataConnection.setDataOnRoamingEnabled(enable);
     }
 
-    public boolean enableDataConnectivity() {
-        return mDataConnection.setDataEnabled(true);
-    }
-
-    public boolean disableDataConnectivity() {
-        return mDataConnection.setDataEnabled(false);
-    }
-
     /**
      * Removes the given MMI from the pending list and notifies
      * registrants that it is complete.
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index b41402c..4713c24 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -284,7 +284,7 @@
                     (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
                     mGsmPhone.mSIMRecords.getRecordsLoaded() &&
                     mPhone.getState() == Phone.State.IDLE &&
-                    mMasterDataEnabled &&
+                    mInternalDataEnabled &&
                     (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
                     !mIsPsRestricted &&
                     desiredPowerState;
@@ -297,7 +297,7 @@
             if (mPhone.getState() != Phone.State.IDLE) {
                 reason += " - PhoneState= " + mPhone.getState();
             }
-            if (!mMasterDataEnabled) reason += " - mMasterDataEnabled= false";
+            if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
             if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
                 reason += " - Roaming and data roaming not enabled";
             }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 08f3c7a..b901e0d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -366,13 +366,59 @@
 
 
     /*package*/ static void native_concat(int nCanvas, int nMatrix) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+        if (matrixDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the current top graphics2D object.
+        Graphics2D g = canvasDelegate.getGraphics2d();
+
+        // get its current matrix
+        AffineTransform currentTx = g.getTransform();
+        // get the AffineTransform of the given matrix
+        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
+
+        // combine them so that the given matrix is applied after.
+        currentTx.preConcatenate(matrixTx);
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        g.setTransform(currentTx);
     }
 
     /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
-        // FIXME
-        throw new UnsupportedOperationException();
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+        }
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+        if (matrixDelegate == null) {
+            assert false;
+        }
+
+        // get the current top graphics2D object.
+        Graphics2D g = canvasDelegate.getGraphics2d();
+
+        // get the AffineTransform of the given matrix
+        AffineTransform matrixTx = matrixDelegate.getAffineTransform();
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        g.setTransform(matrixTx);
+
+        // FIXME: log
+//        if (mLogger != null && matrixDelegate.hasPerspective()) {
+//            mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
+//        }
     }
 
     /*package*/ static boolean native_clipRect(int nCanvas,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 6e80268..b2333f6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -64,7 +64,7 @@
             return null;
         }
 
-        return getAffineTransform(delegate);
+        return delegate.getAffineTransform();
     }
 
     public static boolean hasPerspective(Matrix m) {
@@ -74,7 +74,7 @@
             return false;
         }
 
-        return (delegate.mValues[6] != 0 || delegate.mValues[7] != 0 || delegate.mValues[8] != 1);
+        return delegate.hasPerspective();
     }
 
     /**
@@ -106,6 +106,18 @@
         return true;
     }
 
+    /**
+     * Returns an {@link AffineTransform} matching the matrix.
+     */
+    public AffineTransform getAffineTransform() {
+        return getAffineTransform(mValues);
+    }
+
+    public boolean hasPerspective() {
+        return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
+    }
+
+
 
     // ---- native methods ----
 
@@ -599,7 +611,7 @@
 
 
         try {
-            AffineTransform affineTransform = getAffineTransform(d);
+            AffineTransform affineTransform = d.getAffineTransform();
             AffineTransform inverseTransform = affineTransform.createInverse();
             inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
             inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
@@ -713,10 +725,6 @@
 
     // ---- Private helper methods ----
 
-    private static AffineTransform getAffineTransform(Matrix_Delegate d) {
-        return getAffineTransform(d.mValues);
-    }
-
     /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
         // the AffineTransform constructor takes the value in a different order
         // for a matrix [ 0 1 2 ]
diff --git a/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java
new file mode 100644
index 0000000..4d4ec7f4
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 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.os;
+
+
+/**
+ * Delegate overriding selected methods of android.os.Handler
+ *
+ * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ *
+ */
+public class Handler_Delegate {
+
+    // -------- Delegate methods
+
+    /*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
+        // get the callback
+        IHandlerCallback callback = sCallbacks.get();
+        if (callback != null) {
+            callback.sendMessageAtTime(handler, msg, uptimeMillis);
+        }
+        return true;
+    }
+
+    // -------- Delegate implementation
+
+    public interface IHandlerCallback {
+        void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis);
+    }
+
+    private final static ThreadLocal<IHandlerCallback> sCallbacks =
+        new ThreadLocal<IHandlerCallback>();
+
+    public static void setCallback(IHandlerCallback callback) {
+        sCallbacks.set(callback);
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
new file mode 100644
index 0000000..be222fc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 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.os;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+
+/**
+ * Delegate implementing the native methods of android.os.SystemClock
+ *
+ * Through the layoutlib_create tool, the original native methods of SystemClock have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ */
+public class SystemClock_Delegate {
+    private static long sBootTime = System.currentTimeMillis();
+
+    /*package*/ static boolean setCurrentTimeMillis(long millis) {
+        return true;
+    }
+
+    /**
+     * Returns milliseconds since boot, not counting time spent in deep sleep.
+     * <b>Note:</b> This value may get reset occasionally (before it would
+     * otherwise wrap around).
+     *
+     * @return milliseconds of non-sleep uptime since boot.
+     */
+    /*package*/ static long uptimeMillis() {
+        return System.currentTimeMillis() - sBootTime;
+    }
+
+    /**
+     * Returns milliseconds since boot, including time spent in sleep.
+     *
+     * @return elapsed milliseconds since boot.
+     */
+    /*package*/ static long elapsedRealtime() {
+        return System.currentTimeMillis() - sBootTime;
+    }
+
+    /**
+     * Returns milliseconds running in the current thread.
+     *
+     * @return elapsed milliseconds in the thread
+     */
+    /*package*/ static long currentThreadTimeMillis() {
+        return System.currentTimeMillis();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 35ba73d..2de1cbb 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -40,6 +40,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * Main entry point of the LayoutLib Bridge.
@@ -57,6 +58,12 @@
     }
 
     /**
+     * Lock to ensure only one rendering/inflating happens at a time.
+     * This is due to some singleton in the Android framework.
+     */
+    private final static ReentrantLock sLock = new ReentrantLock();
+
+    /**
      * Maps from id to resource name/type. This is for android.R only.
      */
     private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>();
@@ -160,7 +167,6 @@
 
         BridgeAssetManager.initSystem();
 
-
         // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
         // on static (native) methods which prints the signature on the console and
         // throws an exception.
@@ -252,27 +258,6 @@
     }
 
     /**
-     * Sets a 9 patch chunk in a project cache or in the framework cache.
-     * @param value the path of the 9 patch
-     * @param ninePatch the 9 patch object
-     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
-     */
-    public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
-        if (projectKey != null) {
-            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
-
-            if (map == null) {
-                map = new HashMap<String, SoftReference<NinePatchChunk>>();
-                sProject9PatchCache.put(projectKey, map);
-            }
-
-            map.put(value, new SoftReference<NinePatchChunk>(ninePatch));
-        } else {
-            sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch));
-        }
-    }
-
-    /**
      * Starts a layout session by inflating and rendering it. The method returns a
      * {@link ILayoutScene} on which further actions can be taken.
      *
@@ -306,27 +291,25 @@
     public BridgeLayoutScene createScene(SceneParams params) {
         try {
             SceneResult lastResult = SceneResult.SUCCESS;
-            LayoutSceneImpl scene = null;
-            synchronized (this) {
-                try {
-                    scene = new LayoutSceneImpl(params);
-
-                    scene.prepare();
+            LayoutSceneImpl scene = new LayoutSceneImpl(params);
+            try {
+                scene.prepareThread();
+                lastResult = scene.init(params.getTimeout());
+                if (lastResult == SceneResult.SUCCESS) {
                     lastResult = scene.inflate();
                     if (lastResult == SceneResult.SUCCESS) {
                         lastResult = scene.render();
                     }
-                } finally {
-                    if (scene != null) {
-                        scene.cleanup();
-                    }
                 }
+            } finally {
+                scene.release();
+                scene.cleanupThread();
             }
 
-            return new BridgeLayoutScene(this, scene, lastResult);
+            return new BridgeLayoutScene(scene, lastResult);
         } catch (Throwable t) {
             t.printStackTrace();
-            return new BridgeLayoutScene(this, null, new SceneResult("error!", t));
+            return new BridgeLayoutScene(null, new SceneResult("error!", t));
         }
     }
 
@@ -343,6 +326,13 @@
     }
 
     /**
+     * Returns the lock for the bridge
+     */
+    public static ReentrantLock getLock() {
+        return sLock;
+    }
+
+    /**
      * Returns details of a framework resource from its integer value.
      * @param value the integer value
      * @return an array of 2 strings containing the resource name and type, or null if the id
@@ -461,4 +451,27 @@
 
         return null;
     }
+
+    /**
+     * Sets a 9 patch chunk in a project cache or in the framework cache.
+     * @param value the path of the 9 patch
+     * @param ninePatch the 9 patch object
+     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
+     */
+    public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
+        if (projectKey != null) {
+            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
+
+            if (map == null) {
+                map = new HashMap<String, SoftReference<NinePatchChunk>>();
+                sProject9PatchCache.put(projectKey, map);
+            }
+
+            map.put(value, new SoftReference<NinePatchChunk>(ninePatch));
+        } else {
+            sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch));
+        }
+    }
+
+
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
index 8b67166..97bf857 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java
@@ -17,6 +17,7 @@
 package com.android.layoutlib.bridge;
 
 import com.android.layoutlib.api.LayoutScene;
+import com.android.layoutlib.api.SceneParams;
 import com.android.layoutlib.api.SceneResult;
 import com.android.layoutlib.api.ViewInfo;
 import com.android.layoutlib.bridge.impl.LayoutSceneImpl;
@@ -33,7 +34,6 @@
  */
 public class BridgeLayoutScene extends LayoutScene {
 
-    private final Bridge mBridge;
     private final LayoutSceneImpl mScene;
     private SceneResult mLastResult;
 
@@ -58,15 +58,34 @@
     }
 
     @Override
-    public SceneResult render() {
-
-        synchronized (mBridge) {
-            try {
-                mScene.prepare();
+    public SceneResult render(long timeout) {
+        try {
+            mScene.prepareThread();
+            mLastResult = mScene.acquire(timeout);
+            if (mLastResult == SceneResult.SUCCESS) {
                 mLastResult = mScene.render();
-            } finally {
-                mScene.cleanup();
             }
+        } finally {
+            mScene.release();
+            mScene.cleanupThread();
+        }
+
+        return mLastResult;
+    }
+
+    @Override
+    public SceneResult animate(Object targetObject, String animationName,
+            boolean isFrameworkAnimation, IAnimationListener listener) {
+        try {
+            mScene.prepareThread();
+            mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT);
+            if (mLastResult == SceneResult.SUCCESS) {
+                mLastResult = mScene.animate(targetObject, animationName, isFrameworkAnimation,
+                        listener);
+            }
+        } finally {
+            mScene.release();
+            mScene.cleanupThread();
         }
 
         return mLastResult;
@@ -78,8 +97,7 @@
 
     }
 
-    /*package*/ BridgeLayoutScene(Bridge bridge, LayoutSceneImpl scene, SceneResult lastResult) {
-        mBridge = bridge;
+    /*package*/ BridgeLayoutScene(LayoutSceneImpl scene, SceneResult lastResult) {
         mScene = scene;
         mLastResult = lastResult;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
index 1011173..affd1c6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
@@ -261,6 +261,41 @@
     }
 
     @Override
+    public XmlResourceParser getAnimation(int id) throws NotFoundException {
+        IResourceValue value = getResourceValue(id, mPlatformResourceFlag);
+
+        if (value != null) {
+            XmlPullParser parser = null;
+
+            try {
+                File xml = new File(value.getValue());
+                if (xml.isFile()) {
+                    // we need to create a pull parser around the layout XML file, and then
+                    // give that to our XmlBlockParser
+                    parser = new KXmlParser();
+                    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+                    parser.setInput(new FileReader(xml));
+
+                    return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                }
+            } catch (XmlPullParserException e) {
+                mContext.getLogger().error(e);
+                // we'll return null below.
+            } catch (FileNotFoundException e) {
+                // this shouldn't happen since we check above.
+            }
+
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+
+    @Override
     public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
         return mContext.obtainStyledAttributes(set, attrs);
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
new file mode 100644
index 0000000..c20bdfd
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 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.layoutlib.bridge.impl;
+
+import com.android.layoutlib.api.SceneResult;
+import com.android.layoutlib.api.LayoutScene.IAnimationListener;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.os.Handler;
+import android.os.Handler_Delegate;
+import android.os.Message;
+import android.os.Handler_Delegate.IHandlerCallback;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+public class AnimationThread extends Thread {
+
+    private static class MessageBundle {
+        final Handler mTarget;
+        final Message mMessage;
+        final long mUptimeMillis;
+
+        MessageBundle(Handler target, Message message, long uptimeMillis) {
+            mTarget = target;
+            mMessage = message;
+            mUptimeMillis = uptimeMillis;
+        }
+    }
+
+    private final LayoutSceneImpl mScene;
+    private final Animator mAnimator;
+
+    Queue<MessageBundle> mQueue = new LinkedList<MessageBundle>();
+    private final IAnimationListener mListener;
+
+    public AnimationThread(LayoutSceneImpl scene, Animator animator, IAnimationListener listener) {
+        mScene = scene;
+        mAnimator = animator;
+        mListener = listener;
+    }
+
+    @Override
+    public void run() {
+        mScene.prepareThread();
+        try {
+            Handler_Delegate.setCallback(new IHandlerCallback() {
+                public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
+                    if (msg.what == ValueAnimator.ANIMATION_START ||
+                            msg.what == ValueAnimator.ANIMATION_FRAME) {
+                        mQueue.add(new MessageBundle(handler, msg, uptimeMillis));
+                    } else {
+                        // just ignore.
+                    }
+                }
+            });
+
+            // start the animation. This will send a message to the handler right away, so
+            // mQueue is filled when this method returns.
+            mAnimator.start();
+
+            // loop the animation
+            do {
+                // get the next message.
+                MessageBundle bundle = mQueue.poll();
+                if (bundle == null) {
+                    break;
+                }
+
+                // sleep enough for this bundle to be on time
+                long currentTime = System.currentTimeMillis();
+                if (currentTime < bundle.mUptimeMillis) {
+                    try {
+                        sleep(bundle.mUptimeMillis - currentTime);
+                    } catch (InterruptedException e) {
+                        // TODO Auto-generated catch block
+                        e.printStackTrace();
+                    }
+                }
+
+                // ready to do the work, acquire the scene.
+                SceneResult result = mScene.acquire(250);
+                if (result != SceneResult.SUCCESS) {
+                    mListener.done(result);
+                    return;
+                }
+
+                // process the bundle. If the animation is not finished, this will enqueue
+                // the next message, so mQueue will have another one.
+                try {
+                    bundle.mTarget.handleMessage(bundle.mMessage);
+                    if (mScene.render() == SceneResult.SUCCESS) {
+                        mListener.onNewFrame(mScene.getImage());
+                    }
+                } finally {
+                    mScene.release();
+                }
+            } while (mQueue.size() > 0);
+
+            mListener.done(SceneResult.SUCCESS);
+        } finally {
+            Handler_Delegate.setCallback(null);
+            mScene.cleanupThread();
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
index f7d249e..0859976 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
@@ -16,6 +16,10 @@
 
 package com.android.layoutlib.bridge.impl;
 
+import static com.android.layoutlib.api.SceneResult.SceneStatus.ERROR_LOCK_INTERRUPTED;
+import static com.android.layoutlib.api.SceneResult.SceneStatus.ERROR_TIMEOUT;
+import static com.android.layoutlib.api.SceneResult.SceneStatus.SUCCESS;
+
 import com.android.internal.util.XmlUtils;
 import com.android.layoutlib.api.IProjectCallback;
 import com.android.layoutlib.api.IResourceValue;
@@ -25,7 +29,9 @@
 import com.android.layoutlib.api.SceneResult;
 import com.android.layoutlib.api.ViewInfo;
 import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
+import com.android.layoutlib.api.LayoutScene.IAnimationListener;
 import com.android.layoutlib.api.SceneParams.RenderingMode;
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.BridgeConstants;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeInflater;
@@ -33,6 +39,8 @@
 import com.android.layoutlib.bridge.android.BridgeWindowSession;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 
+import android.animation.AnimatorInflater;
+import android.animation.ObjectAnimator;
 import android.app.Fragment_Delegate;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
@@ -59,6 +67,8 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * Class managing a layout "scene".
@@ -73,6 +83,12 @@
     private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
     private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
 
+    /**
+     * The current context being rendered. This is set through {@link #acquire(long)} and
+     * {@link #init(long)}, and unset in {@link #release()}.
+     */
+    private static BridgeContext sCurrentContext = null;
+
     private final SceneParams mParams;
 
     // scene state
@@ -98,22 +114,35 @@
 
     /**
      * Creates a layout scene with all the information coming from the layout bridge API.
-     *
-     * This also calls {@link LayoutSceneImpl#prepare()}.
      * <p>
-     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
+     * This <b>must</b> be followed by a call to {@link LayoutSceneImpl#init()}, which act as a
+     * call to {@link LayoutSceneImpl#acquire(long)}
      *
      * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams)
      */
     public LayoutSceneImpl(SceneParams params) {
-        // we need to make sure the Looper has been initialized for this thread.
-        // this is required for View that creates Handler objects.
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
         // copy the params.
         mParams = new SceneParams(params);
+    }
+
+    /**
+     * Initializes and acquires the scene, creating various Android objects such as context,
+     * inflater, and parser.
+     *
+     * @param timeout the time to wait if another rendering is happening.
+     *
+     * @return whether the scene was prepared
+     *
+     * @see #acquire(long)
+     * @see #release()
+     */
+    public SceneResult init(long timeout) {
+        // acquire the lock. if the result is null, lock was just acquired, otherwise, return
+        // the result.
+        SceneResult result = acquireLock(timeout);
+        if (result != null) {
+            return result;
+        }
 
         // setup the display Metrics.
         DisplayMetrics metrics = new DisplayMetrics();
@@ -138,6 +167,9 @@
                 mParams.getProjectResources(), mParams.getFrameworkResources(),
                 styleParentMap, mParams.getProjectCallback(), mParams.getLogger());
 
+        // set the current rendering context
+        sCurrentContext = mContext;
+
         // make sure the Resources object references the context (and other objects) for this
         // scene
         mContext.initResources();
@@ -149,7 +181,8 @@
             mWindowBackground = mContext.findItemInStyle(mCurrentTheme, "windowBackground");
             mWindowBackground = mContext.resolveResValue(mWindowBackground);
 
-            mScreenOffset = getScreenOffset(mParams.getFrameworkResources(), mCurrentTheme, mContext);
+            mScreenOffset = getScreenOffset(mParams.getFrameworkResources(), mCurrentTheme,
+                    mContext);
         }
 
         // build the inflater and parser.
@@ -159,44 +192,144 @@
 
         mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(),
                 mContext, false /* platformResourceFlag */);
+
+        return SceneResult.SUCCESS;
     }
 
     /**
-     * Prepares the scene for action.
-     * <p>
-     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
+     * Prepares the current thread for rendering.
+     *
+     * Note that while this can be called several time, the first call to {@link #cleanupThread()}
+     * will do the clean-up, and make the thread unable to do further scene actions.
      */
-    public void prepare() {
+    public void prepareThread() {
         // we need to make sure the Looper has been initialized for this thread.
         // this is required for View that creates Handler objects.
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
+    }
+
+    /**
+     * Cleans up thread-specific data. After this, the thread cannot be used for scene actions.
+     * <p>
+     * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
+     * call to this will prevent the thread from doing further scene actions
+     */
+    public void cleanupThread() {
+        // clean up the looper
+        Looper.sThreadLocal.remove();
+    }
+
+    /**
+     * Prepares the scene for action.
+     * <p>
+     * This call is blocking if another rendering/inflating is currently happening, and will return
+     * whether the preparation worked.
+     *
+     * The preparation can fail if another rendering took too long and the timeout was elapsed.
+     *
+     * More than one call to this from the same thread will have no effect and will return
+     * {@link SceneResult#SUCCESS}.
+     *
+     * After scene actions have taken place, only one call to {@link #release()} must be
+     * done.
+     *
+     * @param timeout the time to wait if another rendering is happening.
+     *
+     * @return whether the scene was prepared
+     *
+     * @see #release()
+     *
+     * @throws IllegalStateException if {@link #init(long)} was never called.
+     */
+    public SceneResult acquire(long timeout) {
+        if (mContext == null) {
+            throw new IllegalStateException("After scene creation, #init() must be called");
+        }
+
+        // acquire the lock. if the result is null, lock was just acquired, otherwise, return
+        // the result.
+        SceneResult result = acquireLock(timeout);
+        if (result != null) {
+            return result;
+        }
 
         // make sure the Resources object references the context (and other objects) for this
         // scene
         mContext.initResources();
+        sCurrentContext = mContext;
+
+        return SUCCESS.getResult();
+    }
+
+    /**
+     * Acquire the lock so that the scene can be acted upon.
+     * <p>
+     * This returns null if the lock was just acquired, otherwise it returns
+     * {@link SceneResult#SUCCESS} if the lock already belonged to that thread, or another
+     * instance (see {@link SceneResult#getStatus()}) if an error occurred.
+     *
+     * @param timeout the time to wait if another rendering is happening.
+     * @return null if the lock was just acquire or another result depending on the state.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene.
+     */
+    private SceneResult acquireLock(long timeout) {
+        ReentrantLock lock = Bridge.getLock();
+        if (lock.isHeldByCurrentThread() == false) {
+            try {
+                boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
+
+                if (acquired == false) {
+                    return ERROR_TIMEOUT.getResult();
+                }
+            } catch (InterruptedException e) {
+                return ERROR_LOCK_INTERRUPTED.getResult();
+            }
+        } else {
+            // This thread holds the lock already. Checks that this wasn't for a different context.
+            // If this is called by init, mContext will be null and so should sCurrentContext
+            // anyway
+            if (mContext != sCurrentContext) {
+                throw new IllegalStateException("Acquiring different scenes from same thread without releases");
+            }
+            return SUCCESS.getResult();
+        }
+
+        return null;
     }
 
     /**
      * Cleans up the scene after an action.
-     * <p>
-     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
      */
-    public void cleanup() {
-        // clean up the looper
-        Looper.sThreadLocal.remove();
+    public void release() {
+        ReentrantLock lock = Bridge.getLock();
 
-        // Make sure to remove static references, otherwise we could not unload the lib
-        mContext.disposeResources();
+        // with the use of finally blocks, it is possible to find ourself calling this
+        // without a successful call to prepareScene. This test makes sure that unlock() will
+        // not throw IllegalMonitorStateException.
+        if (lock.isHeldByCurrentThread()) {
+            // Make sure to remove static references, otherwise we could not unload the lib
+            mContext.disposeResources();
+            sCurrentContext = null;
+
+            lock.unlock();
+        }
     }
 
     /**
      * Inflates the layout.
      * <p>
-     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #init(long)} was not called.
      */
     public SceneResult inflate() {
+        checkLock();
+
         try {
 
             mViewRoot = new FrameLayout(mContext);
@@ -247,10 +380,16 @@
     /**
      * Renders the scene.
      * <p>
-     * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
      */
     public SceneResult render() {
+        checkLock();
+
         try {
+            long current = System.currentTimeMillis();
             if (mViewRoot == null) {
                 return new SceneResult("Layout has not been inflated!");
             }
@@ -329,6 +468,8 @@
 
             mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext);
 
+            System.out.println("rendering (ms): " + (System.currentTimeMillis() - current));
+
             // success!
             return SceneResult.SUCCESS;
         } catch (Throwable e) {
@@ -346,6 +487,71 @@
     }
 
     /**
+     * Animate an object
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     */
+    public SceneResult animate(Object targetObject, String animationName,
+            boolean isFrameworkAnimation, IAnimationListener listener) {
+        checkLock();
+
+        // find the animation file.
+        IResourceValue animationResource = null;
+        int animationId = 0;
+        if (isFrameworkAnimation) {
+            animationResource = mContext.getFrameworkResource("anim", animationName);
+            if (animationResource != null) {
+                animationId = Bridge.getResourceValue("anim", animationName);
+            }
+        } else {
+            animationResource = mContext.getProjectResource("anim", animationName);
+            if (animationResource != null) {
+                animationId = mContext.getProjectCallback().getResourceValue("anim", animationName);
+            }
+        }
+
+        if (animationResource != null) {
+            try {
+                ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(
+                        mContext, animationId);
+                if (anim != null) {
+                    anim.setTarget(targetObject);
+
+                    new AnimationThread(this, anim, listener).start();
+
+                    return SceneResult.SUCCESS;
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+                return new SceneResult("", e);
+            }
+        }
+
+        return new SceneResult("Failed to find animation");
+    }
+
+    /**
+     * Checks that the lock is owned by the current thread and that the current context is the one
+     * from this scene.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     */
+    private void checkLock() {
+        ReentrantLock lock = Bridge.getLock();
+        if (lock.isHeldByCurrentThread() == false) {
+            throw new IllegalStateException("scene must be acquired first. see #acquire(long)");
+        }
+        if (sCurrentContext != mContext) {
+            throw new IllegalStateException("Thread acquired a scene but is rendering a different one");
+        }
+    }
+
+
+    /**
      * Compute style information from the given list of style for the project and framework.
      * @param themeName the name of the current theme.  In order to differentiate project and
      * platform themes sharing the same name, all project themes must be prepended with
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index bb2e6b3..4440685 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -95,6 +95,7 @@
      */
     private final static String[] DELEGATE_METHODS = new String[] {
         "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
+        "android.os.Handler#sendMessageAtTime",
         "android.view.View#isInEditMode",
         // TODO: comment out once DelegateClass is working
         // "android.content.res.Resources$Theme#obtainStyledAttributes",
@@ -118,6 +119,7 @@
         "android.graphics.SweepGradient",
         "android.graphics.Typeface",
         "android.graphics.Xfermode",
+        "android.os.SystemClock",
         "android.util.FloatMath",
     };