Merge "Add the min fps option to set the min fps in the media recorder test. Add the procmem log to the media memory stress test." into ics-mr1
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a9a628a..18167b6 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -608,7 +608,7 @@
 
                     final int relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
-                            View.VISIBLE, false, mWinFrame, mContentInsets,
+                            View.VISIBLE, 0, mWinFrame, mContentInsets,
                             mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
@@ -654,7 +654,7 @@
                         }
 
                         redrawNeeded |= creating
-                                || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0;
+                                || (relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0;
 
                         if (forceReport || creating || surfaceCreating
                                 || formatChanged || sizeChanged) {
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index 0194240..fd00dce 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -118,12 +118,26 @@
         if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
             stop(current);
         }
+
+        final MessageParams lastSynthesis = mLastSynthesisRequest;
+
+        if (lastSynthesis != null && lastSynthesis != current &&
+                TextUtils.equals(callingApp, lastSynthesis.getCallingApp())) {
+            stop(lastSynthesis);
+        }
     }
 
     synchronized public void removeAllItems() {
         if (DBG_THREADING) Log.d(TAG, "Removing all items");
         removeAllMessages();
-        stop(getCurrentParams());
+
+        final MessageParams current = getCurrentParams();
+        final MessageParams lastSynthesis = mLastSynthesisRequest;
+        stop(current);
+
+        if (lastSynthesis != null && lastSynthesis != current) {
+            stop(lastSynthesis);
+        }
     }
 
     /**
@@ -350,7 +364,7 @@
         // extra trouble to clean the data to prevent the AudioTrack resources
         // from being leaked.
         if (mLastSynthesisRequest != null) {
-            Log.w(TAG, "Error : Missing call to done() for request : " +
+            Log.e(TAG, "Error : Missing call to done() for request : " +
                     mLastSynthesisRequest);
             handleSynthesisDone(mLastSynthesisRequest);
         }
@@ -443,7 +457,11 @@
             audioTrack.release();
             params.setAudioTrack(null);
         }
-        params.getDispatcher().dispatchOnDone();
+        if (params.isError()) {
+            params.getDispatcher().dispatchOnError();
+        } else {
+            params.getDispatcher().dispatchOnDone();
+        }
         mLastSynthesisRequest = null;
         params.mLogger.onWriteData();
     }
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index ce3522b..91a3452 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -80,27 +80,23 @@
 
     @Override
     void stop() {
+        stopImpl(false);
+    }
+
+    void stopImpl(boolean wasError) {
         if (DBG) Log.d(TAG, "stop()");
 
         // Note that mLogger.mError might be true too at this point.
         mLogger.onStopped();
 
-        SynthesisMessageParams token = null;
+        SynthesisMessageParams token;
         synchronized (mStateLock) {
             if (mStopped) {
                 Log.w(TAG, "stop() called twice");
                 return;
             }
 
-            // mToken will be null if the engine encounters
-            // an error before it called start().
-            if (mToken == null) {
-                // In all other cases, mAudioTrackHandler.stop() will
-                // result in onComplete being called.
-                mLogger.onWriteData();
-            } else {
-                token = mToken;
-            }
+            token = mToken;
             mStopped = true;
         }
 
@@ -109,7 +105,24 @@
             // point it will write an additional buffer to the token - but we
             // won't worry about that because the audio playback queue will be cleared
             // soon after (see SynthHandler#stop(String).
+            token.setIsError(wasError);
             token.clearBuffers();
+            if (wasError) {
+                // Also clean up the audio track if an error occurs.
+                mAudioTrackHandler.enqueueSynthesisDone(token);
+            }
+        } else {
+            // This happens when stop() or error() were called before start() was.
+
+            // In all other cases, mAudioTrackHandler.stop() will
+            // result in onSynthesisDone being called, and we will
+            // write data there.
+            mLogger.onWriteData();
+
+            if (wasError) {
+                // We have to dispatch the error ourselves.
+                mDispatcher.dispatchOnError();
+            }
         }
     }
 
@@ -219,7 +232,7 @@
         // Currently, this call will not be logged if error( ) is called
         // before start.
         mLogger.onError();
-        stop();
+        stopImpl(true);
     }
 
 }
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
index 0c0f033..ed66420 100644
--- a/core/java/android/speech/tts/SynthesisMessageParams.java
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -51,6 +51,7 @@
     int mAudioBufferSize;
     // Always synchronized on "this".
     int mUnconsumedBytes;
+    volatile boolean mIsError;
 
     private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
 
@@ -74,6 +75,7 @@
         mAudioTrack = null;
         mBytesWritten = 0;
         mAudioBufferSize = 0;
+        mIsError = false;
     }
 
     @Override
@@ -120,6 +122,14 @@
         return mAudioTrack;
     }
 
+    void setIsError(boolean isError) {
+        mIsError = isError;
+    }
+
+    boolean isError() {
+        return mIsError;
+    }
+
     // Must be called synchronized on this.
     private long getUnconsumedAudioLengthMs() {
         final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame;
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 38699ea..a220615 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -490,6 +490,7 @@
     private final Map<String, Uri> mUtterances;
     private final Bundle mParams = new Bundle();
     private final TtsEngines mEnginesHelper;
+    private final String mPackageName;
     private volatile String mCurrentEngine = null;
 
     /**
@@ -518,19 +519,36 @@
      * @param engine Package name of the TTS engine to use.
      */
     public TextToSpeech(Context context, OnInitListener listener, String engine) {
+        this(context, listener, engine, null);
+    }
+
+    /**
+     * Used by the framework to instantiate TextToSpeech objects with a supplied
+     * package name, instead of using {@link android.content.Context#getPackageName()}
+     *
+     * @hide
+     */
+    public TextToSpeech(Context context, OnInitListener listener, String engine,
+            String packageName) {
         mContext = context;
         mInitListener = listener;
         mRequestedEngine = engine;
 
         mEarcons = new HashMap<String, Uri>();
         mUtterances = new HashMap<String, Uri>();
+        mUtteranceProgressListener = null;
 
         mEnginesHelper = new TtsEngines(mContext);
+        if (packageName != null) {
+            mPackageName = packageName;
+        } else {
+            mPackageName = mContext.getPackageName();
+        }
         initTts();
     }
 
     private String getPackageName() {
-        return mContext.getPackageName();
+        return mPackageName;
     }
 
     private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) {
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 39922da..aee678a 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -450,7 +450,7 @@
         @Override
         public void dispatchOnDone() {
             final String utteranceId = getUtteranceId();
-            if (!TextUtils.isEmpty(utteranceId)) {
+            if (utteranceId != null) {
                 mCallbacks.dispatchOnDone(getCallingApp(), utteranceId);
             }
         }
@@ -458,7 +458,7 @@
         @Override
         public void dispatchOnStart() {
             final String utteranceId = getUtteranceId();
-            if (!TextUtils.isEmpty(utteranceId)) {
+            if (utteranceId != null) {
                 mCallbacks.dispatchOnStart(getCallingApp(), utteranceId);
             }
         }
@@ -466,7 +466,7 @@
         @Override
         public void dispatchOnError() {
             final String utteranceId = getUtteranceId();
-            if (!TextUtils.isEmpty(utteranceId)) {
+            if (utteranceId != null) {
                 mCallbacks.dispatchOnError(getCallingApp(), utteranceId);
             }
         }
@@ -509,6 +509,7 @@
     }
 
     class SynthesisSpeechItem extends SpeechItem {
+        // Never null.
         private final String mText;
         private final SynthesisRequest mSynthesisRequest;
         private final String[] mDefaultLocale;
@@ -532,8 +533,8 @@
 
         @Override
         public boolean isValid() {
-            if (TextUtils.isEmpty(mText)) {
-                Log.w(TAG, "Got empty text");
+            if (mText == null) {
+                Log.wtf(TAG, "Got null text");
                 return false;
             }
             if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) {
diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java
index a04458a..cf0d22c 100644
--- a/core/java/android/speech/tts/UtteranceProgressListener.java
+++ b/core/java/android/speech/tts/UtteranceProgressListener.java
@@ -57,12 +57,16 @@
                 listener.onUtteranceCompleted(utteranceId);
             }
 
-            // The following methods are left unimplemented.
             @Override
-            public void onStart(String utteranceId) { }
+            public void onError(String utteranceId) {
+                listener.onUtteranceCompleted(utteranceId);
+            }
 
             @Override
-            public void onError(String utteranceId) { }
+            public void onStart(String utteranceId) {
+                // Left unimplemented, has no equivalent in the old
+                // API.
+            }
         };
     }
 }
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index d948ec2..4ca299f 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -737,8 +737,21 @@
         // Shaders are ignored when drawing bitmaps
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, src.left, src.top, src.right,
-                src.bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint);
+
+        float left, top, right, bottom;
+        if (src == null) {
+            left = top = 0;
+            right = bitmap.getWidth();
+            bottom = bitmap.getHeight();
+        } else {
+            left = src.left;
+            right = src.right;
+            top = src.top;
+            bottom = src.bottom;
+        }
+
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
         if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
     }
 
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index f77cf7e..ccb6489 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -219,6 +219,13 @@
     abstract int getHeight();
 
     /**
+     * Gets the current canvas associated with this HardwareRenderer.
+     *
+     * @return the current HardwareCanvas
+     */
+    abstract HardwareCanvas getCanvas();
+
+    /**
      * Sets the directory to use as a persistent storage for hardware rendering
      * resources.
      * 
@@ -783,6 +790,11 @@
             return mHeight;
         }
 
+        @Override
+        HardwareCanvas getCanvas() {
+            return mCanvas;
+        }
+
         boolean canDraw() {
             return mGl != null && mCanvas != null;
         }        
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 282d7be..53d6e1f 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -54,9 +54,8 @@
      * @param requestedWidth The width the window wants to be.
      * @param requestedHeight The height the window wants to be.
      * @param viewVisibility Window root view's visibility.
-     * @param insetsPending Set to true if the client will be later giving
-     * internal insets; as a result, the window will not impact other window
-     * layouts until the insets are given.
+     * @param flags Request flags: {@link WindowManagerImpl#RELAYOUT_INSETS_PENDING},
+     * {@link WindowManagerImpl#RELAYOUT_DEFER_SURFACE_DESTROY}.
      * @param outFrame Rect in which is placed the new position/size on
      * screen.
      * @param outContentInsets Rect in which is placed the offsets from
@@ -80,11 +79,17 @@
      */
     int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility,
