Merge "Hack around b/36066697." 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/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/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index dabe608..4400ad33 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -288,6 +288,7 @@
 
         @Override
         public void onActivityDestroyed(Activity activity) {
+            if (activity != getActivity()) return;
             try {
                 mService.stopScan(mRequest, this, getCallingPackage());
             } catch (RemoteException e) {
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/core/java/android/view/View.java b/core/java/android/view/View.java
index 57409d2..4882165 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -26312,9 +26312,6 @@
                 mTooltipInfo.mHideTooltipRunnable = this::hideTooltip;
             }
             mTooltipInfo.mTooltipText = tooltipText;
-            if (mTooltipInfo.mTooltipPopup != null && mTooltipInfo.mTooltipPopup.isShowing()) {
-                mTooltipInfo.mTooltipPopup.updateContent(mTooltipInfo.mTooltipText);
-            }
         }
     }
 
diff --git a/core/java/com/android/internal/view/TooltipPopup.java b/core/java/com/android/internal/view/TooltipPopup.java
index 52357ac..3930214 100644
--- a/core/java/com/android/internal/view/TooltipPopup.java
+++ b/core/java/com/android/internal/view/TooltipPopup.java
@@ -91,10 +91,6 @@
         return mContentView.getParent() != null;
     }
 
-    public void updateContent(CharSequence tooltipText) {
-        mMessageView.setText(tooltipText);
-    }
-
     private void computePosition(View anchorView, int anchorX, int anchorY, boolean fromTouch,
             WindowManager.LayoutParams outParams) {
         outParams.token = anchorView.getWindowToken();
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/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/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/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b3c6ff6..d7b36aa 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -525,12 +525,11 @@
         if (r == null) {
             throw new IllegalArgumentException("Invalid package");
         }
-        LogMaker lm = new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
-                .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
-                .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
-                        group.getId())
-                .setPackageName(pkg);
-        MetricsLogger.action(lm);
+        final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
+        if (!group.equals(oldGroup)) {
+            // will log for new entries as well as name changes
+            MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
+        }
         r.groups.put(group.getId(), group);
         updateConfig();
     }
@@ -558,13 +557,16 @@
         if (existing != null && fromTargetApp) {
             if (existing.isDeleted()) {
                 existing.setDeleted(false);
+
+                // log a resurrected channel as if it's new again
+                MetricsLogger.action(getChannelLog(channel, pkg).setType(
+                        MetricsProto.MetricsEvent.TYPE_OPEN));
             }
 
             existing.setName(channel.getName().toString());
             existing.setDescription(channel.getDescription());
             existing.setBlockableSystem(channel.isBlockableSystem());
 
-            MetricsLogger.action(getChannelLog(channel, pkg));
             updateConfig();
             return;
         }
@@ -622,7 +624,10 @@
             r.showBadge = updatedChannel.canShowBadge();
         }
 
-        MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+        if (!channel.equals(updatedChannel)) {
+            // only log if there are real changes
+            MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+        }
         updateConfig();
     }
 
@@ -1141,6 +1146,14 @@
                         channel.getImportance());
     }
 
+    private LogMaker getChannelGroupLog(String groupId, String pkg) {
+        return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
+                .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+                .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
+                        groupId)
+                .setPackageName(pkg);
+    }
+
     public void updateBadgingEnabled() {
         if (mBadgingEnabled == null) {
             mBadgingEnabled = new SparseBooleanArray();
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",