Merge "Import translations. DO NOT MERGE"
diff --git a/Android.mk b/Android.mk
index c2910fd..be7e055 100644
--- a/Android.mk
+++ b/Android.mk
@@ -155,6 +155,7 @@
 	core/java/android/net/INetworkManagementEventObserver.aidl \
 	core/java/android/net/INetworkPolicyListener.aidl \
 	core/java/android/net/INetworkPolicyManager.aidl \
+	core/java/android/net/INetworkScoreService.aidl \
 	core/java/android/net/INetworkStatsService.aidl \
 	core/java/android/net/INetworkStatsSession.aidl \
 	core/java/android/net/nsd/INsdManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3573e6d..4e2a3bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27974,7 +27974,6 @@
 
   public static final class TvInputManager.Session {
     method public void release();
-    method public void setSurface(android.view.Surface);
     method public void setVolume(float);
     method public void tune(android.net.Uri);
   }
@@ -27996,12 +27995,22 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.tv.TvInputService";
   }
 
-  public static abstract class TvInputService.TvInputSessionImpl {
+  public abstract class TvInputService.TvInputSessionImpl {
     ctor public TvInputService.TvInputSessionImpl();
+    method public android.view.View onCreateOverlayView();
     method public abstract void onRelease();
     method public abstract boolean onSetSurface(android.view.Surface);
     method public abstract void onSetVolume(float);
     method public abstract boolean onTune(android.net.Uri);
+    method public void setOverlayViewEnabled(boolean);
+  }
+
+  public class TvView extends android.view.SurfaceView {
+    ctor public TvView(android.content.Context);
+    ctor public TvView(android.content.Context, android.util.AttributeSet);
+    ctor public TvView(android.content.Context, android.util.AttributeSet, int);
+    method public void bindTvInput(android.content.ComponentName, android.tv.TvInputManager.SessionCreateCallback);
+    method public void unbindTvInput();
   }
 
 }
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 51cb12a..edf21dd 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -33,17 +33,20 @@
 import android.view.Surface;
 import android.view.TextureView;
 import android.view.TextureView.SurfaceTextureListener;
-import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import dalvik.system.CloseGuard;
+
+import java.lang.ref.WeakReference;
 
 /** @hide */
 public class ActivityView extends ViewGroup {
-    private final String TAG = "ActivityView";
-    private final boolean DEBUG = false;
+    private static final String TAG = "ActivityView";
+    private static final boolean DEBUG = false;
 
+    DisplayMetrics mMetrics;
     private final TextureView mTextureView;
-    private IActivityContainer mActivityContainer;
+    private ActivityContainerWrapper mActivityContainer;
     private Activity mActivity;
     private int mWidth;
     private int mHeight;
@@ -75,9 +78,23 @@
             throw new IllegalStateException("The ActivityView's Context is not an Activity.");
         }
 
+        try {
+            mActivityContainer = new ActivityContainerWrapper(
+                    ActivityManagerNative.getDefault().createActivityContainer(
+                            mActivity.getActivityToken(), new ActivityContainerCallback(this)));
+        } catch (RemoteException e) {
+            throw new RuntimeException("ActivityView: Unable to create ActivityContainer. "
+                    + e);
+        }
+
         mTextureView = new TextureView(context);
         mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
         addView(mTextureView);
+
+        WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
+        mMetrics = new DisplayMetrics();
+        wm.getDefaultDisplay().getMetrics(mMetrics);
+
         if (DEBUG) Log.v(TAG, "ctor()");
     }
 
@@ -86,57 +103,8 @@
         mTextureView.layout(0, 0, r - l, b - t);
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        if (DEBUG) Log.v(TAG, "onAttachedToWindow()");
-        super.onAttachedToWindow();
-        try {
-            final IBinder token = mActivity.getActivityToken();
-            mActivityContainer = ActivityManagerNative.getDefault().createActivityContainer(token,
-                      new ActivityContainerCallback());
-        } catch (RemoteException e) {
-            throw new IllegalStateException("ActivityView: Unable to create ActivityContainer. "
-                    + e);
-        }
-
-        attachToSurfaceWhenReady();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer);
-        super.onDetachedFromWindow();
-        if (mActivityContainer != null) {
-            detach();
-            try {
-                ActivityManagerNative.getDefault().deleteActivityContainer(mActivityContainer);
-            } catch (RemoteException e) {
-            }
-            mActivityContainer = null;
-        }
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        if (DEBUG) Log.v(TAG, "onWindowVisibilityChanged(): visibility=" + visibility);
-        super.onWindowVisibilityChanged(visibility);
-        switch (visibility) {
-            case  View.VISIBLE:
-                attachToSurfaceWhenReady();
-                break;
-            case  View.INVISIBLE:
-                break;
-            case View.GONE:
-                break;
-        }
-    }
-
     private boolean injectInputEvent(InputEvent event) {
-        try {
-            return mActivityContainer != null && mActivityContainer.injectEvent(event);
-        } catch (RemoteException e) {
-            return false;
-        }
+        return mActivityContainer != null && mActivityContainer.injectEvent(event);
     }
 
     @Override
@@ -154,40 +122,45 @@
         return super.onGenericMotionEvent(event);
     }
 
+    @Override
+    public void onAttachedToWindow() {
+        if (DEBUG) Log.v(TAG, "onAttachedToWindow(): mActivityContainer=" + mActivityContainer +
+                " mSurface=" + mSurface);
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer +
+                " mSurface=" + mSurface);
+    }
+
     public boolean isAttachedToDisplay() {
         return mSurface != null;
     }
 
     public void startActivity(Intent intent) {
+        if (mActivityContainer == null) {
+            throw new IllegalStateException("Attempt to call startActivity after release");
+        }
         if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " +
                 (isAttachedToDisplay() ? "" : "not") + " attached");
         if (mSurface != null) {
-            try {
-                mActivityContainer.startActivity(intent);
-            } catch (RemoteException e) {
-                throw new IllegalStateException("ActivityView: Unable to startActivity. " + e);
-            }
+            mActivityContainer.startActivity(intent);
         } else {
             mQueuedIntent = intent;
             mQueuedPendingIntent = null;
         }
     }
 
-    private void startActivityIntentSender(IIntentSender iIntentSender) {
-        try {
-            mActivityContainer.startActivityIntentSender(iIntentSender);
-        } catch (RemoteException e) {
-            throw new IllegalStateException(
-                    "ActivityView: Unable to startActivity from IntentSender. " + e);
-        }
-    }
-
     public void startActivity(IntentSender intentSender) {
+        if (mActivityContainer == null) {
+            throw new IllegalStateException("Attempt to call startActivity after release");
+        }
         if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " +
                 (isAttachedToDisplay() ? "" : "not") + " attached");
         final IIntentSender iIntentSender = intentSender.getTarget();
         if (mSurface != null) {
-            startActivityIntentSender(iIntentSender);
+            mActivityContainer.startActivityIntentSender(iIntentSender);
         } else {
             mQueuedPendingIntent = iIntentSender;
             mQueuedIntent = null;
@@ -195,84 +168,102 @@
     }
 
     public void startActivity(PendingIntent pendingIntent) {
+        if (mActivityContainer == null) {
+            throw new IllegalStateException("Attempt to call startActivity after release");
+        }
         if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " "
                 + (isAttachedToDisplay() ? "" : "not") + " attached");
         final IIntentSender iIntentSender = pendingIntent.getTarget();
         if (mSurface != null) {
-            startActivityIntentSender(iIntentSender);
+            mActivityContainer.startActivityIntentSender(iIntentSender);
         } else {
             mQueuedPendingIntent = iIntentSender;
             mQueuedIntent = null;
         }
     }
 
+    public void release() {
+        if (DEBUG) Log.v(TAG, "release() mActivityContainer=" + mActivityContainer +
+                " mSurface=" + mSurface);
+        if (mActivityContainer == null) {
+            Log.e(TAG, "Duplicate call to release");
+            return;
+        }
+        mActivityContainer.release();
+        mActivityContainer = null;
+
+        if (mSurface != null) {
+            mSurface.release();
+            mSurface = null;
+        }
+
+        mTextureView.setSurfaceTextureListener(null);
+    }
+
     private void attachToSurfaceWhenReady() {
         final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
-        if (mActivityContainer == null || surfaceTexture == null || mSurface != null) {
+        if (surfaceTexture == null || mSurface != null) {
             // Either not ready to attach, or already attached.
             return;
         }
 
-        WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
-        DisplayMetrics metrics = new DisplayMetrics();
-        wm.getDefaultDisplay().getMetrics(metrics);
-
         mSurface = new Surface(surfaceTexture);
         try {
-            mActivityContainer.attachToSurface(mSurface, mWidth, mHeight, metrics.densityDpi);
+            mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
         } catch (RemoteException e) {
             mSurface.release();
             mSurface = null;
-            throw new IllegalStateException(
-                    "ActivityView: Unable to create ActivityContainer. " + e);
+            throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e);
         }
 
         if (DEBUG) Log.v(TAG, "attachToSurfaceWhenReady: " + (mQueuedIntent != null ||
                 mQueuedPendingIntent != null ? "" : "no") + " queued intent");
         if (mQueuedIntent != null) {
-            startActivity(mQueuedIntent);
+            mActivityContainer.startActivity(mQueuedIntent);
             mQueuedIntent = null;
         } else if (mQueuedPendingIntent != null) {
-            startActivityIntentSender(mQueuedPendingIntent);
+            mActivityContainer.startActivityIntentSender(mQueuedPendingIntent);
             mQueuedPendingIntent = null;
         }
     }
 
-    private void detach() {
-        if (DEBUG) Log.d(TAG, "detach: attached=" + isAttachedToDisplay());
-        if (mSurface != null) {
-            try {
-                mActivityContainer.detachFromDisplay();
-            } catch (RemoteException e) {
-            }
-            mSurface.release();
-            mSurface = null;
-        }
-    }
-
     private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
         @Override
         public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
                 int height) {
+            if (mActivityContainer == null) {
+                return;
+            }
             if (DEBUG) Log.d(TAG, "onSurfaceTextureAvailable: width=" + width + " height="
                     + height);
             mWidth = width;
             mHeight = height;
-            if (mActivityContainer != null) {
-                attachToSurfaceWhenReady();
-            }
+            attachToSurfaceWhenReady();
         }
 
         @Override
         public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width,
                 int height) {
+            if (mActivityContainer == null) {
+                return;
+            }
             if (DEBUG) Log.d(TAG, "onSurfaceTextureSizeChanged: w=" + width + " h=" + height);
         }
 
         @Override
         public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+            if (mActivityContainer == null) {
+                return true;
+            }
             if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
-            detach();
+            mSurface.release();
+            mSurface = null;
+            try {
+                mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
+            } catch (RemoteException e) {
+                throw new RuntimeException(
+                        "ActivityView: Unable to set surface of ActivityContainer. " + e);
+            }
             return true;
         }
 
@@ -283,13 +274,95 @@
 
     }
 
-    private class ActivityContainerCallback extends IActivityContainerCallback.Stub {
+    private static class ActivityContainerCallback extends IActivityContainerCallback.Stub {
+        private final WeakReference<ActivityView> mActivityViewWeakReference;
+
+        ActivityContainerCallback(ActivityView activityView) {
+            mActivityViewWeakReference = new WeakReference<ActivityView>(activityView);
+        }
+
         @Override
         public void setVisible(IBinder container, boolean visible) {
-            if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible);
-            if (visible) {
-            } else {
+            if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible +
+                    " ActivityView=" + mActivityViewWeakReference.get());
+        }
+    }
+
+    private static class ActivityContainerWrapper {
+        private final IActivityContainer mIActivityContainer;
+        private final CloseGuard mGuard = CloseGuard.get();
+
+        ActivityContainerWrapper(IActivityContainer container) {
+            mIActivityContainer = container;
+            mGuard.open("release");
+        }
+
+        void attachToDisplay(int displayId) {
+            try {
+                mIActivityContainer.attachToDisplay(displayId);
+            } catch (RemoteException e) {
             }
         }
+
+        void setSurface(Surface surface, int width, int height, int density)
+                throws RemoteException {
+            mIActivityContainer.setSurface(surface, width, height, density);
+        }
+
+        int startActivity(Intent intent) {
+            try {
+                return mIActivityContainer.startActivity(intent);
+            } catch (RemoteException e) {
+                throw new RuntimeException("ActivityView: Unable to startActivity. " + e);
+            }
+        }
+
+        int startActivityIntentSender(IIntentSender intentSender) {
+            try {
+                return mIActivityContainer.startActivityIntentSender(intentSender);
+            } catch (RemoteException e) {
+                throw new RuntimeException(
+                        "ActivityView: Unable to startActivity from IntentSender. " + e);
+            }
+        }
+
+        int getDisplayId() {
+            try {
+                return mIActivityContainer.getDisplayId();
+            } catch (RemoteException e) {
+                return -1;
+            }
+        }
+
+        boolean injectEvent(InputEvent event) {
+            try {
+                return mIActivityContainer.injectEvent(event);
+            } catch (RemoteException e) {
+                return false;
+            }
+        }
+
+        void release() {
+            if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called");
+            try {
+                mIActivityContainer.release();
+                mGuard.close();
+            } catch (RemoteException e) {
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: finalize called");
+            try {
+                if (mGuard != null) {
+                    mGuard.warnIfOpen();
+                    release();
+                }
+            } finally {
+                super.finalize();
+            }
+        }
+
     }
 }
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index 5b80e06..cc3b10c 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -26,10 +26,10 @@
 /** @hide */
 interface IActivityContainer {
     void attachToDisplay(int displayId);
-    void attachToSurface(in Surface surface, int width, int height, int density);
-    void detachFromDisplay();
+    void setSurface(in Surface surface, int width, int height, int density);
     int startActivity(in Intent intent);
     int startActivityIntentSender(in IIntentSender intentSender);
     int getDisplayId();
     boolean injectEvent(in InputEvent event);
+    void release();
 }
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
new file mode 100644
index 0000000..a72d9a0
--- /dev/null
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.ScoredNetwork;
+
+/**
+ * A service for updating network scores from a network scorer application.
+ * @hide
+ */
+interface INetworkScoreService
+{
+    /**
+     * Update scores.
+     * @return whether the update was successful.
+     * @throws SecurityException if the caller is not the current active scorer.
+     */
+    boolean updateScores(in ScoredNetwork[] networks);
+
+    /**
+     * Clear all scores.
+     * @return whether the clear was successful.
+     * @throws SecurityException if the caller is neither the current active scorer nor the scorer
+     * manager.
+     */
+    boolean clearScores();
+
+    /**
+     * Set the active scorer and clear existing scores.
+     * @param packageName the package name of the new scorer to use.
+     * @return true if the operation succeeded, or false if the new package is not a valid scorer.
+     * @throws SecurityException if the caller is not the scorer manager.
+     */
+    boolean setActiveScorer(in String packageName);
+}
diff --git a/core/java/android/net/NetworkKey.java b/core/java/android/net/NetworkKey.java
index cc3ad3e..bc19658 100644
--- a/core/java/android/net/NetworkKey.java
+++ b/core/java/android/net/NetworkKey.java
@@ -19,11 +19,19 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Information which identifies a specific network.
  *
  * @hide
  */