-            boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
+            int flags, out Rect outFrame, out Rect outContentInsets,
             out Rect outVisibleInsets, out Configuration outConfig,
             out Surface outSurface);
 
     /**
+     * If a call to relayout() asked to have the surface destroy deferred,
+     * it must call this once it is okay to destroy that surface.
+     */
+    void performDeferredDestroy(IWindow window);
+
+    /**
      * Called by a client to report that it ran out of graphics memory.
      */
     boolean outOfMemory(IWindow window);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9a57ea0..0e68490 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import android.util.DisplayMetrics;
 import com.android.internal.view.BaseIWindow;
 
 import android.content.Context;
@@ -82,7 +81,6 @@
 public class SurfaceView extends View {
     static private final String TAG = "SurfaceView";
     static private final boolean DEBUG = false;
-    static private final boolean localLOGV = DEBUG ? true : false;
 
     final ArrayList<SurfaceHolder.Callback> mCallbacks
             = new ArrayList<SurfaceHolder.Callback>();
@@ -90,7 +88,8 @@
     final int[] mLocation = new int[2];
     
     final ReentrantLock mSurfaceLock = new ReentrantLock();
-    final Surface mSurface = new Surface();
+    Surface mSurface = new Surface();       // Current surface in use
+    Surface mNewSurface = new Surface();    // New surface we are switching to
     boolean mDrawingStopped = true;
 
     final WindowManager.LayoutParams mLayout
@@ -145,8 +144,7 @@
     int mRequestedFormat = PixelFormat.RGB_565;
 
     boolean mHaveFrame = false;
-    boolean mDestroyReportNeeded = false;
-    boolean mNewSurfaceNeeded = false;
+    boolean mSurfaceCreated = false;
     long mLastLockTime = 0;
     
     boolean mVisible = false;
@@ -236,46 +234,6 @@
         updateWindow(false, false);
     }
 
-    /**
-     * This method is not intended for general use. It was created
-     * temporarily to improve performance of 3D layers in Launcher
-     * and should be removed and fixed properly.
-     * 
-     * Do not call this method. Ever.
-     * 
-     * @hide
-     */
-    protected void showSurface() {
-        if (mSession != null) {
-            updateWindow(true, false);
-        }
-    }
-
-    /**
-     * This method is not intended for general use. It was created
-     * temporarily to improve performance of 3D layers in Launcher
-     * and should be removed and fixed properly.
-     * 
-     * Do not call this method. Ever.
-     * 
-     * @hide
-     */
-    protected void hideSurface() {
-        if (mSession != null && mWindow != null) {
-            mSurfaceLock.lock();
-            try {
-                DisplayMetrics metrics = getResources().getDisplayMetrics();
-                mLayout.x = metrics.widthPixels * 3;
-                mSession.relayout(mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, VISIBLE, false,
-                        mWinFrame, mContentInsets, mVisibleInsets, mConfiguration, mSurface);
-            } catch (RemoteException e) {
-                // Ignore
-            } finally {
-                mSurfaceLock.unlock();
-            }
-        }
-    }
-    
     @Override
     protected void onDetachedFromWindow() {
         if (mGlobalListenersAdded) {
@@ -444,14 +402,13 @@
         final boolean creating = mWindow == null;
         final boolean formatChanged = mFormat != mRequestedFormat;
         final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
-        final boolean visibleChanged = mVisible != mRequestedVisible
-                || mNewSurfaceNeeded;
+        final boolean visibleChanged = mVisible != mRequestedVisible;
 
         if (force || creating || formatChanged || sizeChanged || visibleChanged
             || mLeft != mLocation[0] || mTop != mLocation[1]
             || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
 
-            if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
+            if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
                     + " format=" + formatChanged + " size=" + sizeChanged
                     + " visible=" + visibleChanged
                     + " left=" + (mLeft != mLocation[0])
@@ -496,15 +453,11 @@
                             mVisible ? VISIBLE : GONE, mContentInsets);
                 }
                 
-                if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
-                    reportSurfaceDestroyed();
-                }
-
-                mNewSurfaceNeeded = false;
-                
                 boolean realSizeChanged;
                 boolean reportDrawNeeded;
-                
+
+                int relayoutResult;
+
                 mSurfaceLock.lock();
                 try {
                     mUpdateWindowNeeded = false;
@@ -512,17 +465,21 @@
                     mReportDrawNeeded = false;
                     mDrawingStopped = !visible;
     
-                    final int relayoutResult = mSession.relayout(
+                    if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
+
+                    relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
-                            visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
-                            mVisibleInsets, mConfiguration, mSurface);
-                    if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+                            visible ? VISIBLE : GONE,
+                            WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY,
+                            mWinFrame, mContentInsets,
+                            mVisibleInsets, mConfiguration, mNewSurface);
+                    if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
                         mReportDrawNeeded = true;
                     }
-                    
-                    if (localLOGV) Log.i(TAG, "New surface: " + mSurface
+
+                    if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
                             + ", vis=" + visible + ", frame=" + mWinFrame);
-                    
+
                     mSurfaceFrame.left = 0;
                     mSurfaceFrame.top = 0;
                     if (mTranslator == null) {
@@ -547,28 +504,54 @@
                 try {
                     redrawNeeded |= creating | reportDrawNeeded;
 
-                    if (visible) {
-                        mDestroyReportNeeded = true;
+                    SurfaceHolder.Callback callbacks[] = null;
 
-                        SurfaceHolder.Callback callbacks[];
-                        synchronized (mCallbacks) {
-                            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
-                            mCallbacks.toArray(callbacks);
+                    final boolean surfaceChanged =
+                            (relayoutResult&WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED) != 0;
+                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
+                        mSurfaceCreated = false;
+                        if (mSurface.isValid()) {
+                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
+                            callbacks = getSurfaceCallbacks();
+                            for (SurfaceHolder.Callback c : callbacks) {
+                                c.surfaceDestroyed(mSurfaceHolder);
+                            }
                         }
+                    }
 
-                        if (visibleChanged) {
+                    Surface tmpSurface = mSurface;
+                    mSurface = mNewSurface;
+                    mNewSurface = tmpSurface;
+                    mNewSurface.release();
+
+                    if (visible) {
+                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
+                            mSurfaceCreated = true;
                             mIsCreating = true;
+                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
+                            if (callbacks == null) {
+                                callbacks = getSurfaceCallbacks();
+                            }
                             for (SurfaceHolder.Callback c : callbacks) {
                                 c.surfaceCreated(mSurfaceHolder);
                             }
                         }
                         if (creating || formatChanged || sizeChanged
                                 || visibleChanged || realSizeChanged) {
+                            if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
+                                    + " w=" + myWidth + " h=" + myHeight);
+                            if (callbacks == null) {
+                                callbacks = getSurfaceCallbacks();
+                            }
                             for (SurfaceHolder.Callback c : callbacks) {
                                 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                             }
                         }
                         if (redrawNeeded) {
+                            if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
+                            if (callbacks == null) {
+                                callbacks = getSurfaceCallbacks();
+                            }
                             for (SurfaceHolder.Callback c : callbacks) {
                                 if (c instanceof SurfaceHolder.Callback2) {
                                     ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
@@ -576,41 +559,34 @@
                                 }
                             }
                         }
-                    } else {
-                        mSurface.release();
                     }
                 } finally {
                     mIsCreating = false;
                     if (redrawNeeded) {
+                        if (DEBUG) Log.i(TAG, "finishedDrawing");
                         mSession.finishDrawing(mWindow);
                     }
+                    mSession.performDeferredDestroy(mWindow);
                 }
             } catch (RemoteException ex) {
             }
-            if (localLOGV) Log.v(
+            if (DEBUG) Log.v(
                 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
                 " w=" + mLayout.width + " h=" + mLayout.height +
                 ", frame=" + mSurfaceFrame);
         }
     }
 
