Merge "remove the lock-use for prunePrintService in case blocking main thread" into oc-dev
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 8eefd25..4966b43 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -382,6 +382,7 @@
                 oldAnims = mWm.getAnimationScales();
                 mWm.setAnimationScale(0, 0.0f);
                 mWm.setAnimationScale(1, 0.0f);
+                mWm.setAnimationScale(2, 0.0f);
             }
 
             // Figure out which component we are tring to do.
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 5fedc9e..c4f17fa 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -214,5 +214,7 @@
     if (mapbase != MAP_FAILED) {
         munmap((void *)mapbase, mapsize);
     }
-    return 0;
+
+    // b/36066697: Avoid running static destructors.
+    _exit(1);
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 24c144c..bc6e9cd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,17 +16,9 @@
 
 package android.app;
 
-import android.graphics.Rect;
-import android.view.ViewRootImpl.ActivityConfigCallback;
-import android.view.autofill.AutofillManager;
-import android.view.autofill.AutofillPopupWindow;
-import android.view.autofill.IAutofillWindowPresenter;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.ToolbarActionBar;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.DecorView;
-import com.android.internal.policy.PhoneWindow;
+import static android.os.Build.VERSION_CODES.O;
+
+import static java.lang.Character.MIN_VALUE;
 
 import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
@@ -62,6 +54,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.media.session.MediaController;
@@ -114,15 +107,26 @@
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewManager;
 import android.view.ViewRootImpl;
+import android.view.ViewRootImpl.ActivityConfigCallback;
 import android.view.Window;
 import android.view.Window.WindowControllerCallback;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillPopupWindow;
+import android.view.autofill.IAutofillWindowPresenter;
 import android.widget.AdapterView;
 import android.widget.Toast;
 import android.widget.Toolbar;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
+import com.android.internal.policy.PhoneWindow;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -131,9 +135,6 @@
 import java.util.HashMap;
 import java.util.List;
 