+// NOTE: Ideally, we would abstract away the details of what identifies a network of a specific
+// type, so that all networks appear the same and can be scored without concern to the network type
+// itself. However, because no such cross-type identifier currently exists in the Android framework,
+// and because systems might obtain information about networks from sources other than Android
+// devices, we need to provide identifying details about each specific network type (wifi, cell,
+// etc.) so that clients can pull out these details depending on the type of network.
 public class NetworkKey implements Parcelable {
 
     /** A wifi network, for which {@link #wifiKey} will be populated. */
@@ -79,6 +87,21 @@
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        NetworkKey that = (NetworkKey) o;
+
+        return type == that.type && Objects.equals(wifiKey, that.wifiKey);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, wifiKey);
+    }
+
+    @Override
     public String toString() {
         switch (type) {
             case TYPE_WIFI:
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 3430547..5e61613 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -19,6 +19,9 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 
 /**
  * Class that manages communication between network subsystems and a network scorer.
@@ -40,7 +43,7 @@
  * <p>The system keeps track of a default scorer application; at any time, only this application
  * will receive {@link #ACTION_SCORE_NETWORKS} broadcasts and will be permitted to call
  * {@link #updateScores}. Applications may determine the current default scorer with
- * {@link #getDefaultScorerPackage()} and request to change the default scorer by sending an
+ * {@link #getActiveScorerPackage()} and request to change the default scorer by sending an
  * {@link #ACTION_CHANGE_DEFAULT} broadcast with another scorer.
  *
  * @hide
@@ -81,38 +84,82 @@
     public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
 
     private final Context mContext;
+    private final INetworkScoreService mService;
 
     /** @hide */
     public NetworkScoreManager(Context context) {
         mContext = context;
+        IBinder iBinder = ServiceManager.getService(Context.NETWORK_SCORE_SERVICE);
+        mService = INetworkScoreService.Stub.asInterface(iBinder);
     }
 
     /**
-     * Obtain the package name of the current default network scorer.
+     * Obtain the package name of the current active network scorer.
      *
-     * At any time, only one scorer application will receive {@link #ACTION_SCORE_NETWORKS}
+     * <p>At any time, only one scorer application will receive {@link #ACTION_SCORE_NETWORKS}
      * broadcasts and be allowed to call {@link #updateScores}. Applications may use this method to
      * determine the current scorer and offer the user the ability to select a different scorer via
      * the {@link #ACTION_CHANGE_DEFAULT} intent.
-     * @return the full package name of the current default scorer, or null if there is no active
+     * @return the full package name of the current active scorer, or null if there is no active
      *     scorer.
      */
-    public String getDefaultScorerPackage() {
-        // TODO: Implement.
-        return null;
+    public String getActiveScorerPackage() {
+        return NetworkScorerAppManager.getActiveScorer(mContext);
     }
 
     /**
      * Update network scores.
      *
-     * This may be called at any time to re-score active networks. Scores will generally be updated
-     * quickly, but if this method is called too frequently, the scores may be held and applied at
-     * a later time.
+     * <p>This may be called at any time to re-score active networks. Scores will generally be
+     * updated quickly, but if this method is called too frequently, the scores may be held and
+     * applied at a later time.
      *
      * @param networks the networks which have been scored by the scorer.
-     * @throws SecurityException if the caller is not the default scorer.
+     * @return whether the update was successful.
+     * @throws SecurityException if the caller is not the active scorer.
      */
-    public void updateScores(ScoredNetwork[] networks) throws SecurityException {
-        // TODO: Implement.
+    public boolean updateScores(ScoredNetwork[] networks) throws SecurityException {
+        try {
+            return mService.updateScores(networks);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Clear network scores.
+     *
+     * <p>Should be called when all scores need to be invalidated, i.e. because the scoring
+     * algorithm has changed and old scores can no longer be compared to future scores.
+     *
+     * <p>Note that scores will be cleared automatically when the active scorer changes, as scores
+     * from one scorer cannot be compared to those from another scorer.
+     *
+     * @return whether the clear was successful.
+     * @throws SecurityException if the caller is not the active scorer or privileged.
+     */
+    public boolean clearScores() throws SecurityException {
+        try {
+            return mService.clearScores();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Set the active scorer to a new package and clear existing scores.
+     *
+     * @return true if the operation succeeded, or false if the new package is not a valid scorer.
+     * @throws SecurityException if the caller does not hold the
+     *      {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating that
+     *      it can manage scorer applications.
+     * @hide
+     */
+    public boolean setActiveScorer(String packageName) throws SecurityException {
+        try {
+            return mService.setActiveScorer(packageName);
+        } catch (RemoteException e) {
+            return false;
+        }
     }
 }
diff --git a/core/java/android/net/NetworkScorerApplication.java b/core/java/android/net/NetworkScorerAppManager.java
similarity index 88%
rename from core/java/android/net/NetworkScorerApplication.java
rename to core/java/android/net/NetworkScorerAppManager.java
index b137ad3..726208a 100644
--- a/core/java/android/net/NetworkScorerApplication.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -26,6 +26,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -36,13 +37,14 @@
  *
  * @hide
  */
-public final class NetworkScorerApplication {
+public final class NetworkScorerAppManager {
+    private static final String TAG = "NetworkScorerAppManager";
 
     private static final Intent SCORE_INTENT =
             new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
 
     /** This class cannot be instantiated. */
-    private NetworkScorerApplication() {}
+    private NetworkScorerAppManager() {}
 
     /**
      * Returns the list of available scorer app package names.
@@ -111,30 +113,38 @@
      * @param context the context of the calling application
      * @param packageName the packageName of the new scorer to use. If null, scoring will be
      *     disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
+     * @return true if the scorer was changed, or false if the package is not a valid scorer.
      */
-    public static void setActiveScorer(Context context, String packageName) {
+    public static boolean setActiveScorer(Context context, String packageName) {
         String oldPackageName = Settings.Global.getString(context.getContentResolver(),
                 Settings.Global.NETWORK_SCORER_APP);
         if (TextUtils.equals(oldPackageName, packageName)) {
             // No change.
-            return;
+            return true;
         }
 
+        Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
+
         if (packageName == null) {
             Settings.Global.putString(context.getContentResolver(), Global.NETWORK_SCORER_APP,
                     null);
+            return true;
         } else {
             // We only make the change if the new package is valid.
             Collection<String> applications = getAllValidScorers(context);
             if (isPackageValidScorer(applications, packageName)) {
                 Settings.Global.putString(context.getContentResolver(),
                         Settings.Global.NETWORK_SCORER_APP, packageName);
+                return true;
+            } else {
+                Log.w(TAG, "Requested network scorer is not valid: " + packageName);
+                return false;
             }
         }
     }
 
     /** Determine whether the application with the given UID is the enabled scorer. */
-    public static boolean isCallerDefaultScorer(Context context, int callingUid) {
+    public static boolean isCallerActiveScorer(Context context, int callingUid) {
         String defaultApp = getActiveScorer(context);
         if (defaultApp == null) {
             return false;
diff --git a/core/java/android/net/RssiCurve.java b/core/java/android/net/RssiCurve.java
index 7af7998..33e81c2 100644
--- a/core/java/android/net/RssiCurve.java
+++ b/core/java/android/net/RssiCurve.java
@@ -19,6 +19,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * A curve defining the network score over a range of RSSI values.
  *
@@ -94,6 +97,30 @@
         out.writeByteArray(rssiBuckets);
     }
 
+    /**
+     * Determine if two RSSI curves are defined in the same way.
+     *
+     * <p>Note that two curves can be equivalent but defined differently, e.g. if one bucket in one
+     * curve is split into two buckets in another. For the purpose of this method, these curves are
+     * not considered equal to each other.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        RssiCurve rssiCurve = (RssiCurve) o;
+
+        return start == rssiCurve.start &&
+                bucketWidth == rssiCurve.bucketWidth &&
+                Arrays.equals(rssiBuckets, rssiCurve.rssiBuckets);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(start, bucketWidth, rssiBuckets);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 8af3c3c..7902313 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * A network identifier along with a score for the quality of that network.
  *
@@ -80,6 +82,22 @@
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ScoredNetwork that = (ScoredNetwork) o;
+
+        return Objects.equals(networkKey, that.networkKey) &&
+                Objects.equals(rssiCurve, that.rssiCurve);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(networkKey, rssiCurve);
+    }
+
+    @Override
     public String toString() {
         return "ScoredNetwork[key=" + networkKey + ",score=" + rssiCurve + "]";
     }
diff --git a/core/java/android/net/WifiKey.java b/core/java/android/net/WifiKey.java
index ffcd85a..9e92e89 100644
--- a/core/java/android/net/WifiKey.java
+++ b/core/java/android/net/WifiKey.java
@@ -19,6 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
 import java.util.regex.Pattern;
 
 /**
@@ -87,6 +88,21 @@
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        WifiKey wifiKey = (WifiKey) o;
+
+        return Objects.equals(ssid, wifiKey.ssid) && Objects.equals(bssid, wifiKey.bssid);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(ssid, bssid);
+    }
+
+    @Override
     public String toString() {
         return "WifiKey[SSID=" + ssid + ",BSSID=" + bssid + "]";
     }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 426f21e..e640649 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2861,29 +2861,21 @@
         int oldTemp = -1;
         int oldVolt = -1;
         long lastTime = -1;
+        long firstTime = -1;
 
-        public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin,
+        public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
                 boolean verbose) {
             if (!checkin) {
                 pw.print("  ");
-                if (now >= 0) {
-                    TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
-                } else {
-                    TimeUtils.formatDuration(rec.time, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
-                }
+                TimeUtils.formatDuration(rec.time - baseTime, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
                 pw.print(" (");
                 pw.print(rec.numReadInts);
                 pw.print(") ");
             } else {
                 if (lastTime < 0) {
-                    if (now >= 0) {
-                        pw.print("@");
-                        pw.print(rec.time-now);
-                    } else {
-                        pw.print(rec.time);
-                    }
+                    pw.print(rec.time - baseTime);
                 } else {
-                    pw.print(rec.time-lastTime);
+                    pw.print(rec.time - lastTime);
                 }
                 lastTime = rec.time;
             }
@@ -3132,10 +3124,27 @@
                     pw.println("):");
                     HistoryPrinter hprinter = new HistoryPrinter();
                     long lastTime = -1;
+                    long baseTime = -1;
+                    boolean printed = false;
                     while (getNextHistoryLocked(rec)) {
                         lastTime = rec.time;
+                        if (baseTime < 0) {
+                            baseTime = lastTime;
+                        }
                         if (rec.time >= histStart) {
-                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false,
+                            if (histStart >= 0 && !printed) {
+                                if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+                                    printed = true;
+                                } else if (rec.currentTime != 0) {
+                                    printed = true;
+                                    byte cmd = rec.cmd;
+                                    rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+                                    hprinter.printNextItem(pw, rec, baseTime, false,
+                                            (flags&DUMP_VERBOSE) != 0);
+                                    rec.cmd = cmd;
+                                }
+                            }
+                            hprinter.printNextItem(pw, rec, baseTime, false,
                                     (flags&DUMP_VERBOSE) != 0);
                         }
                     }
@@ -3152,8 +3161,12 @@
                 try {
                     pw.println("Old battery History:");
                     HistoryPrinter hprinter = new HistoryPrinter();
+                    long baseTime = -1;
                     while (getNextOldHistoryLocked(rec)) {
-                        hprinter.printNextItem(pw, rec, now, false, (flags&DUMP_VERBOSE) != 0);
+                        if (baseTime < 0) {
+                            baseTime = rec.time;
+                        }
+                        hprinter.printNextItem(pw, rec, baseTime, false, (flags&DUMP_VERBOSE) != 0);
                     }
                     pw.println();
                 } finally {
@@ -3226,20 +3239,42 @@
                         pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
                         pw.print(HISTORY_STRING_POOL); pw.print(',');
                         pw.print(i);
-                        pw.print(',');
-                        pw.print(getHistoryTagPoolString(i));
-                        pw.print(',');
+                        pw.print(",");
                         pw.print(getHistoryTagPoolUid(i));
+                        pw.print(",\"");
+                        String str = getHistoryTagPoolString(i);
+                        str = str.replace("\\", "\\\\");
+                        str = str.replace("\"", "\\\"");
+                        pw.print(str);
+                        pw.print("\"");
                         pw.println();
                     }
                     HistoryPrinter hprinter = new HistoryPrinter();
                     long lastTime = -1;
+                    long baseTime = -1;
+                    boolean printed = false;
                     while (getNextHistoryLocked(rec)) {
                         lastTime = rec.time;
+                        if (baseTime < 0) {
+                            baseTime = lastTime;
+                        }
                         if (rec.time >= histStart) {
+                            if (histStart >= 0 && !printed) {
+                                if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+                                    printed = true;
+                                } else if (rec.currentTime != 0) {
+                                    printed = true;
+                                    byte cmd = rec.cmd;
+                                    rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+                                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                                    pw.print(HISTORY_DATA); pw.print(',');
+                                    hprinter.printNextItem(pw, rec, baseTime, true, false);
+                                    rec.cmd = cmd;
+                                }
+                            }
                             pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
                             pw.print(HISTORY_DATA); pw.print(',');
-                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true, false);
+                            hprinter.printNextItem(pw, rec, baseTime, true, false);
                         }
                     }
                     if (histStart >= 0) {
diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl
index a927dc9..a4c99e4 100644
--- a/core/java/android/tv/ITvInputManager.aidl
+++ b/core/java/android/tv/ITvInputManager.aidl
@@ -17,6 +17,7 @@
 package android.tv;
 
 import android.content.ComponentName;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.tv.ITvInputClient;
 import android.tv.TvInputInfo;
@@ -40,4 +41,9 @@
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
     void setVolume(in IBinder sessionToken, float volume, int userId);
     void tune(in IBinder sessionToken, in Uri channelUri, int userId);
+
+    void createOverlayView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
+            int userId);
+    void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
+    void removeOverlayView(in IBinder sessionToken, int userId);
 }
diff --git a/core/java/android/tv/ITvInputService.aidl b/core/java/android/tv/ITvInputService.aidl
index d80f286..672784f 100644
--- a/core/java/android/tv/ITvInputService.aidl
+++ b/core/java/android/tv/ITvInputService.aidl
@@ -17,7 +17,6 @@
 package android.tv;
 
 import android.tv.ITvInputServiceCallback;
-import android.tv.ITvInputSession;
 import android.tv.ITvInputSessionCallback;
 
 /**
diff --git a/core/java/android/tv/ITvInputSession.aidl b/core/java/android/tv/ITvInputSession.aidl
index d379d2d..32fee4b 100644
--- a/core/java/android/tv/ITvInputSession.aidl
+++ b/core/java/android/tv/ITvInputSession.aidl
@@ -16,6 +16,7 @@
 
 package android.tv;
 
+import android.graphics.Rect;
 import android.net.Uri;
 import android.view.Surface;
 
@@ -31,4 +32,8 @@
     // is to introduce some new concepts that will solve a number of problems in audio policy today.
     void setVolume(float volume);
     void tune(in Uri channelUri);
+
+    void createOverlayView(in IBinder windowToken, in Rect frame);
+    void relayoutOverlayView(in Rect frame);
+    void removeOverlayView();
 }
diff --git a/core/java/android/tv/ITvInputSessionWrapper.java b/core/java/android/tv/ITvInputSessionWrapper.java
index 66fe5e1..a6e0877 100644
--- a/core/java/android/tv/ITvInputSessionWrapper.java
+++ b/core/java/android/tv/ITvInputSessionWrapper.java
@@ -17,13 +17,16 @@
 package android.tv;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.net.Uri;
+import android.os.IBinder;
 import android.os.Message;
 import android.tv.TvInputService.TvInputSessionImpl;
 import android.util.Log;
 import android.view.Surface;
 
 import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
 
 /**
  * Implements the internal ITvInputSession interface to convert incoming calls on to it back to
@@ -38,6 +41,9 @@
     private static final int DO_SET_SURFACE = 2;
     private static final int DO_SET_VOLUME = 3;
     private static final int DO_TUNE = 4;
+    private static final int DO_CREATE_OVERLAY_VIEW = 5;
+    private static final int DO_RELAYOUT_OVERLAY_VIEW = 6;
+    private static final int DO_REMOVE_OVERLAY_VIEW = 7;
 
     private TvInputSessionImpl mTvInputSession;
     private final HandlerCaller mCaller;
@@ -71,6 +77,20 @@
                 mTvInputSession.tune((Uri) msg.obj);
                 return;
             }
+            case DO_CREATE_OVERLAY_VIEW: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mTvInputSession.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
+                args.recycle();
+                return;
+            }
+            case DO_RELAYOUT_OVERLAY_VIEW: {
+                mTvInputSession.relayoutOverlayView((Rect) msg.obj);
+                return;
+            }
+            case DO_REMOVE_OVERLAY_VIEW: {
+                mTvInputSession.removeOverlayView(true);
+                return;
+            }
             default: {
                 Log.w(TAG, "Unhandled message code: " + msg.what);
                 return;
@@ -97,4 +117,20 @@
     public void tune(Uri channelUri) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TUNE, channelUri));
     }
+
+    @Override
+    public void createOverlayView(IBinder windowToken, Rect frame) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_OVERLAY_VIEW, windowToken,
+                frame));
+    }
+
+    @Override
+    public void relayoutOverlayView(Rect frame) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_OVERLAY_VIEW, frame));
+    }
+
+    @Override
+    public void removeOverlayView() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW));
+    }
 }
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index 4cf2b35..05f0b9c 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -17,6 +17,7 @@
 package android.tv;
 
 import android.content.ComponentName;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -24,6 +25,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Surface;
+import android.view.View;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -320,8 +322,8 @@
     /** The Session provides the per-session functionality of TV inputs. */
     public static final class Session {
         private final ITvInputManager mService;
-        private final IBinder mToken;
         private final int mUserId;
+        private IBinder mToken;
 
         /** @hide */
         private Session(ComponentName name, IBinder token, ITvInputManager service, int userId) {
@@ -332,10 +334,16 @@
 
         /**
          * Releases this session.
+         *
+         * @throws IllegalStateException if the session has been already released.
          */
         public void release() {
+            if (mToken == null) {
+                throw new IllegalStateException("the session has been already released");
+            }
             try {
                 mService.releaseSession(mToken, mUserId);
+                mToken = null;
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -345,8 +353,12 @@
          * Sets the {@link android.view.Surface} for this session.
          *
          * @param surface A {@link android.view.Surface} used to render video.
+         * @throws IllegalStateException if the session has been already released.
          */
-        public void setSurface(Surface surface) {
+        void setSurface(Surface surface) {
+            if (mToken == null) {
+                throw new IllegalStateException("the session has been already released");
+            }
             // surface can be null.
             try {
                 mService.setSurface(mToken, surface, mUserId);
@@ -360,8 +372,12 @@
          *
          * @param volume A volume value between 0.0f to 1.0f.
          * @throws IllegalArgumentException if the volume value is out of range.
+         * @throws IllegalStateException if the session has been already released.
          */
         public void setVolume(float volume) {
+            if (mToken == null) {
+                throw new IllegalStateException("the session has been already released");
+            }
             try {
                 if (volume < 0.0f || volume > 1.0f) {
                     throw new IllegalArgumentException("volume should be between 0.0f and 1.0f");
@@ -377,16 +393,90 @@
          *
          * @param channelUri The URI of a channel.
          * @throws IllegalArgumentException if the argument is {@code null}.
+         * @throws IllegalStateException if the session has been already released.
          */
         public void tune(Uri channelUri) {
             if (channelUri == null) {
                 throw new IllegalArgumentException("channelUri cannot be null");
             }
+            if (mToken == null) {
+                throw new IllegalStateException("the session has been already released");
+            }
             try {
                 mService.tune(mToken, channelUri, mUserId);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
         }
+
+        /**
+         * Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
+         * should be called whenever the layout of its containing view is changed.
+         * {@link #removeOverlayView()} should be called to remove the overlay view.
+         * Since a session can have only one overlay view, this method should be called only once
+         * or it can be called again after calling {@link #removeOverlayView()}.
+         *
+         * @param view A view playing TV.
+         * @param frame A position of the overlay view.
+         * @throws IllegalArgumentException if any of the arguments is {@code null}.
+         * @throws IllegalStateException if {@code view} is not attached to a window or
+         *         if the session has been already released.
+         */
+        void createOverlayView(View view, Rect frame) {
+            if (view == null) {
+                throw new IllegalArgumentException("view cannot be null");
+            }
+            if (frame == null) {
+                throw new IllegalArgumentException("frame cannot be null");
+            }
+            if (view.getWindowToken() == null) {
+                throw new IllegalStateException("view must be attached to a window");
+            }
+            if (mToken == null) {
+                throw new IllegalStateException("the session has been already released");
+            }
+            try {
+                mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
+         * Relayouts the current overlay view.
+         *
+         * @param frame A new position of the overlay view.
+         * @throws IllegalArgumentException if the arguments is {@code null}.
+         * @throws IllegalStateException if the session has been already released.
+         */
+        void relayoutOverlayView(Rect frame) {
+            if (frame == null) {
+                throw new IllegalArgumentException("frame cannot be null");
+            }
+            if (mToken == null) {
+                throw new IllegalStateException("the session has been already released");
+            }
+            try {
+                mService.relayoutOverlayView(mToken, frame, mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
+         * Removes the current overlay view.
+         *
+         * @throws IllegalStateException if the session has been already released.
+         */
+        void removeOverlayView() {
+            if (mToken == null) {
+                throw new IllegalStateException("the session has been already released");
+            }
+            try {
+                mService.removeOverlayView(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 }
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index d7f6c32..80eb407 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -18,7 +18,10 @@
 
 import android.app.Service;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -26,7 +29,10 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -125,7 +131,37 @@
     /**
      * Base class for derived classes to implement to provide {@link TvInputManager.Session}.
      */
-    public abstract static class TvInputSessionImpl {
+    public abstract class TvInputSessionImpl {
+        private final WindowManager mWindowManager;
+        private WindowManager.LayoutParams mWindowParams;
+        private View mOverlayView;
+        private boolean mOverlayViewEnabled;
+        private IBinder mWindowToken;
+        private Rect mOverlayFrame;
+
+        public TvInputSessionImpl() {
+            mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+        }
+
+        public void setOverlayViewEnabled(final boolean enable) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (enable == mOverlayViewEnabled) {
+                        return;
+                    }
+                    mOverlayViewEnabled = enable;
+                    if (enable) {
+                        if (mWindowToken != null) {
+                            createOverlayView(mWindowToken, mOverlayFrame);
+                        }
+                    } else {
+                        removeOverlayView(false);
+                    }
+                }
+            });
+        }
+
         /**
          * Called when the session is released.
          */
@@ -157,11 +193,22 @@
         public abstract boolean onTune(Uri channelUri);
 
         /**
+         * Called when an application requests to create an overlay view. Each session
+         * implementation can override this method and return its own view.
+         *
+         * @return a view attached to the overlay window
+         */
+        public View onCreateOverlayView() {
+            return null;
+        }
+
+        /**
          * This method is called when the application would like to stop using the current input
          * session.
          */
         void release() {
             onRelease();
+            removeOverlayView(true);
         }
 
         /**
@@ -186,6 +233,87 @@
             onTune(channelUri);
             // TODO: Handle failure.
         }
+
+        /**
+         * Creates an overlay view. This calls {@link onCreateOverlayView} to get
+         * a view to attach to the overlay window.
+         *
+         * @param windowToken A window token of an application.
+         * @param frame A position of the overlay view.
+         */
+        void createOverlayView(IBinder windowToken, Rect frame) {
+            if (mOverlayView != null) {
+                mWindowManager.removeView(mOverlayView);
+                mOverlayView = null;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "create overlay view(" + frame + ")");
+            }
+            mWindowToken = windowToken;
+            mOverlayFrame = frame;
+            if (!mOverlayViewEnabled) {
+                return;
+            }
+            mOverlayView = onCreateOverlayView();
+            if (mOverlayView == null) {
+                return;
+            }
+            // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create
+            // an overlay window above the media window but below the application window.
+            int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+            // We make the overlay view non-focusable and non-touchable so that
+            // the application that owns the window token can decide whether to consume or
+            // dispatch the input events.
+            int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+            mWindowParams = new WindowManager.LayoutParams(
+                    frame.right - frame.left, frame.bottom - frame.top,
+                    frame.left, frame.top, type, flag, PixelFormat.TRANSPARENT);
+            mWindowParams.privateFlags |=
+                    WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+            mWindowParams.gravity = Gravity.START | Gravity.TOP;
+            mWindowParams.token = windowToken;
+            mWindowManager.addView(mOverlayView, mWindowParams);
+        }
+
+        /**
+         * Relayouts the current overlay view.
+         *
+         * @param frame A new position of the overlay view.
+         */
+        void relayoutOverlayView(Rect frame) {
+            if (DEBUG) {
+                Log.d(TAG, "relayout overlay view(" + frame + ")");
+            }
+            mOverlayFrame = frame;
+            if (!mOverlayViewEnabled || mOverlayView == null) {
+                return;
+            }
+            mWindowParams.x = frame.left;
+            mWindowParams.y = frame.top;
+            mWindowParams.width = frame.right - frame.left;
+            mWindowParams.height = frame.bottom - frame.top;
+            mWindowManager.updateViewLayout(mOverlayView, mWindowParams);
+        }
+
+        /**
+         * Removes the current overlay view.
+         */
+        void removeOverlayView(boolean clearWindowToken) {
+            if (DEBUG) {
+                Log.d(TAG, "remove overlay view(" + mOverlayView + ")");
+            }
+            if (clearWindowToken) {
+                mWindowToken = null;
+                mOverlayFrame = null;
+            }
+            if (mOverlayView != null) {
+                mWindowManager.removeView(mOverlayView);
+                mOverlayView = null;
+                mWindowParams = null;
+            }
+        }
     }
 
     private final class ServiceHandler extends Handler {
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
new file mode 100644
index 0000000..325950d
--- /dev/null
+++ b/core/java/android/tv/TvView.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.tv.TvInputManager;
+import android.tv.TvInputManager.Session;
+import android.tv.TvInputManager.SessionCreateCallback;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewTreeObserver;
+
+/**
+ * View playing TV
+ */
+public class TvView extends SurfaceView {
+    private static final String TAG = "TvView";
+
+    private final Handler mHandler = new Handler();
+    private TvInputManager.Session mSession;
+    private Surface mSurface;
+    private boolean mOverlayViewCreated;
+    private Rect mOverlayViewFrame;
+    private boolean mGlobalListenersAdded;
+    private TvInputManager mTvInputManager;
+    private SessionCreateCallback mSessionCreateCallback;
+
+    private SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format + ", width=" + width
+                    + ", height=" + height + ")");
+            if (holder.getSurface() == mSurface) {
+                return;
+            }
+            mSurface = holder.getSurface();
+            setSessionSurface(mSurface);
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+            mSurface = holder.getSurface();
+            setSessionSurface(mSurface);
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            mSurface = null;
+            setSessionSurface(null);
+        }
+    };
+
+    public TvView(Context context) {
+        this(context, null, 0);
+    }
+
+    public TvView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        getHolder().addCallback(mSurfaceHolderCallback);
+        mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+    }
+
+    /**
+     * Binds a TV input to this view. {@link SessionCreateCallback#onSessionCreated} will be
+     * called to send the result of this binding with {@link TvInputManager.Session}.
+     * If a TV input is already bound, the input will be unbound from this view and its session
+     * will be released.
+     *
+     * @param name TV input name will be bound to this view.
+     * @param callback called when TV input is bound. The callback sends
+     *        {@link TvInputManager.Session}
+     * @throws IllegalArgumentException if any of the arguments is {@code null}.
+     */
+    public void bindTvInput(ComponentName name, SessionCreateCallback callback) {
+        if (name == null) {
+            throw new IllegalArgumentException("name cannot be null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        if (mSession != null) {
+            release();
+        }
+        // When bindTvInput is called multiple times before the callback is called,
+        // only the callback of the last bindTvInput call will be actually called back.
+        // The previous callbacks will be ignored. For the logic, mSessionCreateCallback
+        // is newly assigned for every bindTvInput call and compared with
+        // MySessionCreateCallback.this.
+        mSessionCreateCallback = new MySessionCreateCallback(callback);
+        mTvInputManager.createSession(name, mSessionCreateCallback, mHandler);
+    }
+
+    /**
+     * Unbinds a TV input currently bound. Its corresponding {@link TvInputManager.Session}
+     * is released.
+     */
+    public void unbindTvInput() {
+        if (mSession != null) {
+            release();
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        createSessionOverlayView();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        removeSessionOverlayView();
+        super.onDetachedFromWindow();
+    }
+
+    /** @hide */
+    @Override
+    protected void updateWindow(boolean force, boolean redrawNeeded) {
+        super.updateWindow(force, redrawNeeded);
+        relayoutSessionOverlayView();
+    }
+
+    private void release() {
+        setSessionSurface(null);
+        removeSessionOverlayView();
+        mSession.release();
+        mSession = null;
+    }
+
+    private void setSessionSurface(Surface surface) {
+        if (mSession == null) {
+            return;
+        }
+        mSession.setSurface(surface);
+    }
+
+    private void createSessionOverlayView() {
+        if (mSession == null || !isAttachedToWindow()
+                || mOverlayViewCreated) {
+            return;
+        }
+        mOverlayViewFrame = getViewFrameOnScreen();
+        mSession.createOverlayView(this, mOverlayViewFrame);
+        mOverlayViewCreated = true;
+    }
+
+    private void removeSessionOverlayView() {
+        if (mSession == null || !mOverlayViewCreated) {
+            return;
+        }
+        mSession.removeOverlayView();
+        mOverlayViewCreated = false;
+        mOverlayViewFrame = null;
+    }
+
+    private void relayoutSessionOverlayView() {
+        if (mSession == null || !isAttachedToWindow()
+                || !mOverlayViewCreated) {
+            return;
+        }
+        Rect viewFrame = getViewFrameOnScreen();
+        if (viewFrame.equals(mOverlayViewFrame)) {
+            return;
+        }
+        mSession.relayoutOverlayView(viewFrame);
+        mOverlayViewFrame = viewFrame;
+    }
+
+    private Rect getViewFrameOnScreen() {
+        int[] location = new int[2];
+        getLocationOnScreen(location);
+        return new Rect(location[0], location[1],
+                location[0] + getWidth(), location[1] + getHeight());
+    }
+
+    private class MySessionCreateCallback implements SessionCreateCallback {
+        final SessionCreateCallback mExternalCallback;
+
+        MySessionCreateCallback(SessionCreateCallback externalCallback) {
+            mExternalCallback = externalCallback;
+        }
+
+        @Override
+        public void onSessionCreated(Session session) {
+            if (this != mSessionCreateCallback) {
+                // This callback is obsolete.
+                session.release();
+                return;
+            }
+            mSession = session;
+            if (session != null) {
+                // mSurface may not be ready yet as soon as starting an application.
+                // In the case, we don't send Session.setSurface(null) unnecessarily.
+                // setSessionSurface will be called in surfaceCreated.
+                if (mSurface != null) {
+                    setSessionSurface(mSurface);
+                }
+                createSessionOverlayView();
+            }
+            if (mExternalCallback != null) {
+                mExternalCallback.onSessionCreated(session);
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 23123dd..4a2cc1a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -422,7 +422,8 @@
         mWindowType = type;
     }
 
-    private void updateWindow(boolean force, boolean redrawNeeded) {
+    /** @hide */
+    protected void updateWindow(boolean force, boolean redrawNeeded) {
         if (!mHaveFrame) {
             return;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1f9ba46..be316e2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4737,13 +4737,13 @@
     private void manageFocusHotspot(boolean focused, View v) {
         if (mBackground != null && mBackground.supportsHotspots()) {
             final Rect r = new Rect();
-            if (v != null) {
+            if (!focused && v != null) {
                 v.getBoundsOnScreen(r);
                 final int[] location = new int[2];
                 getLocationOnScreen(location);
                 r.offset(-location[0], -location[1]);
             } else {
-                r.set(mLeft, mTop, mRight, mBottom);
+                r.set(0, 0, mRight - mLeft, mBottom - mTop);
             }
 
             final float x = r.exactCenterX();
@@ -4858,16 +4858,13 @@
         if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
             mPrivateFlags &= ~PFLAG_FOCUSED;
 
-            if (hasFocus()) {
-                manageFocusHotspot(false, focused);
-            }
-
             if (propagate && mParent != null) {
                 mParent.clearChildFocus(this);
             }
 
             onFocusChanged(false, 0, null);
 
+            manageFocusHotspot(false, focused);
             refreshDrawableState();
 
             if (propagate && (!refocus || !rootViewRequestFocus())) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4e4f37b..097dff0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3722,7 +3722,8 @@
                     if (result == InputMethodManager.DISPATCH_HANDLED) {
                         return FINISH_HANDLED;
                     } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
-                        return FINISH_NOT_HANDLED;
+                        // The IME could not handle it, so skip along to the next InputStage
+                        return FORWARD;
                     } else {
                         return DEFER; // callback will be invoked later
                     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4c11fa9..eaedba5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5580,13 +5580,13 @@
             if (end) {
                 Slog.w(TAG, "New history ends before old history!");
             } else if (!out.same(mHistoryReadTmp)) {
-                long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
                 PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
                 pw.println("Histories differ!");
                 pw.println("Old history:");
-                (new HistoryPrinter()).printNextItem(pw, out, now, false, true);
+                (new HistoryPrinter()).printNextItem(pw, out, 0, false, true);
                 pw.println("New history:");
-                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false, true);
+                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, 0, false,
+                        true);
                 pw.flush();
             }
         }
@@ -5664,7 +5664,12 @@
             return false;
         }
 
+        final long lastRealtime = out.time;
+        final long lastWalltime = out.currentTime;
         readHistoryDelta(mHistoryBuffer, out);
+        if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) {
+            out.currentTime = lastWalltime + (out.time - lastRealtime);
+        }
         return true;
     }
 
diff --git a/core/res/res/drawable/edit_text_quantum.xml b/core/res/res/drawable/edit_text_quantum.xml
index d1f9831..c42c7b7 100644
--- a/core/res/res/drawable/edit_text_quantum.xml
+++ b/core/res/res/drawable/edit_text_quantum.xml
@@ -14,29 +14,26 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_window_focused="false" android:state_enabled="true">
-        <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-    <item android:state_window_focused="false" android:state_enabled="false">
-        <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-    <item android:state_enabled="true" android:state_focused="true">
-        <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
-    <item android:state_enabled="true" android:state_activated="true">
-        <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
-    <item android:state_enabled="true">
-        <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="?attr/colorControlActivated">
     <item>
-        <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
+        <selector>
+            <item android:state_window_focused="false">
+                <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+                    android:tint="?attr/colorControlNormal" />
+            </item>
+            <item android:state_enabled="false">
+                <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+                    android:tint="?attr/colorControlNormal" />
+            </item>
+            <item android:state_focused="false" android:state_activated="false">
+                <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+                    android:tint="?attr/colorControlNormal" />
+            </item>
+            <item>
+                <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
+                    android:tint="?attr/colorControlNormal" />
+            </item>
+        </selector>
     </item>
-</selector>
+</touch-feedback>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2df5dc1..c610146 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1432,4 +1432,6 @@
     <!-- default window inset isRound property -->
     <bool name="config_windowIsRound">false</bool>
 
+    <!-- Package name for default network scorer app; overridden by product overlays. -->
+    <string name="config_defaultNetworkScorerPackageName"></string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 03c617a..26efe36 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1625,6 +1625,7 @@
   <java-symbol type="bool" name="config_powerDecoupleAutoSuspendModeFromDisplay" />
   <java-symbol type="bool" name="config_powerDecoupleInteractiveModeFromDisplay" />
   <java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
+  <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
index 60b6dc1..cbfa84d 100644
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -31,5 +31,8 @@
     <instrumentation android:name="android.bluetooth.BluetoothTestRunner"
             android:targetPackage="com.android.bluetooth.tests"
             android:label="Bluetooth Tests" />
+    <instrumentation android:name="android.bluetooth.BluetoothInstrumentation"
+            android:targetPackage="com.android.bluetooth.tests"
+            android:label="Bluetooth Test Utils" />
 
 </manifest>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
new file mode 100644
index 0000000..34393f9
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Bundle;
+
+public class BluetoothInstrumentation extends Instrumentation {
+
+    private BluetoothTestUtils mUtils = null;
+    private BluetoothAdapter mAdapter = null;
+    private Bundle mArgs = null;
+
+    private BluetoothTestUtils getBluetoothTestUtils() {
+        if (mUtils == null) {
+            mUtils = new BluetoothTestUtils(getContext(),
+                    BluetoothInstrumentation.class.getSimpleName());
+        }
+        return mUtils;
+    }
+
+    private BluetoothAdapter getBluetoothAdapter() {
+        if (mAdapter == null) {
+            mAdapter = ((BluetoothManager)getContext().getSystemService(
+                    Context.BLUETOOTH_SERVICE)).getAdapter();
+        }
+        return mAdapter;
+    }
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        super.onCreate(arguments);
+        mArgs = arguments;
+        start();
+    }
+
+    @Override
+    public void onStart() {
+        String command = mArgs.getString("command");
+        if ("enable".equals(command)) {
+            enable();
+        } else if ("disable".equals(command)) {
+            disable();
+        } else if ("unpairAll".equals(command)) {
+            unpairAll();
+        } else if ("getName".equals(command)) {
+            getName();
+        } else {
+            finish(null);
+        }
+    }
+
+    public void enable() {
+        getBluetoothTestUtils().enable(getBluetoothAdapter());
+        finish(null);
+    }
+
+    public void disable() {
+        getBluetoothTestUtils().disable(getBluetoothAdapter());
+        finish(null);
+    }
+
+    public void unpairAll() {
+        getBluetoothTestUtils().unpairAll(getBluetoothAdapter());
+        finish(null);
+    }
+
+    public void getName() {
+        String name = getBluetoothAdapter().getName();
+        Bundle bundle = new Bundle();
+        bundle.putString("name", name);
+        finish(bundle);
+    }
+
+    public void finish(Bundle result) {
+        if (result == null) {
+            result = new Bundle();
+        }
+        finish(Activity.RESULT_OK, result);
+    }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index 4858be8..8fbd214 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -34,6 +34,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 public class BluetoothTestUtils extends Assert {
 
@@ -893,6 +894,17 @@
     }
 
     /**
+     * Deletes all pairings of remote devices
+     * @param adapter the BT adapter
+     */
+    public void unpairAll(BluetoothAdapter adapter) {
+        Set<BluetoothDevice> devices = adapter.getBondedDevices();
+        for (BluetoothDevice device : devices) {
+            unpair(adapter, device);
+        }
+    }
+
+    /**
      * Connects a profile from the local device to a remote device and checks to make sure that the
      * profile is connected and that the correct actions were broadcast.
      *
diff --git a/core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
similarity index 95%
rename from core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java
rename to core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 6d5ede8..cac6b93 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -33,7 +33,7 @@
 
 import java.util.Iterator;
 
-public class NetworkScorerApplicationTest extends InstrumentationTestCase {
+public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
     @Mock private Context mMockContext;
     @Mock private PackageManager mMockPm;
 
@@ -64,7 +64,7 @@
         setScorers(package1, package2, package3);
 
         Iterator<String> result =
-                NetworkScorerApplication.getAllValidScorers(mMockContext).iterator();
+                NetworkScorerAppManager.getAllValidScorers(mMockContext).iterator();
 
         assertTrue(result.hasNext());
         assertEquals("package1", result.next());
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index a122443..f169f5f 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -1145,7 +1145,7 @@
         &lt;item&gt;Venus&lt;/item&gt;
         &lt;item&gt;Earth&lt;/item&gt;
     &lt;/string-array&gt;
-&lt;/pre&gt;
+&lt;/resources&gt;
 </pre>
 
 <p>The {@link android.widget.ArrayAdapter} returned by {@link
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 03dd841..796da50 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -62,27 +62,31 @@
 
     /** Whether the center is within the parent bounds. */
     private boolean mInside;
+
+    /** Whether to pulse this ripple. */
+    boolean mPulse;
     
     /** Enter state. A value in [0...1] or -1 if not set. */
-    private float mEnterState = -1;
+    float mEnterState = -1;
 
     /** Exit state. A value in [0...1] or -1 if not set. */
-    private float mExitState = -1;
+    float mExitState = -1;
 
     /** Outside state. A value in [0...1] or -1 if not set. */
-    private float mOutsideState = -1;
+    float mOutsideState = -1;
 
     /** Pulse state. A value in [0...1] or -1 if not set. */
-    private float mPulseState = -1;
+    float mPulseState = -1;
 
     /**
      * Creates a new ripple with the specified parent bounds, padding, initial
      * position, and screen density.
      */
-    public Ripple(Rect bounds, Rect padding, float x, float y, float density) {
+    public Ripple(Rect bounds, Rect padding, float x, float y, float density, boolean pulse) {
         mBounds = bounds;
         mPadding = padding;
         mInside = mBounds.contains((int) x, (int) y);
+        mPulse = pulse;
 
         mX = x;
         mY = y;
@@ -115,6 +119,16 @@
         }
     }
 
+    public void onBoundsChanged() {
+        final boolean inside = mBounds.contains((int) mX, (int) mY);
+        if (mInside != inside) {
+            if (mAnimator != null) {
+                mAnimator.outside();
+            }
+            mInside = inside;
+        }
+    }
+
     public RippleAnimator animate() {
         if (mAnimator == null) {
             mAnimator = new RippleAnimator(this);
@@ -308,9 +322,11 @@
                     MathUtils.constrain((currentTime - mOutsideTime) / (float) OUTSIDE_DURATION, 0, 1));
 
             // Pulse is a little more complicated.
-            final long pulseTime = (currentTime - mEnterTime - ENTER_DURATION - PULSE_DELAY);
-            mTarget.mPulseState = pulseTime < 0 ? -1
-                    : (pulseTime % (PULSE_INTERVAL + PULSE_DURATION)) / (float) PULSE_DURATION;
+            if (mTarget.mPulse) {
+                final long pulseTime = (currentTime - mEnterTime - ENTER_DURATION - PULSE_DELAY);
+                mTarget.mPulseState = pulseTime < 0 ? -1
+                        : (pulseTime % (PULSE_INTERVAL + PULSE_DURATION)) / (float) PULSE_DURATION;
+            }
         }
     }
 }
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index cf08031..813d755c 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -112,6 +112,16 @@
     }
 
     @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+
+        final int N = mActiveRipplesCount;
+        for (int i = 0; i < N; i++) {
+            mActiveRipples[i].onBoundsChanged();
+        }
+    }
+
+    @Override
     public boolean setVisible(boolean visible, boolean restart) {
         if (!visible) {
             if (mTouchedRipples != null) {
@@ -291,7 +301,9 @@
                 y = bounds.exactCenterY();
             }
 
-            final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity);
+            // TODO: Clean this up in the API.
+            final boolean pulse = (id != R.attr.state_focused);
+            final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity, pulse);
             newRipple.animate().enter();
 
             mActiveRipples[mActiveRipplesCount++] = newRipple;
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 7c45682..4fbd2a4 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -297,8 +297,6 @@
 }
 
 JDrm::~JDrm() {
-    mDrm.clear();
-
     JNIEnv *env = AndroidRuntime::getJNIEnv();
 
     env->DeleteWeakGlobalRef(mObject);
@@ -363,6 +361,13 @@
     }
 }
 
+void JDrm::disconnect() {
+    if (mDrm != NULL) {
+        mDrm->destroyPlugin();
+        mDrm.clear();
+    }
+}
+
 
 // static
 bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
@@ -527,6 +532,7 @@
     sp<JDrm> drm = setDrm(env, thiz, NULL);
     if (drm != NULL) {
         drm->setListener(NULL);
+        drm->disconnect();
     }
 }
 
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index 620ad28..b7b8e5d 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -47,6 +47,8 @@
     void notify(DrmPlugin::EventType, int extra, const Parcel *obj);
     status_t setListener(const sp<DrmListener>& listener);
 
+    void disconnect();
+
 protected:
     virtual ~JDrm();
 
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index a9322b9..1406f6b 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -878,7 +878,7 @@
         protected int[] mConfigSpec;
 
         private int[] filterConfigSpec(int[] configSpec) {
-            if (mEGLContextClientVersion != 2) {
+            if (mEGLContextClientVersion != 2 && mEGLContextClientVersion != 3) {
                 return configSpec;
             }
             /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
@@ -888,7 +888,11 @@
             int[] newConfigSpec = new int[len + 2];
             System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
             newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
-            newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+            if (mEGLContextClientVersion == 2) {
+                newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT;  /* EGL_OPENGL_ES2_BIT */
+            } else {
+                newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR; /* EGL_OPENGL_ES3_BIT_KHR */
+            }
             newConfigSpec[len+1] = EGL10.EGL_NONE;
             return newConfigSpec;
         }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d371d70..327df8d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -273,7 +273,8 @@
 
         <service
             android:name=".keyguard.KeyguardService"
-            android:exported="true" />
+            android:exported="true"
+            android:enabled="@bool/config_enableKeyguardService" />
 
         <activity android:name=".Somnambulator"
             android:label="@string/start_dreams"
diff --git a/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..85db9c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..c4941a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..4618f40
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png
new file mode 100644
index 0000000..36e7e45
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..c0bf31d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/recents_search_bar.xml b/packages/SystemUI/res/layout/recents_search_bar.xml
new file mode 100644
index 0000000..915283e
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_search_bar.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/search_bg_transparent">
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/recents_search_bar_label"
+        android:textColor="#99ffffff"
+        android:textSize="18sp"
+        android:textAllCaps="true" />
+</FrameLayout>
+
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 7f64032..4442bca 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -63,13 +63,6 @@
             android:maxLines="2"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
-        <ImageView
-            android:id="@+id/activity_icon"
-            android:layout_width="@dimen/recents_task_view_activity_icon_size"
-            android:layout_height="@dimen/recents_task_view_activity_icon_size"
-            android:layout_gravity="center_vertical|end"
-            android:padding="12dp"
-            android:visibility="invisible" />
     </com.android.systemui.recents.views.TaskBarView>
 </com.android.systemui.recents.views.TaskView>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 85a9388..722ca15 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -72,7 +72,7 @@
     <!-- decay duration (from size_max -> size), in ms -->
     <integer name="navigation_bar_deadzone_hold">333</integer>
     <integer name="navigation_bar_deadzone_decay">333</integer>
-    
+
     <bool name="config_dead_zone_flash">false</bool>
 
     <!-- Min alpha % that recent items will fade to while being dismissed -->
@@ -122,6 +122,9 @@
     <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
     <integer name="recents_max_task_stack_view_dim">96</integer>
 
+    <!-- Whether to enable KeyguardService or not -->
+    <bool name="config_enableKeyguardService">true</bool>
+
     <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
      card. -->
     <integer name="keyguard_max_notification_count">4</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 25ae38c..3047f15 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -236,6 +236,12 @@
     <!-- The amount of space a user has to scroll to dismiss any info panes. -->
     <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
 
+    <!-- The height of the search bar space. -->
+    <dimen name="recents_search_bar_space_height">40dp</dimen>
+
+    <!-- The search bar edge margins. -->
+    <dimen name="recents_search_bar_space_edge_margins">12dp</dimen>
+
     <!-- Used to calculate the translation animation duration, the expected amount of movement 
          in dps over one second of time. -->
     <dimen name="recents_animation_movement_in_dps_per_second">800dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 73e5e19..72d14fe 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -511,6 +511,8 @@
     <string name="recents_empty_message">RECENTS</string>
     <!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
     <string name="recents_app_info_button_label">Application Info</string>
+    <!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
+    <string name="recents_search_bar_label">search</string>
 
 
     <!-- Glyph to be overlaid atop the battery when the level is extremely low. Do not translate. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 4fb90cb..f2e322d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -44,6 +44,7 @@
 import android.view.WindowManager;
 import com.android.systemui.R;
 
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
@@ -240,12 +241,10 @@
     }
 
     /** Returns whether there is are multiple recents tasks */
-    boolean hasMultipleRecentsTask() {
+    boolean hasMultipleRecentsTask(List<ActivityManager.RecentTaskInfo> tasks) {
         // NOTE: Currently there's no method to get the number of non-home tasks, so we have to
         // compute this ourselves
         SystemServicesProxy ssp = mSystemServicesProxy;
-        List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(4,
-                UserHandle.CURRENT.getIdentifier());
         Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
         while (iter.hasNext()) {
             ActivityManager.RecentTaskInfo t = iter.next();
@@ -259,6 +258,17 @@
         return (tasks.size() > 1);
     }
 
+    /** Returns whether the base intent of the top task stack was launched with the flag
+     * Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. */
+    boolean isTopTaskExcludeFromRecents(List<ActivityManager.RecentTaskInfo> tasks) {
+        if (tasks.size() > 0) {
+            ActivityManager.RecentTaskInfo t = tasks.get(0);
+            Console.log(t.baseIntent.toString());
+            return (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+        }
+        return false;
+    }
+
     /** Converts from the device rotation to the degree */
     float getDegreesForRotation(int value) {
         switch (value) {
@@ -334,10 +344,18 @@
             isTopTaskHome = ssp.isInHomeStack(topTask.id);
         }
 
-        // Otherwise, Recents is not the front-most activity and we should animate into it
-        boolean hasMultipleTasks = hasMultipleRecentsTask();
+        // Otherwise, Recents is not the front-most activity and we should animate into it.  If
+        // the activity at the root of the top task stack is excluded from recents, or if that
+        // task stack is in the home stack, then we just do a simple transition.  Otherwise, we
+        // animate to the rects defined by the Recents service, which can differ depending on the
+        // number of items in the list.
+        List<ActivityManager.RecentTaskInfo> recentTasks =
+                ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier());
+        boolean hasMultipleTasks = hasMultipleRecentsTask(recentTasks);
+        boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks);
         Rect taskRect = hasMultipleTasks ? mMultipleCountFirstTaskRect : mSingleCountFirstTaskRect;
-        if (!isTopTaskHome && taskRect != null && taskRect.width() > 0 && taskRect.height() > 0) {
+        if (!isTopTaskHome && !isTaskExcludedFromRecents &&
+                (taskRect != null) && (taskRect.width() > 0) && (taskRect.height() > 0)) {
             // Loading from thumbnail
             Bitmap thumbnail;
             Bitmap firstThumbnail = loadFirstTaskThumbnail();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 64770a4..72d9a52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -29,6 +29,7 @@
             public static final boolean EnableTaskFiltering = true;
             public static final boolean EnableTaskStackClipping = false;
             public static final boolean EnableInfoPane = true;
+            public static final boolean EnableSearchButton = false;
 
             // This disables the bitmap and icon caches
             public static final boolean DisableBackgroundCache = false;
@@ -84,6 +85,9 @@
             public static final int TaskStackOverscrollRange = 150;
             public static final int FilterStartDelay = 25;
 
+            // The amount to inverse scale the movement if we are overscrolling
+            public static final float TouchOverscrollScaleFactor = 3f;
+
             // The padding will be applied to the smallest dimension, and then applied to all sides
             public static final float StackPaddingPct = 0.15f;
             // The overlap height relative to the task height
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 5e5b841..d54df13 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -44,6 +44,8 @@
     public int taskStackMaxDim;
     public int taskViewInfoPaneAnimDuration;
     public int taskViewRoundedCornerRadiusPx;
+    public int searchBarSpaceHeightPx;
+    public int searchBarSpaceEdgeMarginsPx;
 
     public boolean launchedWithThumbnailAnimation;
 
@@ -92,6 +94,9 @@
                 res.getInteger(R.integer.recents_animate_task_view_info_pane_duration);
         taskViewRoundedCornerRadiusPx =
                 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
+        searchBarSpaceEdgeMarginsPx =
+                res.getDimensionPixelSize(R.dimen.recents_search_bar_space_edge_margins);
     }
 
     /** Updates the system insets */
@@ -99,6 +104,26 @@
         systemInsets.set(insets);
     }
 
+    /** Returns the search bar bounds in the specified orientation */
+    public void getSearchBarBounds(int width, int height,
+                                   Rect searchBarSpaceBounds, Rect searchBarBounds) {
+        // Return empty rects if search is not enabled
+        if (!Constants.DebugFlags.App.EnableSearchButton) {
+            searchBarSpaceBounds.set(0, 0, 0, 0);
+            searchBarBounds.set(0, 0, 0, 0);
+            return;
+        }
+
+        // Calculate the search bar bounds, and account for the system insets
+        int edgeMarginPx = searchBarSpaceEdgeMarginsPx;
+        int availableWidth = width - systemInsets.left - systemInsets.right;
+        searchBarSpaceBounds.set(0, 0, availableWidth, 2 * edgeMarginPx + searchBarSpaceHeightPx);
+
+        // Inset from the search bar space to get the search bar bounds
+        searchBarBounds.set(searchBarSpaceBounds);
+        searchBarBounds.inset(edgeMarginPx, edgeMarginPx);
+    }
+
     /** Converts from DPs to PXs */
     public int pxFromDp(float size) {
         return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 06ca9e2..36b761e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -66,22 +66,33 @@
                 Bundle replyData = new Bundle();
                 TaskViewTransform transform;
 
+                // Get the search bar bounds so that we can account for its height in the children
+                Rect searchBarSpaceBounds = new Rect();
+                Rect searchBarBounds = new Rect();
+                RecentsConfiguration config = RecentsConfiguration.getInstance();
+                config.getSearchBarBounds(windowRect.width(), windowRect.height(),
+                        searchBarSpaceBounds, searchBarBounds);
+
                 // Calculate the target task rect for when there is one task
                 // NOTE: Since the nav bar height is already accounted for in the windowRect, don't
                 // pass in a bottom inset
                 stack.addTask(new Task());
-                tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
+                tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top -
+                        systemInsets.bottom - searchBarSpaceBounds.height(), 0);
                 tsv.boundScroll();
                 transform = tsv.getStackTransform(0, tsv.getStackScroll());
+                transform.rect.offset(0, searchBarSpaceBounds.height());
                 replyData.putParcelable(AlternateRecentsComponent.KEY_SINGLE_TASK_STACK_RECT,
                         new Rect(transform.rect));
 
                 // Also calculate the target task rect when there are multiple tasks
                 stack.addTask(new Task());
-                tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
+                tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top -
+                        systemInsets.bottom - searchBarSpaceBounds.height(), 0);
                 tsv.setStackScrollRaw(Integer.MAX_VALUE);
                 tsv.boundScroll();
                 transform = tsv.getStackTransform(1, tsv.getStackScroll());
+                transform.rect.offset(0, searchBarSpaceBounds.height());
                 replyData.putParcelable(AlternateRecentsComponent.KEY_MULTIPLE_TASK_STACK_RECT,
                         new Rect(transform.rect));
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index fd0f6d1..da265e1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -415,7 +415,10 @@
             ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
             String activityLabel = (t.activityLabel == null ? ssp.getActivityLabel(info) :
                     t.activityLabel.toString());
-            Bitmap activityIcon = t.activityIcon;
+            BitmapDrawable activityIcon = null;
+            if (t.activityIcon != null) {
+                activityIcon = new BitmapDrawable(res, t.activityIcon);
+            }
             boolean isForemostTask = (i == (taskCount - 1));
 
             // Create a new task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 7f0d9ee..33ac0a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -19,6 +19,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -26,11 +28,15 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -45,6 +51,7 @@
     PackageManager mPm;
     IPackageManager mIpm;
     UserManager mUm;
+    SearchManager mSm;
     String mPackage;
 
     Bitmap mDummyIcon;
@@ -55,6 +62,7 @@
         mPm = context.getPackageManager();
         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mIpm = AppGlobals.getPackageManager();
+        mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
         mPackage = context.getPackageName();
 
         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
@@ -199,4 +207,28 @@
         }
         return icon;
     }
+
+
+    /**
+     * Composes an intent to launch the global search activity.
+     */
+    public Intent getGlobalSearchIntent(Rect sourceBounds) {
+        if (mSm == null) return null;
+
+        // Try and get the global search activity
+        ComponentName globalSearchActivity = mSm.getGlobalSearchActivity();
+        if (globalSearchActivity == null) return null;
+
+        // Bundle the source of the search
+        Bundle appSearchData = new Bundle();
+        appSearchData.putString("source", mPackage);
+
+        // Compose the intent and Start the search activity
+        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setComponent(globalSearchActivity);
+        intent.putExtra(SearchManager.APP_DATA, appSearchData);
+        intent.setSourceBounds(sourceBounds);
+        return intent;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index ff062f6c..1566a49 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 
 
@@ -69,8 +70,8 @@
 
     public TaskKey key;
     public Drawable applicationIcon;
+    public Drawable activityIcon;
     public String activityLabel;
-    public Bitmap activityIcon;
     public Bitmap thumbnail;
     public boolean isActive;
     public int userId;
@@ -82,7 +83,7 @@
     }
 
     public Task(int id, boolean isActive, Intent intent, String activityTitle,
-                Bitmap activityIcon, int userId) {
+                BitmapDrawable activityIcon, int userId) {
         this.key = new TaskKey(id, intent, userId);
         this.activityLabel = activityTitle;
         this.activityIcon = activityIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b054a22..a04cd3e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -27,8 +27,11 @@
 import android.net.Uri;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.FrameLayout;
+import android.widget.TextView;
 import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -36,6 +39,7 @@
 import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.R;
 
 import java.util.ArrayList;
 
@@ -53,11 +57,16 @@
 
     // The space partitioning root of this container
     SpaceNode mBSP;
+    // Search bar view
+    View mSearchBar;
     // Recents view callbacks
     RecentsViewCallbacks mCb;
 
+    LayoutInflater mInflater;
+
     public RecentsView(Context context) {
         super(context);
+        mInflater = LayoutInflater.from(context);
         setWillNotDraw(false);
     }
 
@@ -71,12 +80,22 @@
         mBSP = n;
 
         // Create and add all the stacks for this partition of space.
+        boolean hasTasks = false;
         removeAllViews();
         ArrayList<TaskStack> stacks = mBSP.getStacks();
         for (TaskStack stack : stacks) {
             TaskStackView stackView = new TaskStackView(getContext(), stack);
             stackView.setCallbacks(this);
             addView(stackView);
+            hasTasks |= (stack.getTaskCount() > 0);
+        }
+
+        // Create the search bar (and hide it if we have no recent tasks)
+        if (Constants.DebugFlags.App.EnableSearchButton) {
+            createSearchBar();
+            if (!hasTasks) {
+                mSearchBar.setVisibility(View.GONE);
+            }
         }
     }
 
@@ -85,29 +104,45 @@
         // Get the first stack view
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
-            TaskStackView stackView = (TaskStackView) getChildAt(i);
-            TaskStack stack = stackView.mStack;
-            ArrayList<Task> tasks = stack.getTasks();
+            View child = getChildAt(i);
+            if (child instanceof TaskStackView) {
+                TaskStackView stackView = (TaskStackView) child;
+                TaskStack stack = stackView.mStack;
+                ArrayList<Task> tasks = stack.getTasks();
 
-            // Get the first task in the stack
-            if (!tasks.isEmpty()) {
-                Task task = tasks.get(tasks.size() - 1);
-                TaskView tv = null;
+                // Get the first task in the stack
+                if (!tasks.isEmpty()) {
+                    Task task = tasks.get(tasks.size() - 1);
+                    TaskView tv = null;
 
-                // Try and use the first child task view as the source of the launch animation
-                if (stackView.getChildCount() > 0) {
-                    TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
-                    if (stv.getTask() == task) {
-                        tv = stv;
+                    // Try and use the first child task view as the source of the launch animation
+                    if (stackView.getChildCount() > 0) {
+                        TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
+                        if (stv.getTask() == task) {
+                            tv = stv;
+                        }
                     }
+                    onTaskLaunched(stackView, tv, stack, task);
+                    return true;
                 }
-                onTaskLaunched(stackView, tv, stack, task);
-                return true;
             }
         }
         return false;
     }
 
+    /** Creates and adds the search bar */
+    void createSearchBar() {
+        // Create a temporary search bar
+        mSearchBar = mInflater.inflate(R.layout.recents_search_bar, this, false);
+        mSearchBar.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                onSearchTriggered();
+            }
+        });
+        addView(mSearchBar);
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = MeasureSpec.getSize(widthMeasureSpec);
@@ -120,16 +155,26 @@
         Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
                 Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
 
-        // We measure our stack views sans the status bar.  It will handle the nav bar itself.
+        // Get the search bar bounds so that we can account for its height in the children
+        Rect searchBarSpaceBounds = new Rect();
+        Rect searchBarBounds = new Rect();
         RecentsConfiguration config = RecentsConfiguration.getInstance();
+        config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+                searchBarSpaceBounds, searchBarBounds);
+        if (mSearchBar != null) {
+            mSearchBar.measure(MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), widthMode),
+                    MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), heightMode));
+        }
+
+        // We measure our stack views sans the status bar.  It will handle the nav bar itself.
         int childWidth = width - config.systemInsets.right;
-        int childHeight = height - config.systemInsets.top;
+        int childHeight = height - config.systemInsets.top - searchBarSpaceBounds.height();
 
         // Measure each child
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
+            View child = getChildAt(i);
+            if (child instanceof TaskStackView && child.getVisibility() != GONE) {
                 child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
                         MeasureSpec.makeMeasureSpec(childHeight, heightMode));
             }
@@ -145,18 +190,30 @@
         Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
                 Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onLayout");
 
-        // We offset our stack views by the status bar height.  It will handle the nav bar itself.
+        // Get the search bar bounds so that we can account for its height in the children
+        Rect searchBarSpaceBounds = new Rect();
+        Rect searchBarBounds = new Rect();
         RecentsConfiguration config = RecentsConfiguration.getInstance();
-        top += config.systemInsets.top;
+        config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+                searchBarSpaceBounds, searchBarBounds);
+        if (mSearchBar != null) {
+            mSearchBar.layout(config.systemInsets.left + searchBarSpaceBounds.left,
+                    config.systemInsets.top + searchBarSpaceBounds.top,
+                    config.systemInsets.left + mSearchBar.getMeasuredWidth(),
+                    config.systemInsets.top + mSearchBar.getMeasuredHeight());
+        }
+
+        // We offset our stack views by the status bar height.  It will handle the nav bar itself.
+        top += config.systemInsets.top + searchBarSpaceBounds.height();
 
         // Layout each child
         // XXX: Based on the space node for that task view
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                final int width = child.getMeasuredWidth();
-                final int height = child.getMeasuredHeight();
+            View child = getChildAt(i);
+            if (child instanceof TaskStackView && child.getVisibility() != GONE) {
+                int width = child.getMeasuredWidth();
+                int height = child.getMeasuredHeight();
                 child.layout(left, top, left + width, top + height);
             }
         }
@@ -188,9 +245,12 @@
             // Get the first stack view
             int childCount = getChildCount();
             for (int i = 0; i < childCount; i++) {
-                TaskStackView stackView = (TaskStackView) getChildAt(i);
-                if (stackView.closeOpenInfoPanes()) {
-                    return true;
+                View child = getChildAt(i);
+                if (child instanceof TaskStackView) {
+                    TaskStackView stackView = (TaskStackView) child;
+                    if (stackView.closeOpenInfoPanes()) {
+                        return true;
+                    }
                 }
             }
         }
@@ -266,7 +326,6 @@
                             b, offsetX, offsetY);
                 }
 
-
                 if (task.isActive) {
                     // Bring an active task to the foreground
                     RecentsTaskLoader.getInstance().getSystemServicesProxy()
@@ -315,4 +374,24 @@
         TaskStackBuilder.create(getContext())
                 .addNextIntentWithParentStack(intent).startActivities();
     }
+
+    public void onSearchTriggered() {
+        // Get the search bar source bounds
+        Rect searchBarSpaceBounds = new Rect();
+        Rect searchBarBounds = new Rect();
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+                searchBarSpaceBounds, searchBarBounds);
+
+        // Get the search intent and start it
+        Intent searchIntent = RecentsTaskLoader.getInstance().getSystemServicesProxy()
+                .getGlobalSearchIntent(searchBarBounds);
+        if (searchIntent != null) {
+            try {
+                getContext().startActivity(searchIntent);
+            } catch (ActivityNotFoundException anfe) {
+                Console.logError(getContext(), "Could not start Search activity");
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index c9a6d67..124f11e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -31,7 +31,6 @@
     Task mTask;
 
     ImageView mApplicationIcon;
-    ImageView mActivityIcon;
     TextView mActivityDescription;
 
     public TaskBarView(Context context) {
@@ -54,23 +53,22 @@
     protected void onFinishInflate() {
         // Initialize the icon and description views
         mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
-        mActivityIcon = (ImageView) findViewById(R.id.activity_icon);
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
     }
 
     /** Binds the bar view to the task */
     void rebindToTask(Task t, boolean animate) {
         mTask = t;
-        if (t.applicationIcon != null) {
+        // If an activity icon is defined, then we use that as the primary icon to show in the bar,
+        // otherwise, we fall back to the application icon
+        if (t.activityIcon != null) {
+            mApplicationIcon.setImageDrawable(t.activityIcon);
+        } else if (t.applicationIcon != null) {
             mApplicationIcon.setImageDrawable(t.applicationIcon);
-            mActivityDescription.setText(t.activityLabel);
-            if (t.activityIcon != null) {
-                mActivityIcon.setImageBitmap(t.activityIcon);
-                mActivityIcon.setVisibility(View.VISIBLE);
-            }
-            if (animate) {
-                // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
-            }
+        }
+        mActivityDescription.setText(t.activityLabel);
+        if (animate) {
+            // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
         }
     }
 
@@ -78,8 +76,6 @@
     void unbindFromTask() {
         mTask = null;
         mApplicationIcon.setImageDrawable(null);
-        mActivityIcon.setImageBitmap(null);
-        mActivityIcon.setVisibility(View.INVISIBLE);
         mActivityDescription.setText("");
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
index 233e38c..a81d01c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
@@ -70,10 +70,8 @@
     /** Updates the positions of each of the items to fit in the rect specified */
     void updateContents(Rect visibleRect) {
         // Offset the app info button
-        LayoutParams lp = (LayoutParams) mAppInfoButton.getLayoutParams();
-        lp.topMargin = visibleRect.top +
-                (visibleRect.height() - mAppInfoButton.getMeasuredHeight()) / 2;
-        requestLayout();
+        mAppInfoButton.setTranslationY(visibleRect.top +
+                (visibleRect.height() - mAppInfoButton.getMeasuredHeight()) / 2);
     }
 
     /** Sets the circular clip radius on this panel */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index ee92b16..a77e61d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -395,9 +395,12 @@
         return false;
     }
 
-    /** Returns whether the current scroll is out of bounds */
+    /** Returns whether the specified scroll is out of bounds */
+    boolean isScrollOutOfBounds(int scroll) {
+        return (scroll < mMinScroll) || (scroll > mMaxScroll);
+    }
     boolean isScrollOutOfBounds() {
-        return (getStackScroll() < 0) || (getStackScroll() > mMaxScroll);
+        return isScrollOutOfBounds(getStackScroll());
     }
 
     /** Updates the min and max virtual scroll bounds */
@@ -556,7 +559,14 @@
 
         int smallestDimension = Math.min(width, height);
         int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f);
-        mStackRect.inset(padding, padding);
+        if (Constants.DebugFlags.App.EnableSearchButton) {
+            // Don't need to pad the top since we have some padding on the search bar already
+            mStackRect.left += padding;
+            mStackRect.right -= padding;
+            mStackRect.bottom -= padding;
+        } else {
+            mStackRect.inset(padding, padding);
+        }
         mStackRectSansPeek.set(mStackRect);
         mStackRectSansPeek.top += Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
 
@@ -1275,7 +1285,12 @@
                     }
                 }
                 if (mIsScrolling) {
-                    mSv.setStackScroll(mSv.getStackScroll() + deltaY);
+                    int curStackScroll = mSv.getStackScroll();
+                    if (mSv.isScrollOutOfBounds(curStackScroll + deltaY)) {
+                        // Scale the touch if we are overscrolling
+                        deltaY /= Constants.Values.TaskStackView.TouchOverscrollScaleFactor;
+                    }
+                    mSv.setStackScroll(curStackScroll + deltaY);
                     if (mSv.isScrollOutOfBounds()) {
                         mVelocityTracker.clear();
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index f2054a2..cf31b44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -83,7 +83,7 @@
     }
 
     public void onScreenTurnedOff() {
-        if (mKeyguardView != null && mRoot.getVisibility() == View.VISIBLE) {
+        if (mKeyguardView != null && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
             mKeyguardView.onPause();
         }
     }
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index bce85ce..7249985 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -106,22 +106,33 @@
             .append("Kernel: ")
             .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
             .append("\n").toString();
+        final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
 
         String recovery = RecoverySystem.handleAftermath();
         if (recovery != null && db != null) {
             db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
         }
 
+        String lastKmsgFooter = "";
+        if (bootReason != null) {
+            lastKmsgFooter = new StringBuilder(512)
+                .append("\n")
+                .append("Boot info:\n")
+                .append("Last boot reason: ").append(bootReason).append("\n")
+                .toString();
+        }
+
         if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
             String now = Long.toString(System.currentTimeMillis());
             SystemProperties.set("ro.runtime.firstboot", now);
             if (db != null) db.addText("SYSTEM_BOOT", headers);
 
             // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile())
-            addFileToDropBox(db, prefs, headers, "/proc/last_kmsg",
-                    -LOG_SIZE, "SYSTEM_LAST_KMSG");
-            addFileToDropBox(db, prefs, headers, "/sys/fs/pstore/console-ramoops",
-                    -LOG_SIZE, "SYSTEM_LAST_KMSG");
+            addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
+                    "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG");
+            addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
+                    "/sys/fs/pstore/console-ramoops", -LOG_SIZE,
+                    "SYSTEM_LAST_KMSG");
             addFileToDropBox(db, prefs, headers, "/cache/recovery/log",
                     -LOG_SIZE, "SYSTEM_RECOVERY_LOG");
             addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console",
@@ -161,6 +172,14 @@
     private static void addFileToDropBox(
             DropBoxManager db, SharedPreferences prefs,
             String headers, String filename, int maxSize, String tag) throws IOException {
+        addFileWithFootersToDropBox(db, prefs, headers, "", filename, maxSize,
+                tag);
+    }
+
+    private static void addFileWithFootersToDropBox(
+            DropBoxManager db, SharedPreferences prefs,
+            String headers, String footers, String filename, int maxSize,
+            String tag) throws IOException {
         if (db == null || !db.isTagEnabled(tag)) return;  // Logging disabled
 
         File file = new File(filename);
@@ -176,7 +195,7 @@
         }
 
         Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
-        db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"));
+        db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + footers);
     }
 
     private static void addAuditErrorsToDropBox(DropBoxManager db,  SharedPreferences prefs,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 9629bd1..705862a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -153,7 +153,7 @@
      */
     private NativeDaemonConnector mConnector;
 
-    private final Handler mMainHandler = new Handler();
+    private final Handler mFgHandler;
 
     private IBatteryStats mBatteryStats;
 
@@ -203,6 +203,9 @@
     private NetworkManagementService(Context context, String socket) {
         mContext = context;
 
+        // make sure this is on the same looper as our NativeDaemonConnector for sync purposes
+        mFgHandler = new Handler(FgThread.get().getLooper());
+
         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
             return;
         }
@@ -271,14 +274,17 @@
      */
     private void notifyInterfaceStatusChanged(String iface, boolean up) {
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
     }
 
     /**
@@ -287,14 +293,17 @@
      */
     private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
     }
 
     /**
@@ -302,14 +311,17 @@
      */
     private void notifyInterfaceAdded(String iface) {
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).interfaceAdded(iface);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).interfaceAdded(iface);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
     }
 
     /**
@@ -322,14 +334,17 @@
         mActiveQuotas.remove(iface);
 
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).interfaceRemoved(iface);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).interfaceRemoved(iface);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
     }
 
     /**
@@ -337,14 +352,17 @@
      */
     private void notifyLimitReached(String limitName, String iface) {
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).limitReached(limitName, iface);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).limitReached(limitName, iface);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
     }
 
     /**
@@ -357,15 +375,18 @@
         }
 
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
-                        Integer.toString(type), active, tsNanos);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
+                            Integer.toString(type), active, tsNanos);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
 
         boolean report = false;
         synchronized (mIdleTimerLock) {
@@ -456,14 +477,17 @@
      */
     private void notifyAddressUpdated(String iface, LinkAddress address) {
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).addressUpdated(iface, address);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).addressUpdated(iface, address);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
     }
 
     /**
@@ -471,14 +495,17 @@
      */
     private void notifyAddressRemoved(String iface, LinkAddress address) {
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).addressRemoved(iface, address);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).addressRemoved(iface, address);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
     }
 
     /**
@@ -486,14 +513,18 @@
      */
     private void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
         final int length = mObservers.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime, addresses);
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime,
+                        addresses);
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mObservers.finishBroadcast();
         }