-    private void reportSurfaceDestroyed() {
-        if (mDestroyReportNeeded) {
-            mDestroyReportNeeded = false;
-            SurfaceHolder.Callback callbacks[];
-            synchronized (mCallbacks) {
-                callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
-                mCallbacks.toArray(callbacks);
-            }            
-            for (SurfaceHolder.Callback c : callbacks) {
-                c.surfaceDestroyed(mSurfaceHolder);
-            }
+    private SurfaceHolder.Callback[] getSurfaceCallbacks() {
+        SurfaceHolder.Callback callbacks[];
+        synchronized (mCallbacks) {
+            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
+            mCallbacks.toArray(callbacks);
         }
-        super.onDetachedFromWindow();
+        return callbacks;
     }
 
     void handleGetNewSurface() {
-        mNewSurfaceNeeded = true;
         updateWindow(false, false);
     }
 
@@ -636,7 +612,7 @@
                 Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
             SurfaceView surfaceView = mSurfaceView.get();
             if (surfaceView != null) {
-                if (localLOGV) Log.v(
+                if (DEBUG) Log.v(
                         "SurfaceView", surfaceView + " got resized: w=" +
                                 w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
                 surfaceView.mSurfaceLock.lock();
@@ -754,7 +730,7 @@
         private final Canvas internalLockCanvas(Rect dirty) {
             mSurfaceLock.lock();
 
-            if (localLOGV) Log.i(TAG, "Locking canvas... stopped="
+            if (DEBUG) Log.i(TAG, "Locking canvas... stopped="
                     + mDrawingStopped + ", win=" + mWindow);
 
             Canvas c = null;
@@ -774,7 +750,7 @@
                 }
             }
 
-            if (localLOGV) Log.i(TAG, "Returned canvas: " + c);
+            if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
             if (c != null) {
                 mLastLockTime = SystemClock.uptimeMillis();
                 return c;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5f70a39..7f5b5be 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1216,7 +1216,8 @@
                         disposeResizeBuffer();
 
                         boolean completed = false;
-                        HardwareCanvas canvas = null;
+                        HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas();
+                        HardwareCanvas layerCanvas = null;
                         try {
                             if (mResizeBuffer == null) {
                                 mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
@@ -1225,12 +1226,12 @@
                                     mResizeBuffer.getHeight() != mHeight) {
                                 mResizeBuffer.resize(mWidth, mHeight);
                             }
-                            canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas);
-                            canvas.setViewport(mWidth, mHeight);
-                            canvas.onPreDraw(null);
-                            final int restoreCount = canvas.save();
+                            layerCanvas = mResizeBuffer.start(hwRendererCanvas);
+                            layerCanvas.setViewport(mWidth, mHeight);
+                            layerCanvas.onPreDraw(null);
+                            final int restoreCount = layerCanvas.save();
                             
-                            canvas.drawColor(0xff000000, PorterDuff.Mode.SRC);
+                            layerCanvas.drawColor(0xff000000, PorterDuff.Mode.SRC);
 
                             int yoff;
                             final boolean scrolling = mScroller != null
@@ -1242,27 +1243,27 @@
                                 yoff = mScrollY;
                             }
 
-                            canvas.translate(0, -yoff);
+                            layerCanvas.translate(0, -yoff);
                             if (mTranslator != null) {
-                                mTranslator.translateCanvas(canvas);
+                                mTranslator.translateCanvas(layerCanvas);
                             }
 
-                            mView.draw(canvas);
+                            mView.draw(layerCanvas);
 
                             mResizeBufferStartTime = SystemClock.uptimeMillis();
                             mResizeBufferDuration = mView.getResources().getInteger(
                                     com.android.internal.R.integer.config_mediumAnimTime);
                             completed = true;
 
-                            canvas.restoreToCount(restoreCount);
+                            layerCanvas.restoreToCount(restoreCount);
                         } catch (OutOfMemoryError e) {
                             Log.w(TAG, "Not enough memory for content change anim buffer", e);
                         } finally {
-                            if (canvas != null) {
-                                canvas.onPostDraw();
+                            if (layerCanvas != null) {
+                                layerCanvas.onPostDraw();
                             }
                             if (mResizeBuffer != null) {
-                                mResizeBuffer.end(mAttachInfo.mHardwareCanvas);
+                                mResizeBuffer.end(hwRendererCanvas);
                                 if (!completed) {
                                     mResizeBuffer.destroy();
                                     mResizeBuffer = null;
@@ -1425,7 +1426,7 @@
 
             if (!mStopped) {
                 boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
-                        (relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0);
+                        (relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                         || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
                     childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
@@ -1636,7 +1637,7 @@
                 mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
             }
 
-            if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
+            if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0
                     || mReportNextDraw) {
                 if (LOCAL_LOGV) {
                     Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
@@ -1669,7 +1670,7 @@
             }
             // We were supposed to report when we are done drawing. Since we canceled the
             // draw, remember it here.
-            if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+            if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
                 mReportNextDraw = true;
             }
             if (fullRedrawNeeded) {
@@ -3585,8 +3586,8 @@
                 mWindow, mSeq, params,
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
-                viewVisibility, insetsPending, mWinFrame,
-                mPendingContentInsets, mPendingVisibleInsets,
+                viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0,
+                mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingConfiguration, mSurface);
         //Log.d(TAG, "<<<<<< BACK FROM relayout");
         if (restore) {
@@ -3716,7 +3717,7 @@
                     // animation info.
                     try {
                         if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
-                                & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+                                & WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
                             sWindowSession.finishDrawing(mWindow);
                         }
                     } catch (RemoteException e) {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index dfd1d55..d7113374 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -63,15 +63,34 @@
      * The user is navigating with keys (not the touch screen), so
      * navigational focus should be shown.
      */
-    public static final int RELAYOUT_IN_TOUCH_MODE = 0x1;
+    public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1;
     /**
      * This is the first time the window is being drawn,
      * so the client must call drawingFinished() when done
      */
-    public static final int RELAYOUT_FIRST_TIME = 0x2;
-    
+    public static final int RELAYOUT_RES_FIRST_TIME = 0x2;
+    /**
+     * The window manager has changed the surface from the last call.
+     */
+    public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
+
+    /**
+     * Flag for relayout: the client will be later giving
+     * internal insets; as a result, the window will not impact other window
+     * layouts until the insets are given.
+     */
+    public static final int RELAYOUT_INSETS_PENDING = 0x1;
+
+    /**
+     * Flag for relayout: the client may be currently using the current surface,
+     * so if it is to be destroyed as a part of the relayout the destroy must
+     * be deferred until later.  The client will call performDeferredDestroy()
+     * when it is okay.
+     */
+    public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
+
     public static final int ADD_FLAG_APP_VISIBLE = 0x2;
-    public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_IN_TOUCH_MODE;
+    public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE;
     
     public static final int ADD_OKAY = 0;
     public static final int ADD_BAD_APP_TOKEN = -1;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 877c9ea..2af6e3b 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1306,8 +1306,15 @@
         if (AccessibilityManager.getInstance(mContext).isEnabled()
                 && getSettings().getJavaScriptEnabled()) {
             // exposing the TTS for now ...
-            mTextToSpeech = new TextToSpeech(getContext(), null);
-            addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
+            final Context ctx = getContext();
+            if (ctx != null) {
+                final String packageName = ctx.getPackageName();
+                if (packageName != null) {
+                    mTextToSpeech = new TextToSpeech(getContext(), null, null,
+                            packageName + ".**webview**");
+                    addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
+                }
+            }
         }
     }
 
@@ -1628,6 +1635,14 @@
         clearTextEntry();
         clearActionModes();
         dismissFullScreenMode();
+        cancelSelectDialog();
+    }
+
+    private void cancelSelectDialog() {
+        if (mListBoxDialog != null) {
+            mListBoxDialog.cancel();
+            mListBoxDialog = null;
+        }
     }
 
     /**
@@ -3279,6 +3294,8 @@
             if (mNativeClass != 0) {
                 nativeSetPauseDrawing(mNativeClass, true);
             }
+
+            cancelSelectDialog();
         }
     }
 
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 84d00c9..cf2c8a6 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -498,6 +498,11 @@
             if (mZoomScale == 0) {
                 // We've reached the end of the zoom animation.
                 mInHWAcceleratedZoom = false;
+
+                // Ensure that the zoom level is pushed to WebCore. This has not
+                // yet occurred because we prevent it from happening while
+                // mInHWAcceleratedZoom is true.
+                mWebView.sendViewSizeZoom(false);
             }
         } else {
             canvas.translate(tx, ty);
diff --git a/core/res/res/anim/app_starting_exit.xml b/core/res/res/anim/app_starting_exit.xml
index ee8d80b..60e4109 100644
--- a/core/res/res/anim/app_starting_exit.xml
+++ b/core/res/res/anim/app_starting_exit.xml
@@ -18,7 +18,8 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/decelerate_quad">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:detachWallpaper="true" android:interpolator="@interpolator/decelerate_quad">
 	<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="160" />
 </set>
 
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 921bcf4..431a502 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -62,8 +62,8 @@
 
     <!-- Default padding to apply to AppWidgetHostViews containing widgets targeting API level 14 and up. -->
     <dimen name="default_app_widget_padding_left">12dp</dimen>
-    <dimen name="default_app_widget_padding_top">12dp</dimen>
-    <dimen name="default_app_widget_padding_right">4dp</dimen>
+    <dimen name="default_app_widget_padding_top">4dp</dimen>
+    <dimen name="default_app_widget_padding_right">12dp</dimen>
     <dimen name="default_app_widget_padding_bottom">20dp</dimen>
 
     <!-- Minimum width for an action button in the menu area of an action bar -->
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
index d23dfd3..3ffa085 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
@@ -38,7 +38,8 @@
     public int mSoftapIterations = 100;
     public int mScanIterations = 100;
     public int mReconnectIterations = 100;
-    public int mSleepTime = 30 * 1000;  // default sleep time is 30 seconds
+    // sleep time before restart wifi, default is set to 2 minutes
+    public int mSleepTime = 2 * 60 * 1000;
     public String mReconnectSsid = "securenetdhcp";
     public String mReconnectPassword = "androidwifi";
 
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index adf1883c8..0580ebc 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -62,6 +62,8 @@
     public static final int WIFI_SCAN_TIMEOUT = 50 * 1000;
     public static final int SHORT_TIMEOUT = 5 * 1000;
     public static final long LONG_TIMEOUT = 50 * 1000;
+    // 2 minutes timer between wifi stop and start
+    public static final long  WIFI_STOP_START_INTERVAL = 2 * 60 * 1000;
     public static final int SUCCESS = 0;  // for Wifi tethering state change
     public static final int FAILURE = 1;
     public static final int INIT = -1;
@@ -247,6 +249,8 @@
         sleep(SHORT_TIMEOUT);
         removeConfiguredNetworksAndDisableWifi();
         mWifiRegexs = mCM.getTetherableWifiRegexs();
+        // after wifi is shutdown, wait for 2 minute to enable wifi
+        sleep(WIFI_STOP_START_INTERVAL);
      }
 
     public List<WifiConfiguration> loadNetworkConfigurations() throws Exception {
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
index d586396..5a4a2d0 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/NetworkState.java
@@ -77,10 +77,13 @@
                 mReason = "no state is recorded.";
                 return false;
             } else if (mStateDepository.size() > 1) {
-                Log.v(LOG_TAG, "no broadcast is expected, " +
-                        "instead broadcast is probably received");
-                mReason = "no broadcast is expected, instead broadcast is probably received";
-                return false;
+                for (int i = 0; i < mStateDepository.size(); i++) {
+                    if (mStateDepository.get(i) != mTransitionTarget) {
+                        Log.v(LOG_TAG, "state changed.");
+                        mReason = "Unexpected state change";
+                        return false;
+                    }
+                }
             } else if (mStateDepository.get(0) != mTransitionTarget) {
                 Log.v(LOG_TAG, mTransitionTarget + " is expected, but it is " +
                         mStateDepository.get(0));
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
index d9b770a..b1f4bf1 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java
@@ -66,7 +66,7 @@
         // Each test case will start with cellular connection
         if (Settings.System.getInt(getInstrumentation().getContext().getContentResolver(),
                 Settings.System.AIRPLANE_MODE_ON) == 1) {
-            Log.v(LOG_TAG, "airplane is not disabled, disable it.");
+            log("airplane is not disabled, disable it.");
             cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
         }
         if (!UtilHelper.isWifiOnly()) {
@@ -84,13 +84,13 @@
     @Override
     public void tearDown() throws Exception {
         cmActivity.finish();
-        Log.v(LOG_TAG, "tear down ConnectivityManagerTestActivity");
+        log("tear down ConnectivityManagerTestActivity");
         wl.release();
         cmActivity.removeConfiguredNetworksAndDisableWifi();
         // if airplane mode is set, disable it.
         if (Settings.System.getInt(getInstrumentation().getContext().getContentResolver(),
                 Settings.System.AIRPLANE_MODE_ON) == 1) {
-            Log.v(LOG_TAG, "disable airplane mode if it is enabled");
+            log("disable airplane mode if it is enabled");
             cmActivity.setAirplaneMode(getInstrumentation().getContext(), false);
         }
         super.tearDown();
@@ -104,17 +104,24 @@
         assertTrue("not connected to cellular network", extraNetInfo.isConnected());
     }
 
+    private void log(String message) {
+        Log.v(LOG_TAG, message);
+    }
+
+    private void sleep(long sleeptime) {
+        try {
+            Thread.sleep(sleeptime);
+        } catch (InterruptedException e) {}
+    }
+
     // Test case 1: Test enabling Wifi without associating with any AP, no broadcast on network
     //              event should be expected.
     @LargeTest
     public void test3GToWifiNotification() {
         // Enable Wi-Fi to avoid initial UNKNOWN state
         cmActivity.enableWifi();
-        try {
-            Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
-        } catch (Exception e) {
-            Log.v(LOG_TAG, "exception: " + e.toString());
-        }
+        sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
+
         // Wi-Fi is disabled
         cmActivity.disableWifi();
 
@@ -123,11 +130,8 @@
         assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
                 State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT));
         // Wait for 10 seconds for broadcasts to be sent out
-        try {
-            Thread.sleep(10 * 1000);
-        } catch (Exception e) {
-            fail("thread in sleep is interrupted.");
-        }
+        sleep(10 * 1000);
+
         // As Wifi stays in DISCONNETED, Mobile statys in CONNECTED,
         // the connectivity manager will not broadcast any network connectivity event for Wifi
         NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
@@ -138,22 +142,18 @@
                 NetworkState.DO_NOTHING, State.DISCONNECTED);
         // Eanble Wifi without associating with any AP
         cmActivity.enableWifi();
-        try {
-            Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
-        } catch (Exception e) {
-            Log.v(LOG_TAG, "exception: " + e.toString());
-        }
+        sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT);
 
         // validate state and broadcast
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            Log.v(LOG_TAG, "the state for WIFI is changed");
-            Log.v(LOG_TAG, "reason: " +
+            log("the state for WIFI is changed");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
             assertTrue("state validation fail", false);
         }
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-            Log.v(LOG_TAG, "the state for MOBILE is changed");
-            Log.v(LOG_TAG, "reason: " +
+            log("the state for MOBILE is changed");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
             assertTrue("state validation fail", false);
         }
@@ -182,7 +182,7 @@
 
         assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
                 ConnectivityManagerTestActivity.LONG_TIMEOUT));
-        Log.v(LOG_TAG, "wifi state is enabled");
+        log("wifi state is enabled");
         assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
                 ConnectivityManagerTestActivity.LONG_TIMEOUT));
         if (!UtilHelper.isWifiOnly()) {
@@ -192,15 +192,15 @@
 
         // validate states
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            Log.v(LOG_TAG, "Wifi state transition validation failed.");
-            Log.v(LOG_TAG, "reason: " +
+            log("Wifi state transition validation failed.");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
             assertTrue(false);
         }
         if (!UtilHelper.isWifiOnly()) {
             if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-                Log.v(LOG_TAG, "Mobile state transition validation failed.");
-                Log.v(LOG_TAG, "reason: " +
+                log("Mobile state transition validation failed.");
+                log("reason: " +
                         cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
                 assertTrue(false);
             }
@@ -219,16 +219,11 @@
         assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
                 ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
-        try {
-            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
-        } catch (Exception e) {
-            Log.v(LOG_TAG, "exception: " + e.toString());
-        }
-
+        sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         // Disable Wifi
-        Log.v(LOG_TAG, "Disable Wifi");
+        log("Disable Wifi");
         if (!cmActivity.disableWifi()) {
-            Log.v(LOG_TAG, "disable Wifi failed");
+            log("disable Wifi failed");
             return;
         }
 
@@ -254,8 +249,10 @@
         cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(),
                 NetworkState.TO_CONNECTION, State.CONNECTED);
 
+        // wait for 2 minutes before restart wifi
+        sleep(ConnectivityManagerTestActivity.WIFI_STOP_START_INTERVAL);
         // Enable Wifi again
-        Log.v(LOG_TAG, "Enable Wifi again");
+        log("Enable Wifi again");
         cmActivity.enableWifi();
 
         // Wait for Wifi to be connected and mobile to be disconnected
@@ -268,8 +265,8 @@
 
         // validate wifi states
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            Log.v(LOG_TAG, "Wifi state transition validation failed.");
-            Log.v(LOG_TAG, "reason: " +
+            log("Wifi state transition validation failed.");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
             assertTrue(false);
         }
@@ -288,11 +285,7 @@
             ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // Wait for a few seconds to avoid the state that both Mobile and Wifi is connected
-        try {
-            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
-        } catch (Exception e) {
-            Log.v(LOG_TAG, "exception: " + e.toString());
-        }
+        sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
 
         NetworkInfo networkInfo;
         if (!UtilHelper.isWifiOnly()) {
@@ -318,15 +311,15 @@
 
         // validate states
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            Log.v(LOG_TAG, "Wifi state transition validation failed.");
-            Log.v(LOG_TAG, "reason: " +
+            log("Wifi state transition validation failed.");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
             assertTrue(false);
         }
         if (!UtilHelper.isWifiOnly()) {
             if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-                Log.v(LOG_TAG, "Mobile state transition validation failed.");
-                Log.v(LOG_TAG, "reason: " +
+                log("Mobile state transition validation failed.");
+                log("reason: " +
                         cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
                 assertTrue(false);
             }
@@ -346,19 +339,16 @@
         assertEquals(State.DISCONNECTED, networkInfo.getState());
 
         // Enable airplane mode
+        log("Enable airplane mode");
         cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
-        try {
-            Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
-        } catch (Exception e) {
-            Log.v(LOG_TAG, "exception: " + e.toString());
-        }
+        sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
 
         networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
         assertEquals(State.DISCONNECTED, networkInfo.getState());
 
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-            Log.v(LOG_TAG, "Mobile state transition validation failed.");
-            Log.v(LOG_TAG, "reason: " +
+            log("Mobile state transition validation failed.");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
             assertTrue(false);
         }
@@ -381,14 +371,14 @@
 
         // Validate the state transition
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-            Log.v(LOG_TAG, "Mobile state transition validation failed.");
-            Log.v(LOG_TAG, "reason: " +
+            log("Mobile state transition validation failed.");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
             assertTrue(false);
         }
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-          Log.v(LOG_TAG, "Wifi state transition validation failed.");
-          Log.v(LOG_TAG, "reason: " +
+          log("Wifi state transition validation failed.");
+          log("reason: " +
                   cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
           assertTrue(false);
         }
@@ -399,6 +389,7 @@
     public void testDataConnectionOverAMWithWifi() {
         assertNotNull("SSID is null", TEST_ACCESS_POINT);
         // Eanble airplane mode
+        log("Enable airplane mode");
         cmActivity.setAirplaneMode(getInstrumentation().getContext(), true);
 
         NetworkInfo networkInfo;
@@ -423,15 +414,15 @@
 
         // validate state and broadcast
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            Log.v(LOG_TAG, "state validate for Wifi failed");
-            Log.v(LOG_TAG, "reason: " +
+            log("state validate for Wifi failed");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
             assertTrue("State validation failed", false);
         }
         if (!UtilHelper.isWifiOnly()) {
             if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) {
-                Log.v(LOG_TAG, "state validation for Mobile failed");
-                Log.v(LOG_TAG, "reason: " +
+                log("state validation for Mobile failed");
+                log("reason: " +
                         cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE));
                 assertTrue("state validation failed", false);
             }
@@ -454,7 +445,7 @@
         try {
             Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
-            Log.v(LOG_TAG, "exception: " + e.toString());
+            log("exception: " + e.toString());
         }
 
         // Enable airplane mode without clearing Wifi
@@ -466,7 +457,7 @@
         try {
             Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
-            Log.v(LOG_TAG, "exception: " + e.toString());
+            log("exception: " + e.toString());
         }
 
         // Prepare for state validation
@@ -487,8 +478,8 @@
 
         // validate the state transition
         if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) {
-            Log.v(LOG_TAG, "Wifi state transition validation failed.");
-            Log.v(LOG_TAG, "reason: " +
+            log("Wifi state transition validation failed.");
+            log("reason: " +
                     cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI));
             assertTrue(false);
         }
@@ -511,13 +502,13 @@
         try {
             Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT);
         } catch (Exception e) {
-            Log.v(LOG_TAG, "exception: " + e.toString());
+            log("exception: " + e.toString());
         }
 
         // Disconnect from the current AP
-        Log.v(LOG_TAG, "disconnect from the AP");
+        log("disconnect from the AP");
         if (!cmActivity.disconnectAP()) {
-            Log.v(LOG_TAG, "failed to disconnect from " + TEST_ACCESS_POINT);
+            log("failed to disconnect from " + TEST_ACCESS_POINT);
         }
 
         // Verify the connectivity state for Wifi is DISCONNECTED
@@ -525,7 +516,7 @@
                 ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         if (!cmActivity.disableWifi()) {
-            Log.v(LOG_TAG, "disable Wifi failed");
+            log("disable Wifi failed");
             return;
         }
         assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED,
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
index 22b1759..d33a445 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -136,7 +136,7 @@
 
         // step 2: verify Wifi state and network state;
         assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI,
-                State.CONNECTED, 2 * ConnectivityManagerTestActivity.LONG_TIMEOUT));
+                State.CONNECTED, 6 * ConnectivityManagerTestActivity.LONG_TIMEOUT));
 
         // step 3: verify the current connected network is the given SSID
         assertNotNull("Wifi connection returns null", mAct.mWifiManager.getConnectionInfo());
@@ -166,8 +166,9 @@
             String ssid = networks.get(i).SSID;
             log("-- START Wi-Fi connection test to : " + ssid + " --");
             connectToWifi(networks.get(i));
-            sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT,
-                    "interruped while waiting for wifi disabled.");
+            // wait for 2 minutes between wifi stop and start
+            sleep(ConnectivityManagerTestActivity.WIFI_STOP_START_INTERVAL,
+                  "interruped while connected to wifi");
             log("-- END Wi-Fi connection test to " + ssid + " -- ");
         }
     }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
index 7578e67..0b32fde 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
@@ -92,6 +92,9 @@
         mPassword = mRunner.mReconnectPassword;
         mScanIterations = mRunner.mScanIterations;
         mWifiSleepTime = mRunner.mSleepTime;
+        log(String.format("mReconnectIterations(%d), mSsid(%s), mPassword(%s),"
+            + "mScanIterations(%d), mWifiSleepTime(%d)", mReconnectIterations, mSsid,
+            mPassword, mScanIterations, mWifiSleepTime));
         mOutputWriter = new BufferedWriter(new FileWriter(new File(
                 Environment.getExternalStorageDirectory(), OUTPUT_FILE), true));
         mAct.turnScreenOn();
@@ -248,6 +251,7 @@
         assertTrue("Wi-Fi is connected, but no data connection.", mAct.pingTest(null));
 
         int i;
+        long sum = 0;
         for (i = 0; i < mReconnectIterations; i++) {
             // 1. Put device into sleep mode
             // 2. Wait for the device to sleep for sometime, verify wi-fi is off and mobile is on.
@@ -284,12 +288,18 @@
 
             // Turn screen on again
             mAct.turnScreenOn();
+            // Measure the time for Wi-Fi to get connected
+            long startTime = System.currentTimeMillis();
             assertTrue("Wait for Wi-Fi enable timeout after wake up",
                     mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
                     ConnectivityManagerTestActivity.SHORT_TIMEOUT));
             assertTrue("Wait for Wi-Fi connection timeout after wake up",
                     mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
-                    ConnectivityManagerTestActivity.LONG_TIMEOUT));
+                    6 * ConnectivityManagerTestActivity.LONG_TIMEOUT));
+            long connectionTime = System.currentTimeMillis() - startTime;
+            sum += connectionTime;
+            log("average reconnection time is: " + sum/(i+1));
+
             assertTrue("Reconnect to Wi-Fi network, but no data connection.", mAct.pingTest(null));
         }
         if (i == mReconnectIterations) {
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
index 58f158c..60b6dc1 100644
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -19,6 +19,8 @@
 
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
index abd7d9a..755e7c4 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
@@ -32,6 +32,8 @@
 public class BluetoothStressTest extends InstrumentationTestCase {
     private static final String TAG = "BluetoothStressTest";
     private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt";
+    /** The amount of time to sleep between issuing start/stop SCO in ms. */
+    private static final long SCO_SLEEP_TIME = 2 * 1000;
 
     private BluetoothTestUtils mTestUtils;
 
@@ -380,11 +382,20 @@
         for (int i = 0; i < iterations; i++) {
             mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations);
             mTestUtils.startSco(adapter, device);
+            sleep(SCO_SLEEP_TIME);
             mTestUtils.stopSco(adapter, device);
+            sleep(SCO_SLEEP_TIME);
         }
 
         mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null);
         mTestUtils.unpair(adapter, device);
         mTestUtils.disable(adapter);
     }
+
+    private void sleep(long time) {
+        try {
+            Thread.sleep(time);
+        } catch (InterruptedException e) {
+        }
+    }
 }
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index 42e5cd1..4858be8 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -1425,7 +1425,7 @@
     }
 
     private StartStopScoReceiver getStartStopScoReceiver(int expectedFlags) {
-        String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED};
+        String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED};
         StartStopScoReceiver receiver = new StartStopScoReceiver(expectedFlags);
         addReceiver(receiver, actions);
         return receiver;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index a077cbc5..158f785 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -163,7 +163,6 @@
         render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
                 0, 0, NULL);
     }