-import static android.os.Build.VERSION_CODES.O;
-import static java.lang.Character.MIN_VALUE;
-
 /**
  * An activity is a single, focused thing that the user can do.  Almost all
  * activities interact with the user, so the Activity class takes care of
@@ -999,7 +1000,8 @@
         }
         if (savedInstanceState != null) {
             mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false);
-            mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID, View.NO_ID);
+            mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID,
+                    View.LAST_APP_AUTOFILL_ID);
 
             if (mAutoFillResetNeeded) {
                 getAutofillManager().onCreate(savedInstanceState);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 268a105b..1b25e65 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -256,28 +256,34 @@
 
     @Override
     public void setTheme(int resId) {
-        if (mThemeResource != resId) {
-            mThemeResource = resId;
-            initializeTheme();
+        synchronized (mSync) {
+            if (mThemeResource != resId) {
+                mThemeResource = resId;
+                initializeTheme();
+            }
         }
     }
 
     @Override
     public int getThemeResId() {
-        return mThemeResource;
+        synchronized (mSync) {
+            return mThemeResource;
+        }
     }
 
     @Override
     public Resources.Theme getTheme() {
-        if (mTheme != null) {
+        synchronized (mSync) {
+            if (mTheme != null) {
+                return mTheme;
+            }
+
+            mThemeResource = Resources.selectDefaultTheme(mThemeResource,
+                    getOuterContext().getApplicationInfo().targetSdkVersion);
+            initializeTheme();
+
             return mTheme;
         }
-
-        mThemeResource = Resources.selectDefaultTheme(mThemeResource,
-                getOuterContext().getApplicationInfo().targetSdkVersion);
-        initializeTheme();
-
-        return mTheme;
     }
 
     private void initializeTheme() {
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 266fa7e..4e8277c 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1051,6 +1051,9 @@
         public void updateAutofillValue(AutofillValue value) {
             mAutofillValue = value;
             if (value.isText()) {
+                if (mText == null) {
+                    mText = new ViewNodeText();
+                }
                 mText.mText = value.getTextValue();
             }
         }
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 67d56d5..dfd5996 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -208,6 +208,8 @@
             if (wrapper == null) return;
 
             stopAdvertisingSet(wrapper);
+
+            mLegacyAdvertisers.remove(callback);
         }
     }
 
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 9df315b..a90ee93 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -25,7 +25,6 @@
 import android.app.Service;
 import android.app.assist.AssistStructure;
 import android.content.Intent;
-import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
@@ -35,9 +34,6 @@
 
 import com.android.internal.os.SomeArgs;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Top-level service of the current autofill service for a given user.
  *
@@ -192,6 +188,11 @@
      * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
      * to notify the result of the request.
      *
+     * <p><b>NOTE: </b>to retrieve the actual value of the field, the service should call
+     * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls
+     * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no
+     * guarantee such method will return the most recent value of the field.
+     *
      * @param request the {@link SaveRequest request} to handle.
      *        See {@link FillResponse} for examples of multiple-sections requests.
      * @param callback object used to notify the result of the request.
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index caadc36..cb98c88 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -72,6 +72,15 @@
      * Creates a display event receiver.
      *
      * @param looper The looper to use when invoking callbacks.
+     */
+    public DisplayEventReceiver(Looper looper) {
+        this(looper, VSYNC_SOURCE_APP);
+    }
+
+    /**
+     * Creates a display event receiver.
+     *
+     * @param looper The looper to use when invoking callbacks.
      * @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values.
      */
     public DisplayEventReceiver(Looper looper, int vsyncSource) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 5f55bdc..04fa637 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -224,6 +224,14 @@
      * Constant for {@link #getActionMasked}: A movement has happened outside of the
      * normal bounds of the UI element.  This does not provide a full gesture,
      * but only the initial location of the movement/touch.
+     * <p>
+     * Note: Because the location of any event will be outside the
+     * bounds of the view hierarchy, it will not get dispatched to
+     * any children of a ViewGroup by default. Therefore,
+     * movements with ACTION_OUTSIDE should be handled in either the
+     * root {@link View} or in the appropriate {@link Window.Callback}
+     * (e.g. {@link android.app.Activity} or {@link android.app.Dialog}).
+     * </p>
      */
     public static final int ACTION_OUTSIDE          = 4;
 
diff --git a/packages/SystemUI/res/layout/navigation_bar_window.xml b/packages/SystemUI/res/layout/navigation_bar_window.xml
index 051bf3a..6fa46d4 100644
--- a/packages/SystemUI/res/layout/navigation_bar_window.xml
+++ b/packages/SystemUI/res/layout/navigation_bar_window.xml
@@ -16,11 +16,11 @@
 ** limitations under the License.
 */
 -->
-<FrameLayout
+<com.android.systemui.statusbar.phone.NavigationBarFrame
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/navigation_bar_frame"
     android:layout_height="match_parent"
     android:layout_width="match_parent">
 
-</FrameLayout>
+</com.android.systemui.statusbar.phone.NavigationBarFrame>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 2d0fe6f..4d0e60d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -32,6 +32,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.*;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
 
 public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
 
@@ -44,6 +45,7 @@
     private String mAccessibilityClass;
     private boolean mTileState;
     private boolean mCollapsedView;
+    private boolean mClicked;
 
     public QSTileBaseView(Context context, QSIconView icon) {
         this(context, icon, false);
@@ -153,7 +155,11 @@
         setContentDescription(state.contentDescription);
         mAccessibilityClass = state.expandedAccessibilityClassName;
         if (state instanceof QSTile.BooleanState) {
-            mTileState = ((QSTile.BooleanState) state).value;
+            boolean newState = ((BooleanState) state).value;
+            if (mTileState != newState) {
+                mClicked = false;
+                mTileState = newState;
+            }
         }
     }
 
@@ -173,15 +179,22 @@
     }
 
     @Override
+    public boolean performClick() {
+        mClicked = true;
+        return super.performClick();
+    }
+
+    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         if (!TextUtils.isEmpty(mAccessibilityClass)) {
             event.setClassName(mAccessibilityClass);
             if (Switch.class.getName().equals(mAccessibilityClass)) {
+                boolean b = mClicked ? !mTileState : mTileState;
                 String label = getResources()
-                        .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+                        .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
                 event.setContentDescription(label);
-                event.setChecked(!mTileState);
+                event.setChecked(b);
             }
         }
     }