-        mObservers.finishBroadcast();
     }
 
     //
@@ -509,7 +540,7 @@
                 mConnectedSignal.countDown();
                 mConnectedSignal = null;
             } else {
-                mMainHandler.post(new Runnable() {
+                mFgHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         prepareNativeDaemon();
@@ -1270,7 +1301,7 @@
             if (ConnectivityManager.isNetworkTypeMobile(type)) {
                 mNetworkActive = false;
             }
-            mMainHandler.post(new Runnable() {
+            mFgHandler.post(new Runnable() {
                 @Override public void run() {
                     notifyInterfaceClassActivity(type, true, SystemClock.elapsedRealtimeNanos());
                 }
@@ -1297,7 +1328,7 @@
                 throw e.rethrowAsParcelableException();
             }
             mActiveIdleTimers.remove(iface);
-            mMainHandler.post(new Runnable() {
+            mFgHandler.post(new Runnable() {
                 @Override public void run() {
                     notifyInterfaceClassActivity(params.type, false,
                             SystemClock.elapsedRealtimeNanos());
@@ -1880,14 +1911,17 @@
 
     private void reportNetworkActive() {
         final int length = mNetworkActivityListeners.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            try {
-                mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
-            } catch (RemoteException e) {
-            } catch (RuntimeException e) {
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
+                } catch (RemoteException e) {
+                } catch (RuntimeException e) {
+                }
             }
+        } finally {
+            mNetworkActivityListeners.finishBroadcast();
         }
-        mNetworkActivityListeners.finishBroadcast();
     }
 
     /** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
new file mode 100644
index 0000000..8a30e50
--- /dev/null
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.Manifest.permission;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.INetworkScoreService;
+import android.net.NetworkKey;
+import android.net.NetworkScorerAppManager;
+import android.net.RssiCurve;
+import android.net.ScoredNetwork;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Backing service for {@link android.net.NetworkScoreManager}.
+ * @hide
+ */
+public class NetworkScoreService extends INetworkScoreService.Stub {
+    private static final String TAG = "NetworkScoreService";
+
+    /** SharedPreference bit set to true after the service is first initialized. */
+    private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
+
+    private final Context mContext;
+
+    // TODO: Delete this temporary class once we have a real place for scores.
+    private final Map<NetworkKey, RssiCurve> mScoredNetworks;
+
+    public NetworkScoreService(Context context) {
+        mContext = context;
+        mScoredNetworks = new HashMap<>();
+    }
+
+    /** Called when the system is ready to run third-party code but before it actually does so. */
+    void systemReady() {
+        SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
+        if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
+            // On first run, we try to initialize the scorer to the one configured at build time.
+            // This will be a no-op if the scorer isn't actually valid.
+            String defaultPackage = mContext.getResources().getString(
+                    R.string.config_defaultNetworkScorerPackageName);
+            if (!TextUtils.isEmpty(defaultPackage)) {
+                NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
+            }
+            prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
+        }
+    }
+
+    @Override
+    public boolean updateScores(ScoredNetwork[] networks) {
+        if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
+            throw new SecurityException("Caller with UID " + getCallingUid() +
+                    " is not the active scorer.");
+        }
+
+        // TODO: Propagate these scores down to the network subsystem layer instead of just holding
+        // them in memory.
+        for (ScoredNetwork network : networks) {
+            mScoredNetworks.put(network.networkKey, network.rssiCurve);
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean clearScores() {
+        // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should
+        // be allowed to flush all scores.
+        if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
+                mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
+                        PackageManager.PERMISSION_GRANTED) {
+            clearInternal();
+            return true;
+        } else {
+            throw new SecurityException(
+                    "Caller is neither the active scorer nor the scorer manager.");
+        }
+    }
+
+    @Override
+    public boolean setActiveScorer(String packageName) {
+        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
+        // Preemptively clear scores even though the set operation could fail. We do this for safety
+        // as scores should never be compared across apps; in practice, Settings should only be
+        // allowing valid apps to be set as scorers, so failure here should be rare.
+        clearInternal();
+        return NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+    }
+
+    /** Clear scores. Callers are responsible for checking permissions as appropriate. */
+    private void clearInternal() {
+        // TODO: Propagate the flush down to the network subsystem layer.
+        mScoredNetworks.clear();
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
+        String currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
+        if (currentScorer == null) {
+            writer.println("Scoring is disabled.");
+            return;
+        }
+        writer.println("Current scorer: " + currentScorer);
+        if (mScoredNetworks.isEmpty()) {
+            writer.println("No networks scored.");
+        } else {
+            for (Map.Entry<NetworkKey, RssiCurve> entry : mScoredNetworks.entrySet()) {
+                writer.println(entry.getKey() + ": " + entry.getValue());
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7607419..ecbb0d9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7240,6 +7240,9 @@
             if (r == null) {
                 return null;
             }
+            if (callback == null) {
+                throw new IllegalArgumentException("callback must not be null");
+            }
             return mStackSupervisor.createActivityContainer(r, callback);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d596472..60adfb0 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -139,7 +139,7 @@
     boolean forceNewConfig; // force re-create with new config next time
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last lauch of this activity
-    ArrayList<ActivityStack> mChildContainers = new ArrayList<ActivityStack>();
+    ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>();
 
     String stringName;      // for caching of toString().
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8dd4317..0acde09 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -36,6 +36,8 @@
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
 
+import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE;
+
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -1066,9 +1068,9 @@
     private void setVisibile(ActivityRecord r, boolean visible) {
         r.visible = visible;
         mWindowManager.setAppVisibility(r.appToken, visible);
-        final ArrayList<ActivityStack> containers = r.mChildContainers;
+        final ArrayList<ActivityContainer> containers = r.mChildContainers;
         for (int containerNdx = containers.size() - 1; containerNdx >= 0; --containerNdx) {
-            ActivityContainer container = containers.get(containerNdx).mActivityContainer;
+            ActivityContainer container = containers.get(containerNdx);
             container.setVisible(visible);
         }
     }
@@ -1270,6 +1272,7 @@
      * occurred and the activity will be notified immediately.
      */
     void notifyActivityDrawnLocked(ActivityRecord r) {
+        mActivityContainer.setDrawn();
         if ((r == null)
                 || (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
                         mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
@@ -1336,7 +1339,8 @@
         if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
 
         ActivityRecord parent = mActivityContainer.mParentActivity;
-        if (parent != null && parent.state != ActivityState.RESUMED) {
+        if ((parent != null && parent.state != ActivityState.RESUMED) ||
+                !mActivityContainer.isAttached()) {
             // Do not resume this stack if its parent is not resumed.
             // TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st.
             return false;
@@ -2624,6 +2628,20 @@
         return r;
     }
 
+    void finishAllActivitiesLocked() {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.finishing) {
+                    continue;
+                }
+                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r);
+                finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+            }
+        }
+    }
+
     final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
             Intent resultData) {
         final ActivityRecord srec = ActivityRecord.forToken(token);
@@ -2862,7 +2880,6 @@
         }
         if (activityRemoved) {
             mStackSupervisor.resumeTopActivitiesLocked();
-
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 63f9d09..111f010 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -96,7 +96,6 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -225,11 +224,11 @@
 
     // TODO: Add listener for removal of references.
     /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
-    SparseArray<WeakReference<ActivityContainer>> mActivityContainers =
-            new SparseArray<WeakReference<ActivityContainer>>();
+    SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
 
     /** Mapping from displayId to display current state */
-    private SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<ActivityDisplay>();
+    private final SparseArray<ActivityDisplay> mActivityDisplays =
+            new SparseArray<ActivityDisplay>();
 
     InputManagerInternal mInputManagerInternal;
 
@@ -265,7 +264,7 @@
                 mActivityDisplays.put(displayId, activityDisplay);
             }
 
-            createStackOnDisplay(null, HOME_STACK_ID, Display.DEFAULT_DISPLAY);
+            createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY);
             mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
 
             mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -1386,7 +1385,7 @@
             }
 
             // Need to create an app stack for this user.
-            int stackId = createStackOnDisplay(null, getNextStackId(), Display.DEFAULT_DISPLAY);
+            int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
             if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
                     " stackId=" + stackId);
             mFocusedStack = getStack(stackId);
@@ -2154,14 +2153,9 @@
     }
 
     ActivityStack getStack(int stackId) {
-        WeakReference<ActivityContainer> weakReference = mActivityContainers.get(stackId);
-        if (weakReference != null) {
-            ActivityContainer activityContainer = weakReference.get();
-            if (activityContainer != null) {
-                return activityContainer.mStack;
-            } else {
-                mActivityContainers.remove(stackId);
-            }
+        ActivityContainer activityContainer = mActivityContainers.get(stackId);
+        if (activityContainer != null) {
+            return activityContainer.mStack;
         }
         return null;
     }
@@ -2191,49 +2185,26 @@
         return null;
     }
 
-    ActivityContainer createActivityContainer(ActivityRecord parentActivity, int stackId,
+    ActivityContainer createActivityContainer(ActivityRecord parentActivity,
             IActivityContainerCallback callback) {
-        ActivityContainer activityContainer = new ActivityContainer(parentActivity, stackId,
-                callback);
-        mActivityContainers.put(stackId, new WeakReference<ActivityContainer>(activityContainer));
-        if (parentActivity != null) {
-            parentActivity.mChildContainers.add(activityContainer.mStack);
-        }
+        ActivityContainer activityContainer = new VirtualActivityContainer(parentActivity, callback);
+        mActivityContainers.put(activityContainer.mStackId, activityContainer);
+        parentActivity.mChildContainers.add(activityContainer);
         return activityContainer;
     }
 
-    ActivityContainer createActivityContainer(ActivityRecord parentActivity,
-            IActivityContainerCallback callback) {
-        return createActivityContainer(parentActivity, getNextStackId(), callback);
-    }
-
     void removeChildActivityContainers(ActivityRecord parentActivity) {
-        for (int ndx = mActivityContainers.size() - 1; ndx >= 0; --ndx) {
-            final ActivityContainer container = mActivityContainers.valueAt(ndx).get();
-            if (container == null) {
-                mActivityContainers.removeAt(ndx);
-                continue;
-            }
-            if (container.mParentActivity != parentActivity) {
-                continue;
-            }
-
-            ActivityStack stack = container.mStack;
-            ActivityRecord top = stack.topRunningNonDelayedActivityLocked(null);
-            if (top != null) {
-                // TODO: Make sure the next activity doesn't start up when top is destroyed.
-                stack.destroyActivityLocked(top, true, true, "stack parent destroyed");
-            }
-            mActivityContainers.removeAt(ndx);
-            container.detachLocked();
+        final ArrayList<ActivityContainer> childStacks = parentActivity.mChildContainers;
+        for (int containerNdx = childStacks.size() - 1; containerNdx >= 0; --containerNdx) {
+            ActivityContainer container = childStacks.remove(containerNdx);
+            container.release();
         }
     }
 
     void deleteActivityContainer(IActivityContainer container) {
         ActivityContainer activityContainer = (ActivityContainer)container;
         if (activityContainer != null) {
-            activityContainer.mStack.destroyActivitiesLocked(null, true,
-                    "deleteActivityContainer");
+            activityContainer.mStack.finishAllActivitiesLocked();
             final ActivityRecord parent = activityContainer.mParentActivity;
             if (parent != null) {
                 parent.mChildContainers.remove(activityContainer);
@@ -2244,14 +2215,14 @@
         }
     }
 
-    private int createStackOnDisplay(ActivityRecord parentActivity, int stackId, int displayId) {
+    private int createStackOnDisplay(int stackId, int displayId) {
         ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
         if (activityDisplay == null) {
             return -1;
         }
 
-        ActivityContainer activityContainer =
-                createActivityContainer(parentActivity, stackId, null);
+        ActivityContainer activityContainer = new ActivityContainer(stackId);
+        mActivityContainers.put(stackId, activityContainer);
         activityContainer.attachToDisplayLocked(activityDisplay);
         return stackId;
     }
@@ -2334,9 +2305,9 @@
     }
 
     boolean shutdownLocked(int timeout) {
-        boolean timedout = false;
         goingToSleepLocked();
 
+        boolean timedout = false;
         final long endTime = System.currentTimeMillis() + timeout;
         while (true) {
             boolean cantShutdown = false;
@@ -3030,24 +3001,26 @@
 
     class ActivityContainer extends IActivityContainer.Stub {
         final int mStackId;
-        final IActivityContainerCallback mCallback;
+        IActivityContainerCallback mCallback = null;
         final ActivityStack mStack;
-        final ActivityRecord mParentActivity;
-        final String mIdString;
+        ActivityRecord mParentActivity = null;
+        String mIdString;
 
         boolean mVisible = true;
 
         /** Display this ActivityStack is currently on. Null if not attached to a Display. */
         ActivityDisplay mActivityDisplay;
 
-        ActivityContainer(ActivityRecord parentActivity, int stackId,
-                IActivityContainerCallback callback) {
+        final static int CONTAINER_STATE_HAS_SURFACE = 0;
+        final static int CONTAINER_STATE_NO_SURFACE = 1;
+        final static int CONTAINER_STATE_FINISHING = 2;
+        int mContainerState = CONTAINER_STATE_HAS_SURFACE;
+
+        ActivityContainer(int stackId) {
             synchronized (mService) {
                 mStackId = stackId;
                 mStack = new ActivityStack(this);
-                mParentActivity = parentActivity;
-                mCallback = callback;
-                mIdString = "ActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+                mIdString = "ActivtyContainer{" + mStackId + "}";
                 if (DEBUG_STACK) Slog.d(TAG, "Creating " + this);
             }
         }
@@ -3097,6 +3070,14 @@
             }
         }
 
+        @Override
+        public void release() {
+            mContainerState = CONTAINER_STATE_FINISHING;
+            mStack.finishAllActivitiesLocked();
+            detachLocked();
+            mWindowManager.removeStack(mStackId);
+        }
+
         private void detachLocked() {
             if (DEBUG_STACK) Slog.d(TAG, "detachLocked: " + this + " from display="
                     + mActivityDisplay + " Callers=" + Debug.getCallers(2));
@@ -3110,13 +3091,6 @@
         }
 
         @Override
-        public void detachFromDisplay() {
-            synchronized (mService) {
-                detachLocked();
-            }
-        }
-
-        @Override
         public final int startActivity(Intent intent) {
             mService.enforceNotIsolatedCaller("ActivityContainer.startActivity");
             int userId = mService.handleIncomingUser(Binder.getCallingPid(),
@@ -3149,23 +3123,8 @@
         }
 
         @Override
-        public void attachToSurface(Surface surface, int width, int height, int density) {
+        public void setSurface(Surface surface, int width, int height, int density) {
             mService.enforceNotIsolatedCaller("ActivityContainer.attachToSurface");
-
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService) {
-                    ActivityDisplay activityDisplay =
-                            new ActivityDisplay(surface, width, height, density);
-                    mActivityDisplays.put(activityDisplay.mDisplayId, activityDisplay);
-                    attachToDisplayLocked(activityDisplay);
-                    mStack.resumeTopActivityLocked(null);
-                }
-                if (DEBUG_STACK) Slog.d(TAG, "attachToSurface: " + this + " to display="
-                        + mActivityDisplay);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
         }
 
         ActivityStackSupervisor getOuter() {
@@ -3184,6 +3143,7 @@
             }
         }
 
+        // TODO: Make sure every change to ActivityRecord.visible results in a call to this.
         void setVisible(boolean visible) {
             if (mVisible != visible) {
                 mVisible = visible;
@@ -3194,52 +3154,114 @@
             }
         }
 
+        void setDrawn() {
+        }
+
         @Override
         public String toString() {
             return mIdString + (mActivityDisplay == null ? "N" : "A");
         }
     }
 
+    private class VirtualActivityContainer extends ActivityContainer {
+        Surface mSurface;
+        boolean mDrawn = false;
+
+        VirtualActivityContainer(ActivityRecord parent, IActivityContainerCallback callback) {
+            super(getNextStackId());
+            mParentActivity = parent;
+            mCallback = callback;
+            mContainerState = CONTAINER_STATE_NO_SURFACE;
+            mIdString = "VirtualActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+        }
+
+        @Override
+        public void setSurface(Surface surface, int width, int height, int density) {
+            super.setSurface(surface, width, height, density);
+
+            synchronized (mService) {
+                final long origId = Binder.clearCallingIdentity();
+                try {
+                    setSurfaceLocked(surface, width, height, density);
+                } finally {
+                    Binder.restoreCallingIdentity(origId);
+                }
+            }
+        }
+
+        private void setSurfaceLocked(Surface surface, int width, int height, int density) {
+            if (mContainerState == CONTAINER_STATE_FINISHING) {
+                return;
+            }
+            VirtualActivityDisplay virtualActivityDisplay =
+                    (VirtualActivityDisplay) mActivityDisplay;
+            if (virtualActivityDisplay == null) {
+                virtualActivityDisplay =
+                        new VirtualActivityDisplay(width, height, density);
+                mActivityDisplay = virtualActivityDisplay;
+                mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay);
+                attachToDisplayLocked(virtualActivityDisplay);
+            }
+
+            if (mSurface != null) {
+                mSurface.release();
+            }
+
+            mSurface = surface;
+            if (surface != null) {
+                mStack.resumeTopActivityLocked(null);
+            } else {
+                mContainerState = CONTAINER_STATE_NO_SURFACE;
+                ((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
+//                if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
+//                    mStack.startPausingLocked(false, true);
+//                }
+            }
+
+            setSurfaceIfReady();
+
+            if (DEBUG_STACK) Slog.d(TAG, "setSurface: " + this + " to display="
+                    + virtualActivityDisplay);
+        }
+
+        @Override
+        void setDrawn() {
+            synchronized (mService) {
+                mDrawn = true;
+                setSurfaceIfReady();
+            }
+        }
+
+        private void setSurfaceIfReady() {
+            if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn +
+                    " mContainerState=" + mContainerState + " mSurface=" + mSurface);
+            if (mDrawn && mSurface != null && mContainerState == CONTAINER_STATE_NO_SURFACE) {
+                ((VirtualActivityDisplay) mActivityDisplay).setSurface(mSurface);
+                mContainerState = CONTAINER_STATE_HAS_SURFACE;
+            }
+        }
+    }
+
     /** Exactly one of these classes per Display in the system. Capable of holding zero or more
      * attached {@link ActivityStack}s */
-    final class ActivityDisplay {
+    class ActivityDisplay {
         /** Actual Display this object tracks. */
         int mDisplayId;
         Display mDisplay;
         DisplayInfo mDisplayInfo = new DisplayInfo();
-        Surface mSurface;
 
         /** All of the stacks on this display. Order matters, topmost stack is in front of all other
          * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
         final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
 
-        /** If this display is for an ActivityView then the VirtualDisplay created for it is stored
-         * here. */
-        VirtualDisplay mVirtualDisplay;
+        ActivityDisplay() {
+        }
 
         ActivityDisplay(int displayId) {
             init(mDisplayManager.getDisplay(displayId));
         }
 
-        ActivityDisplay(Surface surface, int width, int height, int density) {
-            DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
-            long ident = Binder.clearCallingIdentity();
-            try {
-                mVirtualDisplay = dm.createVirtualDisplay(mService.mContext,
-                        VIRTUAL_DISPLAY_BASE_NAME, width, height, density, surface,
-                        DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
-                        DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-
-            init(mVirtualDisplay.getDisplay());
-            mSurface = surface;
-
-            mWindowManager.handleDisplayAdded(mDisplayId);
-        }
-
-        private void init(Display display) {
+        void init(Display display) {
             mDisplay = display;
             mDisplayId = display.getDisplayId();
             mDisplay.getDisplayInfo(mDisplayInfo);
@@ -3255,11 +3277,6 @@
             if (DEBUG_STACK) Slog.v(TAG, "detachActivitiesLocked: detaching " + stack
                     + " from displayId=" + mDisplayId);
             mStacks.remove(stack);
-            if (mStacks.isEmpty() && mVirtualDisplay != null) {
-                mVirtualDisplay.release();
-                mVirtualDisplay = null;
-            }
-            mSurface.release();
         }
 
         void getBounds(Point bounds) {
@@ -3270,8 +3287,42 @@
 
         @Override
         public String toString() {
-            return "ActivityDisplay={" + mDisplayId + (mVirtualDisplay == null ? "" : "V")
-                    + " numStacks=" + mStacks.size() + "}";
+            return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
+        }
+    }
+
+    class VirtualActivityDisplay extends ActivityDisplay {
+        VirtualDisplay mVirtualDisplay;
+
+        VirtualActivityDisplay(int width, int height, int density) {
+            DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+            mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, VIRTUAL_DISPLAY_BASE_NAME,
+                    width, height, density, null, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
+                    DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+
+            init(mVirtualDisplay.getDisplay());
+
+            mWindowManager.handleDisplayAdded(mDisplayId);
+        }
+
+        void setSurface(Surface surface) {
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.setSurface(surface);
+            }
+        }
+
+        @Override
+        void detachActivitiesLocked(ActivityStack stack) {
+            super.detachActivitiesLocked(stack);
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.release();
+                mVirtualDisplay = null;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "VirtualActivityDisplay={" + mDisplayId + "}";
         }
     }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 75e5857..6700895 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
@@ -42,6 +43,7 @@
 import android.tv.TvInputService;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Surface;
 
@@ -116,17 +118,19 @@
         UserState userState = getUserStateLocked(userId);
         userState.inputList.clear();
 
+        if (DEBUG) Slog.d(TAG, "buildTvInputList");
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> services = pm.queryIntentServices(
                 new Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
         for (ResolveInfo ri : services) {
             ServiceInfo si = ri.serviceInfo;
             if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
-                Log.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
+                Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
                         + android.Manifest.permission.BIND_TV_INPUT);
                 continue;
             }
             TvInputInfo info = new TvInputInfo(ri);
+            if (DEBUG) Slog.d(TAG, "add " + info.getId());
             userState.inputList.add(info);
         }
     }
@@ -161,7 +165,7 @@
                     try {
                         state.session.release();
                     } catch (RemoteException e) {
-                        Log.e(TAG, "error in release", e);
+                        Slog.e(TAG, "error in release", e);
                     }
                 }
             }
@@ -173,7 +177,7 @@
                     try {
                         serviceState.service.unregisterCallback(serviceState.callback);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "error in unregisterCallback", e);
+                        Slog.e(TAG, "error in unregisterCallback", e);
                     }
                 }
                 serviceState.clients.clear();
@@ -244,7 +248,7 @@
                 return;
             }
             if (DEBUG) {
-                Log.i(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
+                Slog.d(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
                         + ")");
             }
             Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(name);
@@ -255,7 +259,7 @@
             // This means that the service is already connected but its state indicates that we have
             // nothing to do with it. Then, disconnect the service.
             if (DEBUG) {
-                Log.i(TAG, "unbindService(name=" + name.getClassName() + ")");
+                Slog.d(TAG, "unbindService(name=" + name.getClassName() + ")");
             }
             mContext.unbindService(serviceState.connection);
             userState.serviceStateMap.remove(name);
@@ -267,7 +271,7 @@
         final SessionState sessionState =
                 getUserStateLocked(userId).sessionStateMap.get(sessionToken);
         if (DEBUG) {
-            Log.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
+            Slog.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
                     + ")");
         }
         // Set up a callback to send the session token.
@@ -275,7 +279,7 @@
             @Override
             public void onSessionCreated(ITvInputSession session) {
                 if (DEBUG) {
-                    Log.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
+                    Slog.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
                 }
                 synchronized (mLock) {
                     sessionState.session = session;
@@ -295,7 +299,7 @@
         try {
             service.createSession(callback);
         } catch (RemoteException e) {
-            Log.e(TAG, "error in createSession", e);
+            Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
             sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null,
                     sessionState.seq, userId);
@@ -307,7 +311,7 @@
         try {
             client.onSessionCreated(name, sessionToken, seq);
         } catch (RemoteException exception) {
-            Log.e(TAG, "error in onSessionCreated", exception);
+            Slog.e(TAG, "error in onSessionCreated", exception);
         }
 
         if (sessionToken == null) {
@@ -396,7 +400,7 @@
                         try {
                             serviceState.service.registerCallback(serviceState.callback);
                         } catch (RemoteException e) {
-                            Log.e(TAG, "error in registerCallback", e);
+                            Slog.e(TAG, "error in registerCallback", e);
                         }
                     } else {
                         updateServiceConnectionLocked(name, resolvedUserId);
@@ -432,7 +436,7 @@
                     try {
                         serviceState.service.unregisterCallback(serviceState.callback);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "error in unregisterCallback", e);
+                        Slog.e(TAG, "error in unregisterCallback", e);
                     } finally {
                         serviceState.callback = null;
                         updateServiceConnectionLocked(name, resolvedUserId);
@@ -493,7 +497,7 @@
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).release();
                     } catch (RemoteException e) {
-                        Log.e(TAG, "error in release", e);
+                        Slog.e(TAG, "error in release", e);
                     }
 
                     removeSessionStateLocked(sessionToken, resolvedUserId);
@@ -515,7 +519,7 @@
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
                                 surface);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "error in setSurface", e);
+                        Slog.e(TAG, "error in setSurface", e);
                     }
                 }
             } finally {
@@ -535,7 +539,7 @@
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
                                 volume);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "error in setVolume", e);
+                        Slog.e(TAG, "error in setVolume", e);
                     }
                 }
             } finally {
@@ -551,13 +555,10 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    SessionState sessionState = getUserStateLocked(resolvedUserId)
-                            .sessionStateMap.get(sessionToken);
-                    final String serviceName = sessionState.name.getClassName();
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "error in tune", e);
+                        Slog.e(TAG, "error in tune", e);
                         return;
                     }
                 }