-
 }
 
 void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
@@ -615,7 +614,8 @@
 void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
         float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
         float x4, float y4, float z4, float u4, float v4) {
-    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
+    if (mClip &&
+            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
         return;
     }
 
@@ -723,11 +723,16 @@
         return image;
     }
 
+    mClip = NULL;
+    mBounds = NULL;
+
     Rect bounds;
     mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
+
     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
     uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
+
     for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
         dataBuffer[i] = 0;
     }
@@ -765,8 +770,11 @@
     mDrawn = false;
     mBounds = bounds;
     mClip = clip;
+
     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
+
     mBounds = NULL;
+    mClip = NULL;
 
     if (mCurrentQuadIndex != 0) {
         issueDrawCommand();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7561a47..4d22646 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -201,14 +201,16 @@
 }
 
 void OpenGLRenderer::resume() {
-    glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight());
+    sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
+
+    glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
 
     glEnable(GL_SCISSOR_TEST);
     dirtyClip();
 
     glDisable(GL_DITHER);
 
-    glBindFramebuffer(GL_FRAMEBUFFER, mSnapshot->fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
     mCaches.blend = true;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 780c0d2..aea31a8 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -89,6 +89,12 @@
 
 static const nsecs_t kSetParametersTimeout = seconds(2);
 
+// minimum sleep time for the mixer thread loop when tracks are active but in underrun
+static const uint32_t kMinThreadSleepTimeUs = 5000;
+// maximum divider applied to the active sleep time in the mixer thread loop
+static const uint32_t kMaxThreadSleepTimeShift = 2;
+
+
 // ----------------------------------------------------------------------------
 
 static bool recordingAllowed() {
@@ -1846,6 +1852,7 @@
     uint32_t activeSleepTime = activeSleepTimeUs();
     uint32_t idleSleepTime = idleSleepTimeUs();
     uint32_t sleepTime = idleSleepTime;
+    uint32_t sleepTimeShift = 0;
     Vector< sp<EffectChain> > effectChains;
 #ifdef DEBUG_CPU_USAGE
     ThreadCpuUsage cpu;
@@ -1937,6 +1944,7 @@
 
                     standbyTime = systemTime() + kStandbyTimeInNsecs;
                     sleepTime = idleSleepTime;
+                    sleepTimeShift = 0;
                     continue;
                 }
             }