@@ -192,10 +205,11 @@
         if (!TextUtils.isEmpty(mAccessibilityClass)) {
             info.setClassName(mAccessibilityClass);
             if (Switch.class.getName().equals(mAccessibilityClass)) {
+                boolean b = mClicked ? !mTileState : mTileState;
                 String label = getResources()
-                        .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+                        .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
                 info.setText(label);
-                info.setChecked(mTileState);
+                info.setChecked(b);
                 info.setCheckable(true);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 76f6e7d..f26afcc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -32,17 +32,16 @@
 import com.android.settingslib.wifi.AccessPoint;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.R.string;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
-import com.android.systemui.qs.QSHost;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.QSDetailItems;
+import com.android.systemui.qs.QSDetailItems.Item;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.SignalTileView;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -180,7 +179,7 @@
                 minimalContentDescription.append(removeDoubleQuotes(cb.enabledDesc));
             }
         }
-        state.contentDescription = minimalContentDescription;
+        state.contentDescription = minimalContentDescription.toString();
         state.dualLabelContentDescription = r.getString(
                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
         state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java
new file mode 100644
index 0000000..741f783
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.systemui.statusbar.phone;
+
+import static android.view.MotionEvent.ACTION_OUTSIDE;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+import com.android.systemui.statusbar.policy.DeadZone;
+
+public class NavigationBarFrame extends FrameLayout {
+
+    private DeadZone mDeadZone = null;
+
+    public NavigationBarFrame(@NonNull Context context) {
+        super(context);
+    }
+
+    public NavigationBarFrame(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public NavigationBarFrame(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void setDeadZone(@NonNull DeadZone deadZone) {
+        mDeadZone = deadZone;
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        if (event.getAction() == ACTION_OUTSIDE) {
+            if (mDeadZone != null) {
+                return mDeadZone.onTouchEvent(event);
+            }
+        }
+        return super.dispatchTouchEvent(event);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 8d9d461..0557cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -250,9 +250,6 @@
         if (mGestureHelper.onTouchEvent(event)) {
             return true;
         }
-        if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-            mDeadZone.poke(event);
-        }
         return super.onTouchEvent(event);
     }
 
@@ -614,9 +611,8 @@
     public void reorient() {
         updateCurrentView();
 
-        getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
-
         mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
+        ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
         mDeadZone.setDisplayRotation(mCurrentRotation);
 
         // force the low profile & disabled states into compliance
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index 4c879c6..13ee23f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -127,6 +127,7 @@
         final int action = event.getAction();
         if (action == MotionEvent.ACTION_OUTSIDE) {
             poke(event);
+            return true;
         } else if (action == MotionEvent.ACTION_DOWN) {
             if (DEBUG) {
                 Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
@@ -158,7 +159,7 @@
         return false;
     }
 
-    public void poke(MotionEvent event) {
+    private void poke(MotionEvent event) {
         mLastPokeTime = event.getEventTime();
         if (DEBUG)
             Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index a120cec..4cc83f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -14,7 +14,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -33,17 +32,13 @@
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.utils.leaks.BaseLeakChecker;
 
 import android.testing.TestableLooper.RunWithLooper;
-import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -54,6 +49,10 @@
         super(NavigationBarFragment.class);
     }
 
+    protected void createRootView() {
+        mView = new NavigationBarFrame(mContext);
+    }
+
     @Before
     public void setup() {
         mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7de56fa..f379791 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2257,6 +2257,31 @@
         return null;
     }
 
+    /**
+     * Get next valid stack for launching provided activity in the system. This will search across
+     * displays and stacks in last-focused order for a focusable and visible stack, except those
+     * that are on a currently focused display.
+     *
+     * @param r The activity that is being launched.
+     * @param currentFocus The display that previously had focus and thus needs to be ignored when
+     *                     searching for the next candidate.
+     * @return Next valid {@link ActivityStack}, null if not found.
+     */
+    ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
+        mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+        for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+            final int displayId = mTmpOrderedDisplayIds.get(i);
+            if (displayId == currentFocus) {
+                continue;
+            }
+            final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
     ActivityRecord getHomeActivity() {
         return getHomeActivityForUser(mCurrentUser);
     }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index d74d1d6..c7ee149 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -2027,7 +2027,18 @@
             return mSupervisor.mFocusedStack;
         }
 
-        if (mSourceDisplayId == DEFAULT_DISPLAY) {
+        if (mSourceDisplayId != DEFAULT_DISPLAY) {
+            // Try to put the activity in a stack on a secondary display.
+            stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
+            if (stack == null) {
+                // If source display is not suitable - look for topmost valid stack in the system.
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Can't launch on mSourceDisplayId=" + mSourceDisplayId
+                                + ", looking on all displays.");
+                stack = mSupervisor.getNextValidLaunchStackLocked(r, mSourceDisplayId);
+            }
+        }
+        if (stack == null) {
             // We first try to put the task in the first dynamic stack on home display.
             final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
             for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
@@ -2043,8 +2054,6 @@
                     bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
                             FULLSCREEN_WORKSPACE_STACK_ID;
             stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
-        } else {
-            stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
         }
         if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
                 + r + " stackId=" + stack.mStackId);
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 5d3f6f7..107475f 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -438,7 +438,21 @@
             switch (message.what) {
                 case MSG_TIMEOUT:
                     synchronized (mLock) {
-                        handleOpTimeoutLocked();
+                        if (message.obj == mRunningCallback) {
+                            handleOpTimeoutLocked();
+                        } else {
+                            JobCallback jc = (JobCallback)message.obj;
+                            StringBuilder sb = new StringBuilder(128);
+                            sb.append("Ignoring timeout of no longer active job");
+                            if (jc.mStoppedReason != null) {
+                                sb.append(", stopped ");
+                                TimeUtils.formatDuration(SystemClock.elapsedRealtime()
+                                        - jc.mStoppedTime, sb);
+                                sb.append(" because: ");
+                                sb.append(jc.mStoppedReason);
+                            }
+                            Slog.w(TAG, sb.toString());
+                        }
                     }
                     break;
                 default:
@@ -621,7 +635,7 @@
     private void handleOpTimeoutLocked() {
         switch (mVerb) {
             case VERB_BINDING:
-                Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
+                Slog.w(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
                         ", dropping.");
                 closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
                 break;
@@ -629,26 +643,28 @@
                 // Client unresponsive - wedged or failed to respond in time. We don't really
                 // know what happened so let's log it and notify the JobScheduler
                 // FINISHED/NO-RETRY.
-                Slog.e(TAG, "No response from client for onStartJob '" +
-                        mRunningJob.toShortString());
+                Slog.w(TAG, "No response from client for onStartJob " +
+                        mRunningJob != null ? mRunningJob.toShortString() : "<null>");
                 closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
                 break;
             case VERB_STOPPING:
                 // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
-                Slog.e(TAG, "No response from client for onStopJob, '" +
-                        mRunningJob.toShortString());
+                Slog.w(TAG, "No response from client for onStopJob " +
+                        mRunningJob != null ? mRunningJob.toShortString() : "<null>");
                 closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
                 break;
             case VERB_EXECUTING:
                 // Not an error - client ran out of time.
-                Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
-                        " sending onStop. "  + mRunningJob.toShortString());
+                Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
+                        "sending onStop: "  +
+                        mRunningJob != null ? mRunningJob.toShortString() : "<null>");
                 mParams.setStopReason(JobParameters.REASON_TIMEOUT);
                 sendStopMessageLocked("timeout while executing");
                 break;
             default:
                 Slog.e(TAG, "Handling timeout for an invalid job state: " +
-                        mRunningJob.toShortString() + ", dropping.");
+                        mRunningJob != null ? mRunningJob.toShortString() : "<null>"
+                        + ", dropping.");
                 closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout");
         }
     }
@@ -749,7 +765,7 @@
                     mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
                     mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
         }
-        Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
+        Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
         mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
         mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
     }
diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java
index 32ee091..f1e4d21 100644
--- a/tests/testables/src/android/testing/BaseFragmentTest.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -50,7 +50,7 @@
     private static final int VIEW_ID = 42;
     private final Class<? extends Fragment> mCls;
     private Handler mHandler;
-    private FrameLayout mView;
+    protected FrameLayout mView;
     protected FragmentController mFragments;
     protected Fragment mFragment;
 
@@ -61,9 +61,13 @@
         mCls = cls;
     }
 
+    protected void createRootView() {
+        mView = new FrameLayout(mContext);
+    }
+
     @Before
     public void setupFragment() throws Exception {
-        mView = new FrameLayout(mContext);
+        createRootView();
         mView.setId(VIEW_ID);
 
         assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",