@@ -565,6 +566,67 @@
                 Binder.restoreCallingIdentity(identity);
             }
         }
+
+        @Override
+        public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
+                int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "createOverlayView");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .createOverlayView(windowToken, frame);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in createOverlayView", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "relayoutOverlayView");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .relayoutOverlayView(frame);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in relayoutOverlayView", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void removeOverlayView(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "removeOverlayView");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
+                                .removeOverlayView();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in removeOverlayView", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
     }
 
     private static final class UserState {
@@ -621,7 +683,7 @@
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG) {
-                Log.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
+                Slog.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
             }
             synchronized (mLock) {
                 ServiceState serviceState = getServiceStateLocked(name, mUserId);
@@ -633,7 +695,7 @@
                     try {
                         serviceState.service.registerCallback(serviceState.callback);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "error in registerCallback", e);
+                        Slog.e(TAG, "error in registerCallback", e);
                     }
                 }
 
@@ -647,7 +709,7 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             if (DEBUG) {
-                Log.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
+                Slog.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
             }
         }
     }
@@ -663,7 +725,7 @@
         public void onAvailabilityChanged(ComponentName name, boolean isAvailable)
                 throws RemoteException {
             if (DEBUG) {
-                Log.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
+                Slog.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
                         + isAvailable + ")");
             }
             synchronized (mLock) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 912ac4d..f08d69f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -311,6 +311,7 @@
         NetworkStatsService networkStats = null;
         NetworkPolicyManagerService networkPolicy = null;
         ConnectivityService connectivity = null;