@@ -1953,6 +1961,10 @@
             // mix buffers...
             mAudioMixer->process();
             sleepTime = 0;
+            // increase sleep time progressively when application underrun condition clears
+            if (sleepTimeShift > 0) {
+                sleepTimeShift--;
+            }
             standbyTime = systemTime() + kStandbyTimeInNsecs;
             //TODO: delay standby when effects have a tail
         } else {
@@ -1960,7 +1972,17 @@
             // buffer size, then write 0s to the output
             if (sleepTime == 0) {
                 if (mixerStatus == MIXER_TRACKS_ENABLED) {
-                    sleepTime = activeSleepTime;
+                    sleepTime = activeSleepTime >> sleepTimeShift;
+                    if (sleepTime < kMinThreadSleepTimeUs) {
+                        sleepTime = kMinThreadSleepTimeUs;
+                    }
+                    // reduce sleep time in case of consecutive application underruns to avoid
+                    // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
+                    // duration we would end up writing less data than needed by the audio HAL if
+                    // the condition persists.
+                    if (sleepTimeShift < kMaxThreadSleepTimeShift) {
+                        sleepTimeShift++;
+                    }
                 } else {
                     sleepTime = idleSleepTime;
                 }
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index ee62a56..77575f2 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -151,18 +151,22 @@
 
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags,
-            boolean insetsPending, Rect outFrame, Rect outContentInsets,
+            int flags, Rect outFrame, Rect outContentInsets,
             Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
         if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         int res = mService.relayoutWindow(this, window, seq, attrs,
-                requestedWidth, requestedHeight, viewFlags, insetsPending,
+                requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface);
         if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
         return res;
     }
 
+    public void performDeferredDestroy(IWindow window) {
+        mService.performDeferredDestroyWindow(this, window);
+    }
+
     public boolean outOfMemory(IWindow window) {
         return mService.outOfMemoryWindow(this, window);
     }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index ebb13d5..769e6cf 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -2499,12 +2499,13 @@
 
     public int relayoutWindow(Session session, IWindow client, int seq,
             WindowManager.LayoutParams attrs, int requestedWidth,
-            int requestedHeight, int viewVisibility, boolean insetsPending,
+            int requestedHeight, int viewVisibility, int flags,
             Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
             Configuration outConfig, Surface outSurface) {
         boolean displayed = false;
         boolean inTouchMode;
         boolean configChanged;
+        boolean surfaceChanged = false;
 
         // if they don't have this permission, mask out the status bar bits
         int systemUiVisibility = 0;
@@ -2534,6 +2535,9 @@
                 mPolicy.adjustWindowParamsLw(attrs);
             }
 
+            win.mSurfaceDestroyDeferred =
+                    (flags&WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
+
             int attrChanges = 0;
             int flagChanges = 0;
             if (attrs != null) {
@@ -2630,8 +2634,12 @@
                     // To change the format, we need to re-build the surface.
                     win.destroySurfaceLocked();
                     displayed = true;
+                    surfaceChanged = true;
                 }
                 try {
+                    if (win.mSurface == null) {
+                        surfaceChanged = true;
+                    }
                     Surface surface = win.createSurfaceLocked();
                     if (surface != null) {
                         outSurface.copyFrom(surface);
@@ -2683,6 +2691,7 @@
                     // If we are not currently running the exit animation, we
                     // need to see about starting one.
                     if (!win.mExiting || win.mSurfacePendingDestroy) {
+                        surfaceChanged = true;
                         // Try starting an animation; if there isn't one, we
                         // can destroy the surface right away.
                         int transit = WindowManagerPolicy.TRANSIT_EXIT;
@@ -2715,10 +2724,10 @@
                 if (win.mSurface == null || (win.getAttrs().flags
                         & WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0
                         || win.mSurfacePendingDestroy) {
-                    // We are being called from a local process, which
+                    // We could be called from a local process, which
                     // means outSurface holds its current surface.  Ensure the
-                    // surface object is cleared, but we don't want it actually
-                    // destroyed at this point.
+                    // surface object is cleared, but we don't necessarily want
+                    // it actually destroyed at this point.
                     win.mSurfacePendingDestroy = false;
                     outSurface.release();
                     if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
@@ -2760,7 +2769,7 @@
             }
 
             mLayoutNeeded = true;
-            win.mGivenInsetsPending = insetsPending;
+            win.mGivenInsetsPending = (flags&WindowManagerImpl.RELAYOUT_INSETS_PENDING) != 0;
             if (assignLayers) {
                 assignLayersLocked();
             }
@@ -2797,8 +2806,25 @@
 
         Binder.restoreCallingIdentity(origId);
 
-        return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)
-                | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0);
+        return (inTouchMode ? WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE : 0)
+                | (displayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0)
+                | (surfaceChanged ? WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED : 0);
+    }
+
+    public void performDeferredDestroyWindow(Session session, IWindow client) {
+        long origId = Binder.clearCallingIdentity();
+
+        try {
+            synchronized(mWindowMap) {
+                WindowState win = windowForClientLocked(session, client, false);
+                if (win == null) {
+                    return;
+                }
+                win.destroyDeferredSurfaceLocked();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
     }
 
     public boolean outOfMemoryWindow(Session session, IWindow client) {
@@ -3738,7 +3764,7 @@
                 return;
             }
 
-            // If this is a translucent or wallpaper window, then don't
+            // If this is a translucent window, then don't
             // show a starting window -- the current effect (a full-screen
             // opaque starting window that fades away to the real contents
             // when it is ready) does not work for this.
@@ -3755,7 +3781,16 @@
                 }
                 if (ent.array.getBoolean(
                         com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
-                    return;
+                    if (mWallpaperTarget == null) {
+                        // If this theme is requesting a wallpaper, and the wallpaper
+                        // is not curently visible, then this effectively serves as
+                        // an opaque window and our starting window transition animation
+                        // can still work.  We just need to make sure the starting window
+                        // is also showing the wallpaper.
+                        windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+                    } else {
+                        return;
+                    }
                 }
             }
 
@@ -7659,7 +7694,8 @@
                         // a detached wallpaper animation.
                         if (nowAnimating) {
                             if (w.mAnimation != null) {
-                                if (w.mAnimation.getDetachWallpaper()) {
+                                if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+                                        && w.mAnimation.getDetachWallpaper()) {
                                     windowDetachedWallpaper = w;
                                 }
                                 if (w.mAnimation.getBackgroundColor() != 0) {
@@ -7679,7 +7715,8 @@
                         // displayed behind it.
                         if (w.mAppToken != null && w.mAppToken.animation != null
                                 && w.mAppToken.animating) {
-                            if (w.mAppToken.animation.getDetachWallpaper()) {
+                            if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+                                    && w.mAppToken.animation.getDetachWallpaper()) {
                                 windowDetachedWallpaper = w;
                             }
                             if (w.mAppToken.animation.getBackgroundColor() != 0) {
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 23ec2d9..aa7bf2d 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -85,6 +85,7 @@
     boolean mPolicyVisibilityAfterAnim = true;
     boolean mAppFreezing;
     Surface mSurface;
+    Surface mPendingDestroySurface;
     boolean mReportDestroySurface;
     boolean mSurfacePendingDestroy;
     boolean mAttachedHidden;    // is our parent window hidden?
@@ -121,7 +122,13 @@
      * we must tell them application to resize (and thus redraw itself).
      */
     boolean mSurfaceResized;
-    
+
+    /**
+     * Set if the client has asked that the destroy of its surface be delayed
+     * until it explicitly says it is okay.
+     */
+    boolean mSurfaceDestroyDeferred;
+
     /**
      * Insets that determine the actually visible area.  These are in the application's
      * coordinate space (without compatibility scale applied).
@@ -764,15 +771,32 @@
                     Slog.w(WindowManagerService.TAG, "Window " + this + " destroying surface "
                             + mSurface + ", session " + mSession, e);
                 }
-                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-                    RuntimeException e = null;
-                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                        e = new RuntimeException();
-                        e.fillInStackTrace();
+                if (mSurfaceDestroyDeferred) {
+                    if (mSurface != null && mPendingDestroySurface != mSurface) {
+                        if (mPendingDestroySurface != null) {
+                            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                                RuntimeException e = null;
+                                if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                                    e = new RuntimeException();
+                                    e.fillInStackTrace();
+                                }
+                                WindowManagerService.logSurface(this, "DESTROY PENDING", e);
+                            }
+                            mPendingDestroySurface.destroy();
+                        }
+                        mPendingDestroySurface = mSurface;
                     }
-                    WindowManagerService.logSurface(this, "DESTROY", e);
+                } else {
+                    if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                        RuntimeException e = null;
+                        if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                            e = new RuntimeException();
+                            e.fillInStackTrace();
+                        }
+                        WindowManagerService.logSurface(this, "DESTROY", e);
+                    }
+                    mSurface.destroy();
                 }
-                mSurface.destroy();
             } catch (RuntimeException e) {
                 Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window " + this
                     + " surface " + mSurface + " session " + mSession
@@ -784,6 +808,28 @@
         }
     }
 
+    void destroyDeferredSurfaceLocked() {
+        try {
+            if (mPendingDestroySurface != null) {
+                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                    RuntimeException e = null;
+                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+                        e = new RuntimeException();
+                        e.fillInStackTrace();
+                    }
+                    mService.logSurface(this, "DESTROY PENDING", e);
+                }
+                mPendingDestroySurface.destroy();
+            }
+        } catch (RuntimeException e) {
+            Slog.w(WindowManagerService.TAG, "Exception thrown when destroying Window "
+                    + this + " surface " + mPendingDestroySurface
+                    + " session " + mSession + ": " + e.toString());
+        }
+        mSurfaceDestroyDeferred = false;
+        mPendingDestroySurface = null;
+    }
+
     boolean finishDrawingLocked() {
         if (mDrawPending) {
             if (SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) Slog.v(
@@ -977,6 +1023,9 @@
             mAnimation.cancel();
             mAnimation = null;
         }
+        if (mService.mWindowDetachedWallpaper == this) {
+            mService.mWindowDetachedWallpaper = null;
+        }
         mAnimLayer = mLayer;
         if (mIsImWindow) {
             mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
@@ -1415,6 +1464,7 @@
             if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(WindowManagerService.TAG, "Removing " + this + " from " + mAttachedWindow);
             mAttachedWindow.mChildWindows.remove(this);
         }
+        destroyDeferredSurfaceLocked();
         destroySurfaceLocked();
         mSession.windowRemovedLocked();
         try {
@@ -1612,6 +1662,10 @@
                     pw.print(") "); pw.print(mSurfaceW);
                     pw.print(" x "); pw.println(mSurfaceH);
         }
+        if (mPendingDestroySurface != null) {
+            pw.print(prefix); pw.print("mPendingDestroySurface=");
+                    pw.println(mPendingDestroySurface);
+        }
         if (dumpAll) {
             pw.print(prefix); pw.print("mToken="); pw.println(mToken);
             pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
@@ -1640,6 +1694,10 @@
         if (!mRelayoutCalled) {
             pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled);
         }
+        if (mSurfaceResized || mSurfaceDestroyDeferred) {
+            pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
+                    pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
+        }
         if (mXOffset != 0 || mYOffset != 0) {
             pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
                     pw.print(" y="); pw.println(mYOffset);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 7cd01a1..de09dfb 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -1848,8 +1848,14 @@
             DataConnection dc = apnContext.getDataConnection();
 
             if (DBG) {
-                log(String.format("onDataSetupComplete: success apn=%s",
-                    apnContext.getWaitingApns().get(0).apn));
+                // TODO We may use apnContext.getApnSetting() directly
+                // instead of getWaitingApns().get(0)
+                String apnStr = "<unknown>";
+                if (apnContext.getWaitingApns() != null
+                        && !apnContext.getWaitingApns().isEmpty()){
+                    apnStr = apnContext.getWaitingApns().get(0).apn;
+                }
+                log("onDataSetupComplete: success apn=" + apnStr);
             }
             ApnSetting apn = apnContext.getApnSetting();
             if (apn.proxy != null && apn.proxy.length() != 0) {
diff --git a/tests/FrameworkPerf/res/layout/main.xml b/tests/FrameworkPerf/res/layout/main.xml
index 7812648..e00ad92 100644
--- a/tests/FrameworkPerf/res/layout/main.xml
+++ b/tests/FrameworkPerf/res/layout/main.xml
@@ -66,7 +66,24 @@
         >
         <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
             android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="Limit by: "
+            />
+        <Spinner android:id="@+id/limitspinner"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:drawSelectorOnTop="true"
+        />
+    </LinearLayout>
+
+    <LinearLayout android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_marginTop="10dp"
+        >
+        <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
             android:text="Test time (ms): "
+            android:id="@+id/limitlabel"
             />
         <EditText android:id="@+id/testtime"
             android:layout_width="match_parent"
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
index 8ee5978..30a968f 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
@@ -50,6 +50,8 @@
 
     Spinner mFgSpinner;
     Spinner mBgSpinner;
+    Spinner mLimitSpinner;
+    TextView mLimitLabel;
     TextView mTestTime;
     Button mStartButton;
     Button mStopButton;
@@ -58,10 +60,12 @@
     PowerManager.WakeLock mPartialWakeLock;
 
     long mMaxRunTime = 5000;
+    boolean mLimitIsIterations;
     boolean mStarted;
 
     final String[] mAvailOpLabels;
     final String[] mAvailOpDescriptions;
+    final String[] mLimitLabels = { "Time", "Iterations" };
 
     int mFgTestIndex = -1;
     int mBgTestIndex = -1;
@@ -169,8 +173,15 @@
         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
         mBgSpinner.setAdapter(adapter);
         mBgSpinner.setOnItemSelectedListener(this);
+        mLimitSpinner = (Spinner) findViewById(R.id.limitspinner);
+        adapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_spinner_item, mLimitLabels);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mLimitSpinner.setAdapter(adapter);
+        mLimitSpinner.setOnItemSelectedListener(this);
 
         mTestTime = (TextView)findViewById(R.id.testtime);
+        mLimitLabel = (TextView)findViewById(R.id.limitlabel);
 
         mStartButton = (Button)findViewById(R.id.start);
         mStartButton.setOnClickListener(new View.OnClickListener() {
@@ -196,16 +207,23 @@
 
     @Override
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-        if (parent == mFgSpinner || parent == mBgSpinner) {
+        if (parent == mFgSpinner || parent == mBgSpinner || parent == mLimitSpinner) {
             TestService.Op op = TestService.mAvailOps[position];
             if (parent == mFgSpinner) {
                 mFgTestIndex = position;
                 mFgTest = op;
                 ((TextView)findViewById(R.id.fgtext)).setText(mAvailOpDescriptions[position]);
-            } else {
+            } else if (parent == mBgSpinner) {
                 mBgTestIndex = position;
                 mBgTest = op;
                 ((TextView)findViewById(R.id.bgtext)).setText(mAvailOpDescriptions[position]);
+            } else if (parent == mLimitSpinner) {
+                mLimitIsIterations = (position != 0);
+                if (mLimitIsIterations) {
+                    mLimitLabel.setText("Iterations: ");
+                } else {
+                    mLimitLabel.setText("Test time (ms): ");
+                }
             }
         }
     }
@@ -234,7 +252,11 @@
             return;
         }
         TestArgs args = new TestArgs();
-        args.maxTime = mMaxRunTime;
+        if (mLimitIsIterations) {
+            args.maxOps = mMaxRunTime;
+        } else {
+            args.maxTime = mMaxRunTime;
+        }
         if (mFgTestIndex == 0 && mBgTestIndex == 0) {
             args.combOp = mCurOpIndex;
         } else if (mFgTestIndex != 0 && mBgTestIndex != 0) {
@@ -376,6 +398,7 @@
             mTestTime.setEnabled(false);
             mFgSpinner.setEnabled(false);
             mBgSpinner.setEnabled(false);
+            mLimitSpinner.setEnabled(false);
             updateWakeLock();
             startService(new Intent(this, SchedulerService.class));
             mCurOpIndex = 0;
@@ -397,6 +420,7 @@
             mTestTime.setEnabled(true);
             mFgSpinner.setEnabled(true);
             mBgSpinner.setEnabled(true);
+            mLimitSpinner.setEnabled(true);
             updateWakeLock();
             stopService(new Intent(this, SchedulerService.class));
             synchronized (mResults) {
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/TestArgs.java b/tests/FrameworkPerf/src/com/android/frameworkperf/TestArgs.java
index f2f7c56..2fe38aa 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/TestArgs.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/TestArgs.java
@@ -21,6 +21,7 @@
 
 public class TestArgs implements Parcelable {
     long maxTime;
+    long maxOps = -1;
     int combOp = -1;
     int fgOp = -1;
     int bgOp = -1;
@@ -30,6 +31,7 @@
 
     public TestArgs(Parcel source) {
         maxTime = source.readLong();
+        maxOps = source.readLong();
         combOp = source.readInt();
         fgOp = source.readInt();
         bgOp = source.readInt();
@@ -43,6 +45,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(maxTime);
+        dest.writeLong(maxOps);
         dest.writeInt(combOp);
         dest.writeInt(fgOp);
         dest.writeInt(bgOp);
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java b/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java
index 8cf1ac2..a8c43e9 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java
@@ -224,6 +224,7 @@
     public class TestRunner {
         Handler mHandler;
         long mMaxRunTime;
+        long mMaxOps;
         Op mForegroundOp;
         Op mBackgroundOp;
         Runnable mDoneCallback;
@@ -277,6 +278,7 @@
         public void run(Handler handler, TestArgs args, Runnable doneCallback) {
             mHandler = handler;
             mMaxRunTime = args.maxTime;
+            mMaxOps = args.maxOps;
             if (args.combOp >= 0) {
                 mForegroundOp = mOpPairs[args.combOp];
                 mBackgroundOp = mOpPairs[args.combOp+1];
@@ -352,9 +354,18 @@
                 if (!mBackgroundRunning && !mForegroundRunning) {
                     return false;
                 }
-                long now = SystemClock.uptimeMillis();
-                if (now > (mStartTime+mMaxRunTime)) {
-                    return false;
+                if (mMaxOps > 0) {
+                    // iteration-limited case
+                    if (mForegroundOps >= mMaxOps) {
+                        return false;
+                    }
+                    mForegroundOps++;
+                } else {
+                    // time-limited case
+                    long now = SystemClock.uptimeMillis();
+                    if (now > (mStartTime+mMaxRunTime)) {
+                        return false;
+                    }
                 }
                 return true;
             }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 1d97e15..a640a91 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -79,12 +79,16 @@
     }
 
     public int relayout(IWindow arg0, int seq, LayoutParams arg1, int arg2, int arg3, int arg4,
-            boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8)
+            int arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8)
             throws RemoteException {
         // pass for now.
         return 0;
     }
 
+    public void performDeferredDestroy(IWindow window) {
+        // pass for now.
+    }
+
     public boolean outOfMemory(IWindow window) throws RemoteException {
         return false;
     }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index aadcaad..3ed9bd5 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -123,6 +123,8 @@
     private final LruCache<String, ScanResult> mScanResultCache;
 
     private String mInterfaceName;
+    /* Tethering interface could be seperate from wlan interface */
+    private String mTetherInterfaceName;
 
     private int mLastSignalLevel = -1;
     private String mLastBssid;
@@ -156,6 +158,14 @@
     /* Tracks sequence number on stop failure message */
     private int mSupplicantStopFailureToken = 0;
 
+    /**
+     * Tether state change notification time out
+     */
+    private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
+
+    /* Tracks sequence number on a tether notification time out */
+    private int mTetherToken = 0;
+
     private LinkProperties mLinkProperties;
 
     // Wakelock held during wifi start/stop and driver load/unload
@@ -240,10 +250,12 @@
     static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
     /* Response to access point configuration request */
     static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
-    /* Set configuration on tether interface */
-    static final int CMD_TETHER_INTERFACE                 = BASE + 29;
+    /* Invoked when getting a tether state change notification */
+    static final int CMD_TETHER_STATE_CHANGE              = BASE + 29;
+    /* A delayed message sent to indicate tether state change failed to arrive */
+    static final int CMD_TETHER_NOTIFICATION_TIMED_OUT    = BASE + 30;
 
-    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 30;
+    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 31;
 
     /* Supplicant commands */
     /* Is supplicant alive ? */
@@ -455,12 +467,25 @@
     private State mSoftApStartingState = new SoftApStartingState();
     /* Soft ap is running */
     private State mSoftApStartedState = new SoftApStartedState();
+    /* Soft ap is running and we are waiting for tether notification */
+    private State mTetheringState = new TetheringState();
     /* Soft ap is running and we are tethered through connectivity service */
     private State mTetheredState = new TetheredState();
+    /* Waiting for untether confirmation to stop soft Ap */
+    private State mSoftApStoppingState = new SoftApStoppingState();
 
     /* Wait till p2p is disabled */
     private State mWaitForP2pDisableState = new WaitForP2pDisableState();
 
+    private class TetherStateChange {
+        ArrayList<String> available;
+        ArrayList<String> active;
+        TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
+            available = av;
+            active = ac;
+        }
+    }
+
 
     /**
      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
@@ -562,7 +587,9 @@
                 public void onReceive(Context context, Intent intent) {
                     ArrayList<String> available = intent.getStringArrayListExtra(
                             ConnectivityManager.EXTRA_AVAILABLE_TETHER);
-                    sendMessage(CMD_TETHER_INTERFACE, available);
+                    ArrayList<String> active = intent.getStringArrayListExtra(
+                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
+                    sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
                 }
             },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
 
@@ -603,7 +630,9 @@
             addState(mSupplicantStoppingState, mDefaultState);
             addState(mSoftApStartingState, mDefaultState);
             addState(mSoftApStartedState, mDefaultState);
+                addState(mTetheringState, mSoftApStartedState);
                 addState(mTetheredState, mSoftApStartedState);
+            addState(mSoftApStoppingState, mDefaultState);
             addState(mWaitForP2pDisableState, mDefaultState);
 
         setInitialState(mInitialState);
@@ -1139,6 +1168,7 @@
                         loge("Error tethering on " + intf);
                         return false;
                     }
+                    mTetherInterfaceName = intf;
                     return true;
                 }
             }
@@ -1165,11 +1195,27 @@
             loge("Error resetting interface " + mInterfaceName + ", :" + e);
         }
 
-        if (mCm.untether(mInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+        if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
             loge("Untether initiate failed!");
         }
     }
 
+    private boolean isWifiTethered(ArrayList<String> active) {
+
+        checkAndSetConnectivityInstance();
+
+        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
+        for (String intf : active) {
+            for (String regex : wifiRegexs) {
+                if (intf.matches(regex)) {
+                    return true;
+                }
+            }
+        }
+        // We found no interfaces that are tethered
+        return false;
+    }
+
     /**
      * Set the country code from the system setting value, if any.
      */
@@ -1800,7 +1846,8 @@
                 case CMD_START_AP_SUCCESS:
                 case CMD_START_AP_FAILURE:
                 case CMD_STOP_AP:
-                case CMD_TETHER_INTERFACE:
+                case CMD_TETHER_STATE_CHANGE:
+                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
                 case CMD_START_SCAN:
                 case CMD_DISCONNECT:
                 case CMD_RECONNECT:
@@ -3284,7 +3331,7 @@
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
-                case CMD_TETHER_INTERFACE:
+                case CMD_TETHER_STATE_CHANGE:
                 case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
@@ -3326,7 +3373,8 @@
                 case CMD_STOP_AP:
                     if (DBG) log("Stopping Soft AP");
                     setWifiApState(WIFI_AP_STATE_DISABLING);
-                    stopTethering();
+
+                    /* We have not tethered at this point, so we just shutdown soft Ap */
                     try {
                         mNwService.stopAccessPoint(mInterfaceName);
                     } catch(Exception e) {
@@ -3342,10 +3390,10 @@
                    loge("Cannot start supplicant with a running soft AP");
                     setWifiState(WIFI_STATE_UNKNOWN);
                     break;
-                case CMD_TETHER_INTERFACE:
-                    ArrayList<String> available = (ArrayList<String>) message.obj;
-                    if (startTethering(available)) {
-                        transitionTo(mTetheredState);
+                case CMD_TETHER_STATE_CHANGE:
+                    TetherStateChange stateChange = (TetherStateChange) message.obj;
+                    if (startTethering(stateChange.available)) {
+                        transitionTo(mTetheringState);
                     }
                     break;
                 case WifiP2pService.P2P_ENABLE_PENDING:
@@ -3405,6 +3453,58 @@
         }
     }
 
+    class TetheringState extends State {
+        @Override
+        public void enter() {
+            if (DBG) log(getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            /* Send ourselves a delayed message to shut down if tethering fails to notify */
+            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
+                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) log(getName() + message.toString() + "\n");
+            switch(message.what) {
+                case CMD_TETHER_STATE_CHANGE:
+                    TetherStateChange stateChange = (TetherStateChange) message.obj;
+                    if (isWifiTethered(stateChange.active)) {
+                        transitionTo(mTetheredState);
+                    }
+                    return HANDLED;
+                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
+                    if (message.arg1 == mTetherToken) {
+                        loge("Failed to get tether update, shutdown soft access point");
+                        setWifiApEnabled(null, false);
+                    }
+                    break;
+                case CMD_LOAD_DRIVER:
+                case CMD_UNLOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_STOP_SUPPLICANT:
+                case CMD_START_AP:
+                case CMD_STOP_AP:
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_SET_FREQUENCY_BAND:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                case WifiP2pService.P2P_ENABLE_PENDING:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
     class TetheredState extends State {
         @Override
         public void enter() {
@@ -3415,13 +3515,89 @@
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
-               case CMD_TETHER_INTERFACE:
-                    // Ignore any duplicate interface available notifications
-                    // when in tethered state
+                case CMD_TETHER_STATE_CHANGE:
+                    TetherStateChange stateChange = (TetherStateChange) message.obj;
+                    if (!isWifiTethered(stateChange.active)) {
+                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
+                        setWifiApEnabled(null, false);
+                    }
                     return HANDLED;
+                case CMD_STOP_AP:
+                    if (DBG) log("Untethering before stopping AP");
+                    setWifiApState(WIFI_AP_STATE_DISABLING);
+                    stopTethering();
+                    transitionTo(mSoftApStoppingState);
+                    break;
                 default:
                     return NOT_HANDLED;
             }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class SoftApStoppingState extends State {
+        @Override
+        public void enter() {
+            if (DBG) log(getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            /* Send ourselves a delayed message to shut down if tethering fails to notify */
+            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
+                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
+
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) log(getName() + message.toString() + "\n");
+            switch(message.what) {
+                case CMD_TETHER_STATE_CHANGE:
+                    TetherStateChange stateChange = (TetherStateChange) message.obj;
+
+                    /* Wait till wifi is untethered */
+                    if (isWifiTethered(stateChange.active)) break;
+
+                    try {
+                        mNwService.stopAccessPoint(mInterfaceName);
+                    } catch(Exception e) {
+                        loge("Exception in stopAccessPoint()");
+                    }
+                    transitionTo(mDriverLoadedState);
+                    break;
+                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
+                    if (message.arg1 == mTetherToken) {
+                        loge("Failed to get tether update, force stop access point");
+                        try {
+                            mNwService.stopAccessPoint(mInterfaceName);
+                        } catch(Exception e) {
+                            loge("Exception in stopAccessPoint()");
+                        }
+                        transitionTo(mDriverLoadedState);
+                    }
+                    break;
+                case CMD_LOAD_DRIVER:
+                case CMD_UNLOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_STOP_SUPPLICANT:
+                case CMD_START_AP:
+                case CMD_STOP_AP:
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_SET_FREQUENCY_BAND:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                case WifiP2pService.P2P_ENABLE_PENDING:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
         }
     }