+        NetworkScoreService networkScore = null;
         NsdService serviceDiscovery= null;
         IPackageManager pm = null;
         WindowManagerService wm = null;
@@ -643,6 +644,14 @@
                 }
 
                 try {
+                    Slog.i(TAG, "Network Score Service");
+                    networkScore = new NetworkScoreService(context);
+                    ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
+                } catch (Throwable e) {
+                    reportWtf("starting Network Score Service", e);
+                }
+
+                try {
                     Slog.i(TAG, "Network Service Discovery Service");
                     serviceDiscovery = NsdService.create(context);
                     ServiceManager.addService(
@@ -1021,6 +1030,7 @@
         final NetworkStatsService networkStatsF = networkStats;
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
         final ConnectivityService connectivityF = connectivity;
+        final NetworkScoreService networkScoreF = networkScore;
         final DockObserver dockF = dock;
         final WallpaperManagerService wallpaperF = wallpaper;
         final InputMethodManagerService immF = imm;
@@ -1069,6 +1079,11 @@
                     reportWtf("making Battery Service ready", e);
                 }
                 try {
+                    if (networkScoreF != null) networkScoreF.systemReady();
+                } catch (Throwable e) {
+                    reportWtf("making Network Score Service ready", e);
+                }
+                try {
                     if (networkManagementF != null) networkManagementF.systemReady();
                 } catch (Throwable e) {
                     reportWtf("making Network Managment Service ready", e);