Merge "AAPT2: Fix resource type id symbol merging"
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 8e659e0..f53ca94 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -31,6 +31,8 @@
  */
 public class OobData implements Parcelable {
     private byte[] securityManagerTk;
+    private byte[] leSecureConnectionsConfirmation;
+    private byte[] leSecureConnectionsRandom;
 
     public byte[] getSecurityManagerTk() {
         return securityManagerTk;
@@ -45,10 +47,28 @@
         this.securityManagerTk = securityManagerTk;
     }
 
+    public byte[] getLeSecureConnectionsConfirmation() {
+        return leSecureConnectionsConfirmation;
+    }
+
+    public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) {
+        this.leSecureConnectionsConfirmation = leSecureConnectionsConfirmation;
+    }
+
+    public byte[] getLeSecureConnectionsRandom() {
+        return leSecureConnectionsRandom;
+    }
+
+    public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) {
+        this.leSecureConnectionsRandom = leSecureConnectionsRandom;
+    }
+
     public OobData() { }
 
     private OobData(Parcel in) {
         securityManagerTk = in.createByteArray();
+        leSecureConnectionsConfirmation = in.createByteArray();
+        leSecureConnectionsRandom = in.createByteArray();
     }
 
     public int describeContents() {
@@ -58,6 +78,8 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeByteArray(securityManagerTk);
+        out.writeByteArray(leSecureConnectionsConfirmation);
+        out.writeByteArray(leSecureConnectionsRandom);
     }
 
     public static final Parcelable.Creator<OobData> CREATOR
diff --git a/core/java/android/view/SoundEffectConstants.java b/core/java/android/view/SoundEffectConstants.java
index 4a77af4..8d891bb 100644
--- a/core/java/android/view/SoundEffectConstants.java
+++ b/core/java/android/view/SoundEffectConstants.java
@@ -37,6 +37,8 @@
      *     or {@link View#FOCUS_BACKWARD}
 
      * @return The appropriate sonification constant.
+     * @throws {@link IllegalArgumentException} when the passed direction is not one of the
+     *     documented values.
      */
     public static int getContantForFocusDirection(int direction) {
         switch (direction) {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 745da51..bb77100 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -229,7 +229,6 @@
     @Override
     protected void destroyHardwareResources() {
         destroyHardwareLayer();
-        mUpdateSurface = mSurface != null;
     }
 
     private void destroyHardwareLayer() {
@@ -376,17 +375,17 @@
             }
 
             mLayer = mAttachInfo.mThreadedRenderer.createTextureLayer();
-            if (!mUpdateSurface) {
+            boolean createNewSurface = (mSurface == null);
+            if (createNewSurface) {
                 // Create a new SurfaceTexture for the layer.
                 mSurface = new SurfaceTexture(false);
                 mLayer.setSurfaceTexture(mSurface);
                 nCreateNativeWindow(mSurface);
             }
             mSurface.setDefaultBufferSize(getWidth(), getHeight());
-
             mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
 
-            if (mListener != null && !mUpdateSurface) {
+            if (mListener != null && createNewSurface) {
                 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
             }
             mLayer.setLayerPaint(mLayerPaint);
@@ -745,9 +744,11 @@
                     "released SurfaceTexture");
         }
         if (mSurface != null) {
+            nDestroyNativeWindow();
             mSurface.release();
         }
         mSurface = surfaceTexture;
+        nCreateNativeWindow(mSurface);
 
         /*
          * If the view is visible and we already made a layer, update the
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c37cf56..e60d918 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20674,6 +20674,9 @@
             Log.w(VIEW_LOG_TAG, "startDragAndDrop called on a detached view.");
             return false;
         }
+
+        data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
+
         boolean okay = false;
 
         Point shadowSize = new Point();
@@ -23864,7 +23867,7 @@
      * on the screen.
      */
     private boolean shouldDrawRoundScrollbar() {
-        if (!mResources.getConfiguration().isScreenRound()) {
+        if (!mResources.getConfiguration().isScreenRound() || mAttachInfo == null) {
             return false;
         }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2bc1d74..757727b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5539,6 +5539,10 @@
                 // Remember who the current drag target is pre-dispatch
                 final View prevDragView = mCurrentDragView;
 
+                if (what == DragEvent.ACTION_DROP) {
+                    event.getClipData().prepareToEnterProcess();
+                }
+
                 // Now dispatch the drag/drop event
                 boolean result = mView.dispatchDragEvent(event);
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 77e8c0e9..e37cf96 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -625,8 +625,12 @@
     final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
 
     InputMethodManager(Looper looper) throws ServiceNotFoundException {
-        mService = IInputMethodManager.Stub.asInterface(
-                ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
+        this(IInputMethodManager.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)), looper);
+    }
+
+    InputMethodManager(IInputMethodManager service, Looper looper) {
+        mService = service;
         mMainLooper = looper;
         mH = new H(looper);
         mIInputContext = new ControlledInputConnectionWrapper(looper,
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 23e291f..ffa7107 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -58,21 +58,19 @@
 #endif
 
 // For both of these, err should be in the errno range (positive), not a status_t (negative)
-
-static void signalExceptionForPriorityError(JNIEnv* env, int err)
-{
+static void signalExceptionForError(JNIEnv* env, int err, int tid) {
     switch (err) {
         case EINVAL:
-            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                                 "Invalid argument: %d", tid);
             break;
         case ESRCH:
-            jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist");
+            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                                 "Given thread %d does not exist", tid);
             break;
         case EPERM:
-            jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread");
-            break;
-        case EACCES:
-            jniThrowException(env, "java/lang/SecurityException", "No permission to set to given priority");
+            jniThrowExceptionFmt(env, "java/lang/SecurityException",
+                                 "No permission to modify given thread %d", tid);
             break;
         default:
             jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
@@ -80,23 +78,27 @@
     }
 }
 
-static void signalExceptionForGroupError(JNIEnv* env, int err)
-{
+static void signalExceptionForPriorityError(JNIEnv* env, int err, int tid) {
     switch (err) {
-        case EINVAL:
-            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-            break;
-        case ESRCH:
-            jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist");
-            break;
-        case EPERM:
-            jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread");
-            break;
         case EACCES:
-            jniThrowException(env, "java/lang/SecurityException", "No permission to set to given group");
+            jniThrowExceptionFmt(env, "java/lang/SecurityException",
+                                 "No permission to set the priority of %d", tid);
             break;
         default:
-            jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+            signalExceptionForError(env, err, tid);
+            break;
+    }
+
+}
+
+static void signalExceptionForGroupError(JNIEnv* env, int err, int tid) {
+    switch (err) {
+        case EACCES:
+            jniThrowExceptionFmt(env, "java/lang/SecurityException",
+                                 "No permission to set the group of %d", tid);
+            break;
+        default:
+            signalExceptionForError(env, err, tid);
             break;
     }
 }
@@ -171,7 +173,7 @@
     SchedPolicy sp = (SchedPolicy) grp;
     int res = set_sched_policy(tid, sp);
     if (res != NO_ERROR) {
-        signalExceptionForGroupError(env, -res);
+        signalExceptionForGroupError(env, -res, tid);
     }
 }
 
@@ -183,7 +185,7 @@
     struct dirent *de;
 
     if ((grp == SP_FOREGROUND) || (grp > SP_MAX)) {
-        signalExceptionForGroupError(env, EINVAL);
+        signalExceptionForGroupError(env, EINVAL, pid);
         return;
     }
 
@@ -219,7 +221,7 @@
     if (!(d = opendir(proc_path))) {
         // If the process exited on us, don't generate an exception
         if (errno != ENOENT)
-            signalExceptionForGroupError(env, errno);
+            signalExceptionForGroupError(env, errno, pid);
         return;
     }
 
@@ -254,7 +256,7 @@
 #ifdef ENABLE_CPUSETS
                 int err = set_cpuset_policy(t_pid, sp);
                 if (err != NO_ERROR) {
-                    signalExceptionForGroupError(env, -err);
+                    signalExceptionForGroupError(env, -err, t_pid);
                     break;
                 }
 #endif
@@ -266,14 +268,14 @@
         // set both cpuset and cgroup for general threads
         err = set_cpuset_policy(t_pid, sp);
         if (err != NO_ERROR) {
-            signalExceptionForGroupError(env, -err);
+            signalExceptionForGroupError(env, -err, t_pid);
             break;
         }
 #endif
 
         err = set_sched_policy(t_pid, sp);
         if (err != NO_ERROR) {
-            signalExceptionForGroupError(env, -err);
+            signalExceptionForGroupError(env, -err, t_pid);
             break;
         }
 
@@ -285,7 +287,7 @@
 {
     SchedPolicy sp;
     if (get_sched_policy(pid, &sp) != 0) {
-        signalExceptionForGroupError(env, errno);
+        signalExceptionForGroupError(env, errno, pid);
     }
     return (int) sp;
 }
@@ -400,7 +402,7 @@
     jintArray cpus;
     int pid = getpid();
     if (get_sched_policy(pid, &sp) != 0) {
-        signalExceptionForGroupError(env, errno);
+        signalExceptionForGroupError(env, errno, pid);
         return NULL;
     }
     get_exclusive_cpuset_cores(sp, &cpu_set);
@@ -449,10 +451,10 @@
     errno = 0;
     policy = sched_getscheduler(tid);
     if (errno != 0) {
-        signalExceptionForPriorityError(env, errno);
+        signalExceptionForPriorityError(env, errno, tid);
     }
 #else
-    signalExceptionForPriorityError(env, ENOSYS);
+    signalExceptionForPriorityError(env, ENOSYS, tid);
 #endif
     return policy;
 }
@@ -466,10 +468,10 @@
     param.sched_priority = pri;
     int rc = sched_setscheduler(tid, policy, &param);
     if (rc) {
-        signalExceptionForPriorityError(env, errno);
+        signalExceptionForPriorityError(env, errno, tid);
     }
 #else
-    signalExceptionForPriorityError(env, ENOSYS);
+    signalExceptionForPriorityError(env, ENOSYS, tid);
 #endif
 }
 
@@ -494,9 +496,9 @@
     int rc = androidSetThreadPriority(pid, pri);
     if (rc != 0) {
         if (rc == INVALID_OPERATION) {
-            signalExceptionForPriorityError(env, errno);
+            signalExceptionForPriorityError(env, errno, pid);
         } else {
-            signalExceptionForGroupError(env, errno);
+            signalExceptionForGroupError(env, errno, pid);
         }
     }
 
@@ -516,7 +518,7 @@
     errno = 0;
     jint pri = getpriority(PRIO_PROCESS, pid);
     if (errno != 0) {
-        signalExceptionForPriorityError(env, errno);
+        signalExceptionForPriorityError(env, errno, pid);
     }
     //ALOGI("Returning priority of %" PRId32 ": %" PRId32 "\n", pid, pri);
     return pri;
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index a8205c8..c5d8767 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -101,7 +101,7 @@
 
     const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
     const LsaVector<FunctorContainer>& getFunctors() const { return functors; }
-    const LsaVector<VectorDrawableRoot*>& getVectorDrawables() { return vectorDrawables; }
+    const LsaVector<VectorDrawableRoot*>& getVectorDrawables() const { return vectorDrawables; }
 
     size_t addChild(NodeOpType* childOp);
 
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index fd3cf37..0d90afa 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <gtest/gtest.h>
+#include <VectorDrawable.h>
 
 #include "AnimationContext.h"
 #include "DamageAccumulator.h"
@@ -132,3 +133,38 @@
 
     canvasContext->destroy(nullptr);
 }
+
+RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
+
+    VectorDrawable::Group* group = new VectorDrawable::Group();
+    VectorDrawableRoot* vectorDrawable = new VectorDrawableRoot(group);
+    auto rootNode = TestUtils::createNode(0, 0, 200, 400,
+            [&](RenderProperties& props, Canvas& canvas) {
+        canvas.drawVectorDrawable(vectorDrawable);
+    });
+    ContextFactory contextFactory;
+    std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+            renderThread, false, rootNode.get(), &contextFactory));
+    TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
+    DamageAccumulator damageAccumulator;
+    LayerUpdateQueue layerUpdateQueue;
+    info.damageAccumulator = &damageAccumulator;
+    info.layerUpdateQueue = &layerUpdateQueue;
+    info.observer = nullptr;
+
+    // Put node on HW layer
+    rootNode->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+
+    TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
+    rootNode->prepareTree(info);
+
+    // Check that the VD is in the dislay list, and the layer update queue contains the correct
+    // damage rect.
+    EXPECT_FALSE(rootNode->getDisplayList()->getVectorDrawables().empty());
+    EXPECT_FALSE(info.layerUpdateQueue->entries().empty());
+    EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode);
+    EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
+
+    delete vectorDrawable;
+    canvasContext->destroy(nullptr);
+}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 2c4c6e5..d413083 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -25,6 +25,7 @@
 import android.system.OsConstants;
 import android.util.Log;
 import android.util.Pair;
+import android.annotation.IntDef;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
@@ -55,6 +56,8 @@
 import java.util.TimeZone;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
@@ -1166,16 +1169,24 @@
     // The following values are used for indicating pointers to the other Image File Directories.
 
     // Indices of Exif Ifd tag groups
-    private static final int IFD_TIFF_HINT = 0;
-    private static final int IFD_EXIF_HINT = 1;
-    private static final int IFD_GPS_HINT = 2;
-    private static final int IFD_INTEROPERABILITY_HINT = 3;
-    private static final int IFD_THUMBNAIL_HINT = 4;
-    private static final int IFD_PREVIEW_HINT = 5;
-    private static final int ORF_MAKER_NOTE_HINT = 6;
-    private static final int ORF_CAMERA_SETTINGS_HINT = 7;
-    private static final int ORF_IMAGE_PROCESSING_HINT = 8;
-    private static final int PEF_HINT = 9;
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({IFD_TYPE_PRIMARY, IFD_TYPE_EXIF, IFD_TYPE_GPS, IFD_TYPE_INTEROPERABILITY,
+            IFD_TYPE_THUMBNAIL, IFD_TYPE_PREVIEW, IFD_TYPE_ORF_MAKER_NOTE,
+            IFD_TYPE_ORF_CAMERA_SETTINGS, IFD_TYPE_ORF_IMAGE_PROCESSING, IFD_TYPE_PEF})
+    public @interface IfdType {}
+
+    private static final int IFD_TYPE_PRIMARY = 0;
+    private static final int IFD_TYPE_EXIF = 1;
+    private static final int IFD_TYPE_GPS = 2;
+    private static final int IFD_TYPE_INTEROPERABILITY = 3;
+    private static final int IFD_TYPE_THUMBNAIL = 4;
+    private static final int IFD_TYPE_PREVIEW = 5;
+    private static final int IFD_TYPE_ORF_MAKER_NOTE = 6;
+    private static final int IFD_TYPE_ORF_CAMERA_SETTINGS = 7;
+    private static final int IFD_TYPE_ORF_IMAGE_PROCESSING = 8;
+    private static final int IFD_TYPE_PEF = 9;
+
     // List of Exif tag groups
     private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
             IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS,
@@ -1191,11 +1202,7 @@
             new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_BYTE),
             new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
     };
-    // List of indices of the indicated tag groups according to the EXIF_POINTER_TAGS
-    private static final int[] EXIF_POINTER_TAG_HINTS = new int[] {
-            IFD_PREVIEW_HINT, IFD_EXIF_HINT, IFD_GPS_HINT, IFD_INTEROPERABILITY_HINT,
-            ORF_CAMERA_SETTINGS_HINT, ORF_IMAGE_PROCESSING_HINT
-    };
+
     // Tags for indicating the thumbnail offset and length
     private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
             new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
@@ -1209,6 +1216,8 @@
     private static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList(
             TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
             TAG_GPS_TIMESTAMP));
+    // Mappings from tag number to IFD type for pointer tags.
+    private static final HashMap sExifPointerTagMap = new HashMap();
 
     // See JPEG File Interchange Format Version 1.02.
     // The following values are defined for handling JPEG streams. In this implementation, we are
@@ -1260,14 +1269,22 @@
         sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
 
         // Build up the hash tables to look up Exif tags for reading Exif tags.
-        for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
-            sExifTagMapsForReading[hint] = new HashMap();
-            sExifTagMapsForWriting[hint] = new HashMap();
-            for (ExifTag tag : EXIF_TAGS[hint]) {
-                sExifTagMapsForReading[hint].put(tag.number, tag);
-                sExifTagMapsForWriting[hint].put(tag.name, tag);
+        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
+            sExifTagMapsForReading[ifdType] = new HashMap();
+            sExifTagMapsForWriting[ifdType] = new HashMap();
+            for (ExifTag tag : EXIF_TAGS[ifdType]) {
+                sExifTagMapsForReading[ifdType].put(tag.number, tag);
+                sExifTagMapsForWriting[ifdType].put(tag.name, tag);
             }
         }
+
+        // Build up the hash table to look up Exif pointer tags.
+        sExifPointerTagMap.put(EXIF_POINTER_TAGS[0].number, IFD_TYPE_PREVIEW); // 330
+        sExifPointerTagMap.put(EXIF_POINTER_TAGS[1].number, IFD_TYPE_EXIF); // 34665
+        sExifPointerTagMap.put(EXIF_POINTER_TAGS[2].number, IFD_TYPE_GPS); // 34853
+        sExifPointerTagMap.put(EXIF_POINTER_TAGS[3].number, IFD_TYPE_INTEROPERABILITY); // 40965
+        sExifPointerTagMap.put(EXIF_POINTER_TAGS[4].number, IFD_TYPE_ORF_CAMERA_SETTINGS); // 8224
+        sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256
     }
 
     private final String mFilename;
@@ -1503,7 +1520,7 @@
         }
 
         for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
-            if (i == IFD_THUMBNAIL_HINT && !mHasThumbnail) {
+            if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) {
                 continue;
             }
             final Object obj = sExifTagMapsForWriting[i].get(tag);
@@ -1662,7 +1679,7 @@
 
             switch (mMimeType) {
                 case IMAGE_TYPE_JPEG: {
-                    getJpegAttributes(in, 0, IFD_TIFF_HINT); // 0 is offset
+                    getJpegAttributes(in, 0, IFD_TYPE_PRIMARY); // 0 is offset
                     break;
                 }
                 case IMAGE_TYPE_RAF: {
@@ -1894,9 +1911,9 @@
             }
 
             ExifAttribute imageLengthAttribute =
-                    (ExifAttribute) mAttributes[IFD_THUMBNAIL_HINT].get(TAG_IMAGE_LENGTH);
+                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
             ExifAttribute imageWidthAttribute =
-                    (ExifAttribute) mAttributes[IFD_THUMBNAIL_HINT].get(TAG_IMAGE_WIDTH);
+                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
             if (imageLengthAttribute != null && imageWidthAttribute != null) {
                 int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
                 int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
@@ -2165,9 +2182,9 @@
      *
      * @param inputStream The input stream that starts with the JPEG data.
      * @param jpegOffset The offset value in input stream for JPEG data.
-     * @param imageTypes The image type from which to retrieve metadata. Use IFD_TIFF_HINT for
-     *                   primary image, IFD_PREVIEW_HINT for preview image, and
-     *                   IFD_THUMBNAIL_HINT for thumbnail image.
+     * @param imageTypes The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for
+     *                   primary image, IFD_TYPE_PREVIEW for preview image, and
+     *                   IFD_TYPE_THUMBNAIL for thumbnail image.
      * @throws IOException If the data contains invalid JPEG markers, offsets, or length values.
      */
     private void getJpegAttributes(InputStream inputStream, int jpegOffset, int imageType)
@@ -2268,7 +2285,7 @@
                     }
                     length = 0;
                     if (getAttribute(TAG_USER_COMMENT) == null) {
-                        mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT, ExifAttribute.createString(
+                        mAttributes[IFD_TYPE_EXIF].put(TAG_USER_COMMENT, ExifAttribute.createString(
                                 new String(bytes, ASCII)));
                     }
                     break;
@@ -2328,12 +2345,12 @@
         parseTiffHeaders(dataInputStream, exifBytes.length);
 
         // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
-        readImageFileDirectory(dataInputStream, IFD_TIFF_HINT);
+        readImageFileDirectory(dataInputStream, IFD_TYPE_PRIMARY);
 
         // Update ImageLength/Width tags for all image data.
-        updateImageSizeValues(in, IFD_TIFF_HINT);
-        updateImageSizeValues(in, IFD_PREVIEW_HINT);
-        updateImageSizeValues(in, IFD_THUMBNAIL_HINT);
+        updateImageSizeValues(in, IFD_TYPE_PRIMARY);
+        updateImageSizeValues(in, IFD_TYPE_PREVIEW);
+        updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
 
         // Check if each image data is in valid position.
         validateImages(in);
@@ -2342,7 +2359,7 @@
             // PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
             // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData()
             ExifAttribute makerNoteAttribute =
-                    (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE);
+                    (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
             if (makerNoteAttribute != null) {
                 // Create an ordered DataInputStream for MakerNote
                 ByteOrderAwarenessDataInputStream makerNoteDataInputStream =
@@ -2353,13 +2370,13 @@
                 makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE);
 
                 // Read IFD data from MakerNote
-                readImageFileDirectory(makerNoteDataInputStream, PEF_HINT);
+                readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_PEF);
 
                 // Update ColorSpace tag
                 ExifAttribute colorSpaceAttribute =
-                        (ExifAttribute) mAttributes[PEF_HINT].get(TAG_COLOR_SPACE);
+                        (ExifAttribute) mAttributes[IFD_TYPE_PEF].get(TAG_COLOR_SPACE);
                 if (colorSpaceAttribute != null) {
-                    mAttributes[IFD_EXIF_HINT].put(TAG_COLOR_SPACE, colorSpaceAttribute);
+                    mAttributes[IFD_TYPE_EXIF].put(TAG_COLOR_SPACE, colorSpaceAttribute);
                 }
             }
         }
@@ -2392,7 +2409,7 @@
         in.reset();
 
         // Retrieve JPEG image metadata
-        getJpegAttributes(in, rafJpegOffset, IFD_PREVIEW_HINT);
+        getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW);
 
         // Skip to CFA header offset.
         // A while loop is used because the skip method may not be able to skip the requested amount
@@ -2429,8 +2446,8 @@
                         ExifAttribute.createUShort(imageLength, mExifByteOrder);
                 ExifAttribute imageWidthAttribute =
                         ExifAttribute.createUShort(imageWidth, mExifByteOrder);
-                mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
-                mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
+                mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
+                mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
                 if (DEBUG) {
                     Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth);
                 }
@@ -2459,7 +2476,7 @@
         // proprietary tags and therefore does not have offical documentation
         // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html
         ExifAttribute makerNoteAttribute =
-                (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE);
+                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
         if (makerNoteAttribute != null) {
             // Create an ordered DataInputStream for MakerNote
             ByteOrderAwarenessDataInputStream makerNoteDataInputStream =
@@ -2481,18 +2498,18 @@
             }
 
             // Read IFD data from MakerNote
-            readImageFileDirectory(makerNoteDataInputStream, ORF_MAKER_NOTE_HINT);
+            readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE);
 
             // Retrieve & update preview image offset & length values
             ExifAttribute imageLengthAttribute = (ExifAttribute)
-                    mAttributes[ORF_CAMERA_SETTINGS_HINT].get(TAG_ORF_PREVIEW_IMAGE_START);
+                    mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START);
             ExifAttribute bitsPerSampleAttribute = (ExifAttribute)
-                    mAttributes[ORF_CAMERA_SETTINGS_HINT].get(TAG_ORF_PREVIEW_IMAGE_LENGTH);
+                    mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH);
 
             if (imageLengthAttribute != null && bitsPerSampleAttribute != null) {
-                mAttributes[IFD_PREVIEW_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT,
+                mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT,
                         imageLengthAttribute);
-                mAttributes[IFD_PREVIEW_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+                mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
                         bitsPerSampleAttribute);
             }
 
@@ -2500,7 +2517,7 @@
             // Retrieve primary image length & width values
             // See piex.cc GetOlympusPreviewImage()
             ExifAttribute aspectFrameAttribute = (ExifAttribute)
-                    mAttributes[ORF_IMAGE_PROCESSING_HINT].get(TAG_ORF_ASPECT_FRAME);
+                    mAttributes[IFD_TYPE_ORF_IMAGE_PROCESSING].get(TAG_ORF_ASPECT_FRAME);
             if (aspectFrameAttribute != null) {
                 int[] aspectFrameValues = new int[4];
                 aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder);
@@ -2519,8 +2536,8 @@
                     ExifAttribute primaryImageLengthAttribute =
                             ExifAttribute.createUShort(primaryImageLength, mExifByteOrder);
 
-                    mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute);
-                    mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute);
+                    mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute);
+                    mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute);
                 }
             }
         }
@@ -2535,47 +2552,19 @@
 
         // Retrieve preview and/or thumbnail image data
         ExifAttribute jpgFromRawAttribute =
-                (ExifAttribute) mAttributes[IFD_TIFF_HINT].get(TAG_RW2_JPG_FROM_RAW);
+                (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_JPG_FROM_RAW);
         if (jpgFromRawAttribute != null) {
-            getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_PREVIEW_HINT);
+            getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_TYPE_PREVIEW);
         }
 
         // Set ISO tag value if necessary
         ExifAttribute rw2IsoAttribute =
-                (ExifAttribute) mAttributes[IFD_TIFF_HINT].get(TAG_RW2_ISO);
+                (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_ISO);
         ExifAttribute exifIsoAttribute =
-                (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_ISO_SPEED_RATINGS);
+                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_ISO_SPEED_RATINGS);
         if (rw2IsoAttribute != null && exifIsoAttribute == null) {
             // Place this attribute only if it doesn't exist
-            mAttributes[IFD_EXIF_HINT].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute);
-        }
-    }
-
-    // PEF is TIFF-based and contains 3 IFDs. It also contains a MakerNote data, which contains the
-    // ColorSpace tag data.
-    // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData()
-    private void getPefAttributes(InputStream in) throws IOException {
-        // Retrieve ColorSpace tag
-        ExifAttribute makerNoteAttribute =
-                (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE);
-        if (makerNoteAttribute != null) {
-            // Create an ordered DataInputStream for MakerNote
-            ByteOrderAwarenessDataInputStream makerNoteDataInputStream =
-                    new ByteOrderAwarenessDataInputStream(makerNoteAttribute.bytes);
-            makerNoteDataInputStream.setByteOrder(mExifByteOrder);
-
-            // Seek to MakerNote data
-            makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE);
-
-            // Read IFD data from MakerNote
-            readImageFileDirectory(makerNoteDataInputStream, PEF_HINT);
-
-            // Update ColorSpace tag
-            ExifAttribute colorSpaceAttribute =
-                    (ExifAttribute) mAttributes[PEF_HINT].get(TAG_COLOR_SPACE);
-            if (colorSpaceAttribute != null) {
-                mAttributes[IFD_EXIF_HINT].put(TAG_COLOR_SPACE, colorSpaceAttribute);
-            }
+            mAttributes[IFD_TYPE_EXIF].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute);
         }
     }
 
@@ -2693,25 +2682,25 @@
         // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
         String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
         if (valueOfDateTimeOriginal != null) {
-            mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME,
+            mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME,
                     ExifAttribute.createString(valueOfDateTimeOriginal));
         }
 
         // Add the default value.
         if (getAttribute(TAG_IMAGE_WIDTH) == null) {
-            mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH,
+            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
         if (getAttribute(TAG_IMAGE_LENGTH) == null) {
-            mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH,
+            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
         if (getAttribute(TAG_ORIENTATION) == null) {
-            mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION,
+            mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
         if (getAttribute(TAG_LIGHT_SOURCE) == null) {
-            mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE,
+            mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
     }
@@ -2763,8 +2752,8 @@
     }
 
     // Reads image file directory, which is a tag group in EXIF.
-    private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint)
-            throws IOException {
+    private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream,
+            @IfdType int ifdType) throws IOException {
         if (dataInputStream.peek() + 2 > dataInputStream.mLength) {
             // Return if there is no data from the offset.
             return;
@@ -2789,12 +2778,12 @@
             long nextEntryOffset = dataInputStream.peek() + 4;
 
             // Look up a corresponding tag from tag number
-            ExifTag tag = (ExifTag) sExifTagMapsForReading[hint].get(tagNumber);
+            ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber);
 
             if (DEBUG) {
-                Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " +
-                        "numberOfComponents: %d", hint, tagNumber, tag != null ? tag.name : null,
-                        dataFormat, numberOfComponents));
+                Log.d(TAG, String.format("ifdType: %d, tagNumber: %d, tagName: %s, dataFormat: %d, "
+                        + "numberOfComponents: %d", ifdType, tagNumber,
+                        tag != null ? tag.name : null, dataFormat, numberOfComponents));
             }
 
             if (tag == null || dataFormat <= 0 ||
@@ -2821,7 +2810,8 @@
                     if (tag.name == TAG_MAKER_NOTE) {
                         // Save offset value for reading thumbnail
                         mOrfMakerNoteOffset = offset;
-                    } else if (hint == ORF_MAKER_NOTE_HINT && tag.name == TAG_ORF_THUMBNAIL_IMAGE) {
+                    } else if (ifdType == IFD_TYPE_ORF_MAKER_NOTE
+                            && tag.name == TAG_ORF_THUMBNAIL_IMAGE) {
                         // Retrieve & update values for thumbnail offset and length values for ORF
                         mOrfThumbnailOffset = offset;
                         mOrfThumbnailLength = numberOfComponents;
@@ -2833,10 +2823,10 @@
                         ExifAttribute jpegInterchangeFormatLengthAttribute =
                                 ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder);
 
-                        mAttributes[IFD_THUMBNAIL_HINT].put(TAG_COMPRESSION, compressionAttribute);
-                        mAttributes[IFD_THUMBNAIL_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT,
+                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_COMPRESSION, compressionAttribute);
+                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
                                 jpegInterchangeFormatAttribute);
-                        mAttributes[IFD_THUMBNAIL_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
                                 jpegInterchangeFormatLengthAttribute);
                     }
                 } else if (mMimeType == IMAGE_TYPE_RW2) {
@@ -2855,12 +2845,12 @@
             }
 
             // Recursively parse IFD when a IFD pointer tag appears.
-            int innerIfdHint = getIfdHintFromTagNumber(tagNumber);
+            Object nextIfdType = sExifPointerTagMap.get(tagNumber);
             if (DEBUG) {
-                Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount);
+                Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
             }
 
-            if (innerIfdHint >= 0) {
+            if (nextIfdType != null) {
                 long offset = -1L;
                 // Get offset from data field
                 switch (dataFormat) {
@@ -2891,7 +2881,7 @@
                 }
                 if (offset > 0L && offset < dataInputStream.mLength) {
                     dataInputStream.seek(offset);
-                    readImageFileDirectory(dataInputStream, innerIfdHint);
+                    readImageFileDirectory(dataInputStream, (int) nextIfdType);
                 } else {
                     Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
                 }
@@ -2903,7 +2893,7 @@
             byte[] bytes = new byte[byteCount];
             dataInputStream.readFully(bytes);
             ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
-            mAttributes[hint].put(tag.name, attribute);
+            mAttributes[ifdType].put(tag.name, attribute);
 
             // DNG files have a DNG Version tag specifying the version of specifications that the
             // image file is following.
@@ -2937,11 +2927,11 @@
             // since the first IFD offset is at least 8.
             if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
                 dataInputStream.seek(nextIfdOffset);
-                if (mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) {
+                if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
                     // Do not overwrite thumbnail IFD data if it alreay exists.
-                    readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT);
-                } else if (mAttributes[IFD_PREVIEW_HINT].isEmpty()) {
-                    readImageFileDirectory(dataInputStream, IFD_PREVIEW_HINT);
+                    readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
+                } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
+                    readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
                 }
             }
         }
@@ -2976,7 +2966,7 @@
 
     // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags
     private void setThumbnailData(InputStream in) throws IOException {
-        HashMap thumbnailData = mAttributes[IFD_THUMBNAIL_HINT];
+        HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL];
 
         ExifAttribute compressionAttribute =
                 (ExifAttribute) thumbnailData.get(TAG_COMPRESSION);
@@ -3146,21 +3136,21 @@
     // Validate primary, preview, thumbnail image data by comparing image size
     private void validateImages(InputStream in) throws IOException {
         // Swap images based on size (primary > preview > thumbnail)
-        swapBasedOnImageSize(IFD_TIFF_HINT, IFD_PREVIEW_HINT);
-        swapBasedOnImageSize(IFD_TIFF_HINT, IFD_THUMBNAIL_HINT);
-        swapBasedOnImageSize(IFD_PREVIEW_HINT, IFD_THUMBNAIL_HINT);
+        swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
+        swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
+        swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
 
         // Check whether thumbnail image exists and whether preview image satisfies the thumbnail
         // image requirements
-        if (mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) {
-            if (isThumbnail(mAttributes[IFD_PREVIEW_HINT])) {
-                mAttributes[IFD_THUMBNAIL_HINT] = mAttributes[IFD_PREVIEW_HINT];
-                mAttributes[IFD_PREVIEW_HINT] = new HashMap();
+        if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
+            if (isThumbnail(mAttributes[IFD_TYPE_PREVIEW])) {
+                mAttributes[IFD_TYPE_THUMBNAIL] = mAttributes[IFD_TYPE_PREVIEW];
+                mAttributes[IFD_TYPE_PREVIEW] = new HashMap();
             }
         }
 
         // Check if the thumbnail image satisfies the thumbnail size requirements
-        if (!isThumbnail(mAttributes[IFD_THUMBNAIL_HINT])) {
+        if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
             Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
         }
     }
@@ -3244,9 +3234,9 @@
                 if (newSubfileTypeValue == ORIGINAL_RESOLUTION_IMAGE) {
                     // Update only for the primary image (OriginalResolutionImage)
                     ExifAttribute pixelXDimAttribute =
-                            (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_PIXEL_X_DIMENSION);
+                            (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION);
                     ExifAttribute pixelYDimAttribute =
-                            (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_PIXEL_Y_DIMENSION);
+                            (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION);
 
                     if (pixelXDimAttribute != null && pixelYDimAttribute != null) {
                         mAttributes[imageType].put(TAG_IMAGE_WIDTH, pixelXDimAttribute);
@@ -3259,16 +3249,6 @@
         }
     }
 
-    // Gets the corresponding IFD group index of the given tag number for writing Exif Tags.
-    private static int getIfdHintFromTagNumber(int tagNumber) {
-        for (int i = 0; i < EXIF_POINTER_TAG_HINTS.length; ++i) {
-            if (EXIF_POINTER_TAGS[i].number == tagNumber) {
-                return EXIF_POINTER_TAG_HINTS[i];
-            }
-        }
-        return -1;
-    }
-
     // Writes an Exif segment into the given output stream.
     private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream,
             int exifOffsetFromBeginning) throws IOException {
@@ -3285,33 +3265,33 @@
         removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
 
         // Remove null value tags.
-        for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
-            for (Object obj : mAttributes[hint].entrySet().toArray()) {
+        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
+            for (Object obj : mAttributes[ifdType].entrySet().toArray()) {
                 final Map.Entry entry = (Map.Entry) obj;
                 if (entry.getValue() == null) {
-                    mAttributes[hint].remove(entry.getKey());
+                    mAttributes[ifdType].remove(entry.getKey());
                 }
             }
         }
 
         // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
         // offset when there is one or more tags in the thumbnail IFD.
-        if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
-            mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[1].name,
+        if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
+            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
-        if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
-            mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[2].name,
+        if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
+            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
-        if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
-            mAttributes[IFD_EXIF_HINT].put(EXIF_POINTER_TAGS[3].name,
+        if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
+            mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
         if (mHasThumbnail) {
-            mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
+            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
                     ExifAttribute.createULong(0, mExifByteOrder));
-            mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
+            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
                     ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
         }
 
@@ -3331,15 +3311,15 @@
 
         // Calculate IFD offsets.
         int position = 8;
-        for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
-            if (!mAttributes[hint].isEmpty()) {
-                ifdOffsets[hint] = position;
-                position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint];
+        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
+            if (!mAttributes[ifdType].isEmpty()) {
+                ifdOffsets[ifdType] = position;
+                position += 2 + mAttributes[ifdType].size() * 12 + 4 + ifdDataSizes[ifdType];
             }
         }
         if (mHasThumbnail) {
             int thumbnailOffset = position;
-            mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
+            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
                     ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
             mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
             position += mThumbnailLength;
@@ -3356,17 +3336,17 @@
         }
 
         // Update IFD pointer tags with the calculated offsets.
-        if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
-            mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[1].name,
-                    ExifAttribute.createULong(ifdOffsets[IFD_EXIF_HINT], mExifByteOrder));
+        if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
+            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
+                    ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifByteOrder));
         }
-        if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
-            mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[2].name,
-                    ExifAttribute.createULong(ifdOffsets[IFD_GPS_HINT], mExifByteOrder));
+        if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
+            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
+                    ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifByteOrder));
         }
-        if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
-            mAttributes[IFD_EXIF_HINT].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(
-                    ifdOffsets[IFD_INTEROPERABILITY_HINT], mExifByteOrder));
+        if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
+            mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(
+                    ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder));
         }
 
         // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
@@ -3379,17 +3359,18 @@
         dataOutputStream.writeUnsignedInt(IFD_OFFSET);
 
         // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9.
-        for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
-            if (!mAttributes[hint].isEmpty()) {
+        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
+            if (!mAttributes[ifdType].isEmpty()) {
                 // See JEITA CP-3451C Section 4.6.2: IFD structure.
                 // Write entry count
-                dataOutputStream.writeUnsignedShort(mAttributes[hint].size());
+                dataOutputStream.writeUnsignedShort(mAttributes[ifdType].size());
 
                 // Write entry info
-                int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4;
-                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
+                int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4;
+                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
                     // Convert tag name to tag number.
-                    final ExifTag tag = (ExifTag) sExifTagMapsForWriting[hint].get(entry.getKey());
+                    final ExifTag tag =
+                            (ExifTag) sExifTagMapsForWriting[ifdType].get(entry.getKey());
                     final int tagNumber = tag.number;
                     final ExifAttribute attribute = (ExifAttribute) entry.getValue();
                     final int size = attribute.size();
@@ -3414,14 +3395,14 @@
                 // Write the next offset. It writes the offset of thumbnail IFD if there is one or
                 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
                 // IFD; Otherwise 0.
-                if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) {
-                    dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]);
+                if (ifdType == 0 && !mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
+                    dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_TYPE_THUMBNAIL]);
                 } else {
                     dataOutputStream.writeUnsignedInt(0);
                 }
 
                 // Write values of data field exceeding 4 bytes after the next offset.
-                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
+                for (Map.Entry entry : (Set<Map.Entry>) mAttributes[ifdType].entrySet()) {
                     ExifAttribute attribute = (ExifAttribute) entry.getValue();
 
                     if (attribute.bytes.length > 4) {
@@ -3748,9 +3729,9 @@
     }
 
     // Swaps image data based on image size
-    private void swapBasedOnImageSize(int firstImageHint, int secondImageHint)
+    private void swapBasedOnImageSize(@IfdType int firstIfdType, @IfdType int secondIfdType)
             throws IOException {
-        if (mAttributes[firstImageHint].isEmpty() || mAttributes[secondImageHint].isEmpty()) {
+        if (mAttributes[firstIfdType].isEmpty() || mAttributes[secondIfdType].isEmpty()) {
             if (DEBUG) {
                 Log.d(TAG, "Cannot perform swap since only one image data exists");
             }
@@ -3758,13 +3739,13 @@
         }
 
         ExifAttribute firstImageLengthAttribute =
-                (ExifAttribute) mAttributes[firstImageHint].get(TAG_IMAGE_LENGTH);
+                (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_LENGTH);
         ExifAttribute firstImageWidthAttribute =
-                (ExifAttribute) mAttributes[firstImageHint].get(TAG_IMAGE_WIDTH);
+                (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_WIDTH);
         ExifAttribute secondImageLengthAttribute =
-                (ExifAttribute) mAttributes[secondImageHint].get(TAG_IMAGE_LENGTH);
+                (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_LENGTH);
         ExifAttribute secondImageWidthAttribute =
-                (ExifAttribute) mAttributes[secondImageHint].get(TAG_IMAGE_WIDTH);
+                (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_WIDTH);
 
         if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) {
             if (DEBUG) {
@@ -3782,9 +3763,9 @@
 
             if (firstImageLengthValue < secondImageLengthValue &&
                     firstImageWidthValue < secondImageWidthValue) {
-                HashMap tempMap = mAttributes[firstImageHint];
-                mAttributes[firstImageHint] = mAttributes[secondImageHint];
-                mAttributes[secondImageHint] = tempMap;
+                HashMap tempMap = mAttributes[firstIfdType];
+                mAttributes[firstIfdType] = mAttributes[secondIfdType];
+                mAttributes[secondIfdType] = tempMap;
             }
         }
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index b75f529..ddccc14 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -42,16 +42,21 @@
  * Displays an alphanumeric (latin-1) key entry for the user to enter
  * an unlock password
  */
-
 public class KeyguardPasswordView extends KeyguardAbsKeyInputView
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
 
     private final boolean mShowImeAtScreenOn;
     private final int mDisappearYTranslation;
 
+    // A delay constant to be used in a workaround for the situation where InputMethodManagerService
+    // is not switched to the new user yet.
+    // TODO: Remove this by ensuring such a race condition never happens.
+    private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500;  // 500ms
+
     InputMethodManager mImm;
     private TextView mPasswordEntry;
     private TextViewInputDisabler mPasswordEntryDisabler;
+    private View mSwitchImeButton;
 
     private Interpolator mLinearOutSlowInInterpolator;
     private Interpolator mFastOutLinearInInterpolator;
@@ -141,12 +146,31 @@
         mPasswordEntry.requestFocus();
     }
 
+    private void updateSwitchImeButton() {
+        // If there's more than one IME, enable the IME switcher button
+        final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
+        final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false);
+        if (wasVisible != shouldBeVisible) {
+            mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
+        }
+
+        // TODO: Check if we still need this hack.
+        // If no icon is visible, reset the start margin on the password field so the text is
+        // still centered.
+        if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
+            android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
+            if (params instanceof MarginLayoutParams) {
+                final MarginLayoutParams mlp = (MarginLayoutParams) params;
+                mlp.setMarginStart(0);
+                mPasswordEntry.setLayoutParams(params);
+            }
+        }
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        boolean imeOrDeleteButtonVisible = false;
-
         mImm = (InputMethodManager) getContext().getSystemService(
                 Context.INPUT_METHOD_SERVICE);
 
@@ -171,31 +195,29 @@
 
         mPasswordEntry.requestFocus();
 
-        // If there's more than one IME, enable the IME switcher button
-        View switchImeButton = findViewById(R.id.switch_ime_button);
-        if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) {
-            switchImeButton.setVisibility(View.VISIBLE);
-            imeOrDeleteButtonVisible = true;
-            switchImeButton.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    mCallback.userActivity(); // Leave the screen on a bit longer
-                    // Do not show auxiliary subtypes in password lock screen.
-                    mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
-                }
-            });
-        }
-
-        // If no icon is visible, reset the start margin on the password field so the text is
-        // still centered.
-        if (!imeOrDeleteButtonVisible) {
-            android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
-            if (params instanceof MarginLayoutParams) {
-                final MarginLayoutParams mlp = (MarginLayoutParams) params;
-                mlp.setMarginStart(0);
-                mPasswordEntry.setLayoutParams(params);
+        mSwitchImeButton = findViewById(R.id.switch_ime_button);
+        mSwitchImeButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mCallback.userActivity(); // Leave the screen on a bit longer
+                // Do not show auxiliary subtypes in password lock screen.
+                mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
             }
-        }
+        });
+
+        // If there's more than one IME, enable the IME switcher button
+        updateSwitchImeButton();
+
+        // When we the current user is switching, InputMethodManagerService sometimes has not
+        // switched internal state yet here. As a quick workaround, we check the keyboard state
+        // again.
+        // TODO: Remove this workaround by ensuring such a race condition never happens.
+        postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                updateSwitchImeButton();
+            }
+        }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
     }
 
     @Override
diff --git a/packages/SettingsLib/res/values-ky-rKG/arrays.xml b/packages/SettingsLib/res/values-ky-rKG/arrays.xml
index a06ecfb..1aadb9b 100644
--- a/packages/SettingsLib/res/values-ky-rKG/arrays.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/arrays.xml
@@ -147,7 +147,7 @@
   <string-array name="track_frame_time_entries">
     <item msgid="2193584639058893150">"Өчүк"</item>
     <item msgid="2751513398307949636">"Экранда тилке катары"</item>
-    <item msgid="2355151170975410323">"Төмөнкүдө <xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>"</item>
+    <item msgid="2355151170975410323">"Төмөнкүдө: <xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>"</item>
   </string-array>
   <string-array name="debug_hw_overdraw_entries">
     <item msgid="8190572633763871652">"Өчүк"</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
index cf08f50..687b3fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
+++ b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
@@ -167,7 +167,7 @@
         return (policy != null) ? policy.warningBytes : WARNING_DISABLED;
     }
 
-    public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) {
+    private void setPolicyWarningBytesInner(NetworkTemplate template, long warningBytes) {
         final NetworkPolicy policy = getOrCreatePolicy(template);
         policy.warningBytes = warningBytes;
         policy.inferred = false;
@@ -175,12 +175,28 @@
         writeAsync();
     }
 
+    public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) {
+        long limitBytes = getPolicyLimitBytes(template);
+
+        // If the warningBytes are larger than limitBytes, set the warningBytes to limitBytes
+        warningBytes = Math.min(warningBytes, limitBytes);
+        setPolicyWarningBytesInner(template, warningBytes);
+    }
+
     public long getPolicyLimitBytes(NetworkTemplate template) {
         final NetworkPolicy policy = getPolicy(template);
         return (policy != null) ? policy.limitBytes : LIMIT_DISABLED;
     }
 
+
     public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) {
+        long warningBytes = getPolicyWarningBytes(template);
+
+        // If the warningBytes are larger than limitBytes, set the warningBytes to limitBytes
+        if (warningBytes > limitBytes) {
+            setPolicyWarningBytesInner(template, limitBytes);
+        }
+
         final NetworkPolicy policy = getOrCreatePolicy(template);
         policy.limitBytes = limitBytes;
         policy.inferred = false;
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 5acc177..a93c3e2 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -313,7 +313,7 @@
     <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
     <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampe de poche"</string>
     <string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Données mobiles"</string>
-    <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Consommation des données"</string>
+    <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Conso des données"</string>
     <string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Données restantes"</string>
     <string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Limite dépassée"</string>
     <string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"<xliff:g id="DATA_USED">%s</xliff:g> utilisés"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index de96cb0..a15ba26 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -309,7 +309,7 @@
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Đang dùng làm điểm truy cập Internet"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Điểm phát sóng"</string>
     <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Thông báo"</string>
-    <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Đèn nháy"</string>
+    <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Đèn pin"</string>
     <string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Dữ liệu di động"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Sử dụng dữ liệu"</string>
     <string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Dữ liệu còn lại"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index ac90ce7..19a5d52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -237,6 +237,10 @@
         mQSPanel.setListening(mListening && mQsExpanded);
     }
 
+    public void setHeaderListening(boolean listening) {
+        mHeader.setListening(listening);
+    }
+
     public void setQsExpansion(float expansion, float headerTranslation) {
         if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
         mQsExpansion = expansion;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b6597a0..ed0fc1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -251,10 +251,12 @@
         if (mListening) {
             refreshAllTiles();
         }
-        if (listening) {
-            mBrightnessController.registerCallbacks();
-        } else {
-            mBrightnessController.unregisterCallbacks();
+        if (mBrightnessView.getVisibility() == View.VISIBLE) {
+            if (listening) {
+                mBrightnessController.registerCallbacks();
+            } else {
+                mBrightnessController.unregisterCallbacks();
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index f27f8f5..812c5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1617,6 +1617,9 @@
         if (mQsExpanded) {
             onQsExpansionStarted();
         }
+        // Since there are QS tiles in the header now, we need to make sure we start listening
+        // immediately so they can be up to date.
+        mQsContainer.setHeaderListening(true);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 488f0e7..6b73fec 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -49,7 +49,6 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.IDeviceIdleController;
 import android.os.IMaintenanceActivityListener;
 import android.os.Looper;
@@ -1238,7 +1237,7 @@
         }
     }
 
-    public final class LocalService {
+    public class LocalService {
         public void addPowerSaveTempWhitelistAppDirect(int appId, long duration, boolean sync,
                 String reason) {
             addPowerSaveTempWhitelistAppDirectInternal(0, appId, duration, sync, reason);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 649016f..09a3a17 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3807,6 +3807,15 @@
             app.killedByAm = false;
             checkTime(startTime, "startProcess: starting to update pids map");
             synchronized (mPidsSelfLocked) {
+                ProcessRecord oldApp;
+                // If there is already an app occupying that pid that hasn't been cleaned up
+                if ((oldApp = mPidsSelfLocked.get(startResult.pid)) != null && !app.isolated) {
+                    // Clean up anything relating to this pid first
+                    Slog.w(TAG, "Reusing pid " + startResult.pid
+                            + " while app is still mapped to it");
+                    cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+                            true /*replacingPid*/);
+                }
                 this.mPidsSelfLocked.put(startResult.pid, app);
                 if (isActivityProcess) {
                     Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
@@ -5039,7 +5048,8 @@
     private final void handleAppDiedLocked(ProcessRecord app,
             boolean restarting, boolean allowRestart) {
         int pid = app.pid;
-        boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
+        boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
+                false /*replacingPid*/);
         if (!kept && !restarting) {
             removeLruProcessLocked(app);
             if (pid > 0) {
@@ -16816,7 +16826,8 @@
      * app that was passed in must remain on the process lists.
      */
     private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
-            boolean restarting, boolean allowRestart, int index) {
+            boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
+        Slog.d(TAG, "cleanUpApplicationRecord -- " + app.pid);
         if (index >= 0) {
             removeLruProcessLocked(app);
             ProcessList.remove(app.pid);
@@ -16947,7 +16958,9 @@
         if (!app.persistent || app.isolated) {
             if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                     "Removing non-persistent process during cleanup: " + app);
-            removeProcessNameLocked(app.processName, app.uid);
+            if (!replacingPid) {
+                removeProcessNameLocked(app.processName, app.uid);
+            }
             if (mHeavyWeightProcess == app) {
                 mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
                         mHeavyWeightProcess.userId, 0));
@@ -21279,7 +21292,7 @@
                             // Ignore exceptions.
                         }
                     }
-                    cleanUpApplicationRecordLocked(app, false, true, -1);
+                    cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
                     mRemovedProcesses.remove(i);
 
                     if (app.persistent) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index d02fbbf..219e095 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -304,6 +304,11 @@
         boolean didSomething = false;
         final BroadcastRecord br = mPendingBroadcast;
         if (br != null && br.curApp.pid == app.pid) {
+            if (br.curApp != app) {
+                Slog.e(TAG, "App mismatch when sending pending broadcast to "
+                        + app.processName + ", intended target is " + br.curApp.processName);
+                return false;
+            }
             try {
                 mPendingBroadcast = null;
                 processCurBroadcastLocked(br, app);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 085de43..9ca609f 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1050,6 +1050,8 @@
                 builder.setTicker(title);
                 builder.setContentTitle(title);
                 builder.setContentText(body);
+                builder.setDefaults(Notification.DEFAULT_ALL);
+                builder.setPriority(Notification.PRIORITY_HIGH);
 
                 final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
                 builder.setDeleteIntent(PendingIntent.getBroadcast(
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c78e94b..d3dab44 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -517,6 +517,11 @@
                 // placement for this window during this period, one or more frame will
                 // show up with wrong position or scale.
                 win.mWinAnimator.mAnimating = false;
+
+                if (win.mDestroying) {
+                    win.mDestroying = false;
+                    mService.mDestroySurface.remove(win);
+                }
             }
         }
         requestUpdateWallpaperIfNeeded();
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index c26c078..4c9211a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1462,7 +1462,7 @@
             mObscured = true;
         }
 
-        if (w.mHasSurface) {
+        if (w.mHasSurface && canBeSeen) {
             if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
                 mHoldScreen = w.mSession;
                 mHoldScreenWindow = w;
@@ -1485,43 +1485,39 @@
             }
 
             final int type = attrs.type;
-            if (canBeSeen
-                    && (type == TYPE_SYSTEM_DIALOG
-                     || type == TYPE_SYSTEM_ERROR
-                     || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0)) {
+            if (type == TYPE_SYSTEM_DIALOG || type == TYPE_SYSTEM_ERROR
+                    || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
                 mSyswin = true;
             }
 
-            if (canBeSeen) {
-                // This function assumes that the contents of the default display are
-                // processed first before secondary displays.
-                final DisplayContent displayContent = w.getDisplayContent();
-                if (displayContent != null && displayContent.isDefaultDisplay) {
-                    // While a dream or keyguard is showing, obscure ordinary application
-                    // content on secondary displays (by forcibly enabling mirroring unless
-                    // there is other content we want to show) but still allow opaque
-                    // keyguard dialogs to be shown.
-                    if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
-                        mObscureApplicationContentOnSecondaryDisplays = true;
-                    }
-                    mDisplayHasContent = true;
-                } else if (displayContent != null &&
-                        (!mObscureApplicationContentOnSecondaryDisplays
-                        || (mObscured && type == TYPE_KEYGUARD_DIALOG))) {
-                    // Allow full screen keyguard presentation dialogs to be seen.
-                    mDisplayHasContent = true;
+            // This function assumes that the contents of the default display are
+            // processed first before secondary displays.
+            final DisplayContent displayContent = w.getDisplayContent();
+            if (displayContent != null && displayContent.isDefaultDisplay) {
+                // While a dream or keyguard is showing, obscure ordinary application
+                // content on secondary displays (by forcibly enabling mirroring unless
+                // there is other content we want to show) but still allow opaque
+                // keyguard dialogs to be shown.
+                if (type == TYPE_DREAM || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+                    mObscureApplicationContentOnSecondaryDisplays = true;
                 }
-                if (mPreferredRefreshRate == 0
-                        && w.mAttrs.preferredRefreshRate != 0) {
-                    mPreferredRefreshRate = w.mAttrs.preferredRefreshRate;
-                }
-                if (mPreferredModeId == 0
-                        && w.mAttrs.preferredDisplayModeId != 0) {
-                    mPreferredModeId = w.mAttrs.preferredDisplayModeId;
-                }
-                if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
-                    mSustainedPerformanceModeCurrent = true;
-                }
+                mDisplayHasContent = true;
+            } else if (displayContent != null &&
+                    (!mObscureApplicationContentOnSecondaryDisplays
+                            || (mObscured && type == TYPE_KEYGUARD_DIALOG))) {
+                // Allow full screen keyguard presentation dialogs to be seen.
+                mDisplayHasContent = true;
+            }
+            if (mPreferredRefreshRate == 0
+                    && w.mAttrs.preferredRefreshRate != 0) {
+                mPreferredRefreshRate = w.mAttrs.preferredRefreshRate;
+            }
+            if (mPreferredModeId == 0
+                    && w.mAttrs.preferredDisplayModeId != 0) {
+                mPreferredModeId = w.mAttrs.preferredDisplayModeId;
+            }
+            if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
+                mSustainedPerformanceModeCurrent = true;
             }
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 541be3d..7303358 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -16,17 +16,12 @@
 
 package com.android.server;
 
-import static android.content.Intent.ACTION_UID_REMOVED;
-import static android.content.Intent.EXTRA_UID;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.NetworkPolicy.CYCLE_NONE;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
 import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
 import static android.net.TrafficStats.KB_IN_BYTES;
@@ -34,28 +29,36 @@
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.Time.TIMEZONE_UTC;
+
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
-import static org.easymock.EasyMock.anyInt;
-import static org.easymock.EasyMock.anyLong;
-import static org.easymock.EasyMock.aryEq;
-import static org.easymock.EasyMock.capture;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.isA;
 
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
-import android.app.IProcessObserver;
+import android.app.IUidObserver;
 import android.app.Notification;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
-import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
@@ -69,38 +72,39 @@
 import android.net.NetworkTemplate;
 import android.os.Binder;
 import android.os.INetworkManagementService;
-import android.os.MessageQueue.IdleHandler;
+import android.os.PowerManagerInternal;
 import android.os.UserHandle;
 import android.test.AndroidTestCase;
-import android.test.mock.MockPackageManager;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.text.format.Time;
+import android.util.Log;
 import android.util.TrustedTime;
 
+import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.net.NetworkPolicyManagerService;
+
+import libcore.io.IoUtils;
+
 import com.google.common.util.concurrent.AbstractFuture;
 
-import org.easymock.Capture;
-import org.easymock.EasyMock;
-import org.easymock.IAnswer;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.io.File;
-import java.util.Calendar;
+import java.util.ArrayList;
 import java.util.LinkedHashSet;
-import java.util.TimeZone;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.logging.Handler;
-
-import libcore.io.IoUtils;
 
 /**
  * Tests for {@link NetworkPolicyManagerService}.
  */
-@LargeTest
 public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
     private static final String TAG = "NetworkPolicyManagerServiceTest";
 
@@ -112,20 +116,22 @@
 
     private BroadcastInterceptingContext mServiceContext;
     private File mPolicyDir;
+    private List<Class<?>> mLocalServices = new ArrayList<>();
 
-    private IActivityManager mActivityManager;
-    private INetworkStatsService mStatsService;
-    private INetworkManagementService mNetworkManager;
-    private INetworkPolicyListener mPolicyListener;
-    private TrustedTime mTime;
-    private IConnectivityManager mConnManager;
-    private INotificationManager mNotifManager;
+    private @Mock IActivityManager mActivityManager;
+    private @Mock INetworkStatsService mStatsService;
+    private @Mock INetworkManagementService mNetworkManager;
+    private @Mock TrustedTime mTime;
+    private @Mock IConnectivityManager mConnManager;
+    private @Mock INotificationManager mNotifManager;
+    private @Mock UsageStatsManagerInternal mUsageStats;
+    private @Mock PackageManager mPackageManager;
 
-    private NetworkPolicyManagerService mService;
-    private IProcessObserver mProcessObserver;
+    private IUidObserver mUidObserver;
     private INetworkManagementEventObserver mNetworkObserver;
 
-    private Binder mStubBinder = new Binder();
+    private NetworkPolicyListenerAnswer mPolicyListener;
+    private NetworkPolicyManagerService mService;
 
     private long mStartTime;
     private long mElapsedRealtime;
@@ -138,39 +144,22 @@
     private static final int UID_A = UserHandle.getUid(USER_ID, APP_ID_A);
     private static final int UID_B = UserHandle.getUid(USER_ID, APP_ID_B);
 
-    private static final int PID_1 = 400;
-    private static final int PID_2 = 401;
-    private static final int PID_3 = 402;
+    private static final String PKG_NAME_A = "name.is.A,pkg.A";
 
-    public void _setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
 
+        MockitoAnnotations.initMocks(this);
+
+        final Context context = getContext();
+
         setCurrentTimeMillis(TEST_START);
 
         // intercept various broadcasts, and pretend that uids have packages
-        mServiceContext = new BroadcastInterceptingContext(getContext()) {
+        mServiceContext = new BroadcastInterceptingContext(context) {
             @Override
             public PackageManager getPackageManager() {
-                return new MockPackageManager() {
-                    @Override
-                    public String[] getPackagesForUid(int uid) {
-                        return new String[] { "com.example" };
-                    }
-
-                    @Override
-                    public PackageInfo getPackageInfo(String packageName, int flags) {
-                        final PackageInfo info = new PackageInfo();
-                        final Signature signature;
-                        if ("android".equals(packageName)) {
-                            signature = new Signature("F00D");
-                        } else {
-                            signature = new Signature("DEAD");
-                        }
-                        info.signatures = new Signature[] { signature };
-                        return info;
-                    }
-
-                };
+                return mPackageManager;
             }
 
             @Override
@@ -179,54 +168,69 @@
             }
         };
 
-        mPolicyDir = getContext().getFilesDir();
+        mPolicyDir = context.getFilesDir();
         if (mPolicyDir.exists()) {
             IoUtils.deleteContents(mPolicyDir);
         }
 
-        mActivityManager = createMock(IActivityManager.class);
-        mStatsService = createMock(INetworkStatsService.class);
-        mNetworkManager = createMock(INetworkManagementService.class);
-        mPolicyListener = createMock(INetworkPolicyListener.class);
-        mTime = createMock(TrustedTime.class);
-        mConnManager = createMock(IConnectivityManager.class);
-        mNotifManager = createMock(INotificationManager.class);
+        doAnswer(new Answer<Void>() {
 
-        mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
-                mStatsService, mNetworkManager, mTime, mPolicyDir, true);
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                mUidObserver = (IUidObserver) invocation.getArguments()[0];
+                Log.d(TAG, "set mUidObserver to " + mUidObserver);
+                return null;
+            }
+        }).when(mActivityManager).registerUidObserver(any(), anyInt());
+
+        addLocalServiceMock(PowerManagerInternal.class);
+        addLocalServiceMock(DeviceIdleController.LocalService.class);
+        addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStats);
+
+        mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mStatsService,
+                mNetworkManager, mTime, mPolicyDir, true);
         mService.bindConnectivityManager(mConnManager);
         mService.bindNotificationManager(mNotifManager);
+        mPolicyListener = new NetworkPolicyListenerAnswer(mService);
 
-        // RemoteCallbackList needs a binder to use as key
-        expect(mPolicyListener.asBinder()).andReturn(mStubBinder).atLeastOnce();
-        replay();
-        mService.registerListener(mPolicyListener);
-        verifyAndReset();
+        // Sets some common expectations.
+        when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenAnswer(
+                new Answer<PackageInfo>() {
 
-        // catch IProcessObserver during systemReady()
-        final Capture<IProcessObserver> processObserver = new Capture<IProcessObserver>();
-        mActivityManager.registerProcessObserver(capture(processObserver));
-        expectLastCall().atLeastOnce();
-
-        // catch INetworkManagementEventObserver during systemReady()
-        final Capture<INetworkManagementEventObserver> networkObserver = new Capture<
-                INetworkManagementEventObserver>();
-        mNetworkManager.registerObserver(capture(networkObserver));
-        expectLastCall().atLeastOnce();
-
-        expect(mNetworkManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
+                    @Override
+                    public PackageInfo answer(InvocationOnMock invocation) throws Throwable {
+                        final String packageName = (String) invocation.getArguments()[0];
+                        final PackageInfo info = new PackageInfo();
+                        final Signature signature;
+                        if ("android".equals(packageName)) {
+                            signature = new Signature("F00D");
+                        } else {
+                            signature = new Signature("DEAD");
+                        }
+                        info.signatures = new Signature[] {
+                            signature
+                        };
+                        return info;
+                    }
+                });
+        when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(new ApplicationInfo());
+        when(mPackageManager.getPackagesForUid(UID_A)).thenReturn(new String[] {PKG_NAME_A});
+        when(mUsageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{});
+        when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
         expectCurrentTime();
 
-        replay();
+        // Prepare NPMS.
         mService.systemReady();
-        verifyAndReset();
 
-        mProcessObserver = processObserver.getValue();
+        // catch INetworkManagementEventObserver during systemReady()
+        ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
+              ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
+        verify(mNetworkManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
-
     }
 
-    public void _tearDown() throws Exception {
+    public void tearDown() throws Exception {
         for (File file : mPolicyDir.listFiles()) {
             file.delete();
         }
@@ -236,170 +240,51 @@
 
         mActivityManager = null;
         mStatsService = null;
-        mPolicyListener = null;
         mTime = null;
 
         mService = null;
-        mProcessObserver = null;
+
+        // TODO: must remove services, otherwise next test will fail.
+        // JUnit4 would avoid that hack by using a static setup.
+        removeLocalServiceMocks();
+
+        // Added by NetworkPolicyManagerService's constructor.
+        LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
 
         super.tearDown();
     }
 
-    @Suppress
-    public void testPolicyChangeTriggersBroadcast() throws Exception {
+    // NOTE: testPolicyChangeTriggersListener() and testUidForeground() are too superficial, they
+    // don't check for side-effects (like calls to NetworkManagementService) neither cover all
+    // different modes (Data Saver, Battery Saver, Doze, App idle, etc...).
+    // These scenarios are extensively tested on CTS' HostsideRestrictBackgroundNetworkTests.
+
+    public void testPolicyChangeTriggersListener() throws Exception {
+        mPolicyListener.expect().onRestrictBackgroundBlacklistChanged(anyInt(), anyBoolean());
+
         mService.setUidPolicy(APP_ID_A, POLICY_NONE);
-
-        // change background policy and expect broadcast
-        final Future<Intent> backgroundChanged = mServiceContext.nextBroadcastIntent(
-                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
-
         mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
 
-        backgroundChanged.get();
+        mPolicyListener.waitAndVerify().onRestrictBackgroundBlacklistChanged(APP_ID_A, true);
     }
 
-    @Suppress
-    public void testPidForegroundCombined() throws Exception {
-        IdleFuture idle;
-
-        // push all uid into background
-        idle = expectIdle();
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
-        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
-        mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
-        idle.get();
+    public void testUidForeground() throws Exception {
+        // push all uids into background
+        mUidObserver.onUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE);
+        mUidObserver.onUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE);
         assertFalse(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
-        // push one of the shared pids into foreground
-        idle = expectIdle();
-        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
-        idle.get();
+        // push one of the uids into foreground
+        mUidObserver.onUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP);
         assertTrue(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // and swap another uid into foreground
-        idle = expectIdle();
-        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
-        mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
-        idle.get();
+        mUidObserver.onUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE);
+        mUidObserver.onUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP);
         assertFalse(mService.isUidForeground(UID_A));
         assertTrue(mService.isUidForeground(UID_B));
-
-        // push both pid into foreground
-        idle = expectIdle();
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
-        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
-        idle.get();
-        assertTrue(mService.isUidForeground(UID_A));
-
-        // pull one out, should still be foreground
-        idle = expectIdle();
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
-        idle.get();
-        assertTrue(mService.isUidForeground(UID_A));
-
-        // pull final pid out, should now be background
-        idle = expectIdle();
-        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
-        idle.get();
-        assertFalse(mService.isUidForeground(UID_A));
-    }
-
-    @Suppress
-    public void testPolicyNone() throws Exception {
-        Future<Void> future;
-
-        expectSetUidMeteredNetworkBlacklist(UID_A, false);
-        expectSetUidForeground(UID_A, true);
-        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
-        replay();
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
-        future.get();
-        verifyAndReset();
-
-        // POLICY_NONE should RULE_ALLOW in foreground
-        expectSetUidMeteredNetworkBlacklist(UID_A, false);
-        expectSetUidForeground(UID_A, true);
-        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
-        replay();
-        mService.setUidPolicy(APP_ID_A, POLICY_NONE);
-        future.get();
-        verifyAndReset();
-
-        // POLICY_NONE should RULE_ALLOW in background
-        expectSetUidMeteredNetworkBlacklist(UID_A, false);
-        expectSetUidForeground(UID_A, false);
-        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
-        replay();
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
-        future.get();
-        verifyAndReset();
-    }
-
-    @Suppress
-    public void testPolicyReject() throws Exception {
-        Future<Void> future;
-
-        // POLICY_REJECT should RULE_ALLOW in background
-        expectSetUidMeteredNetworkBlacklist(UID_A, true);
-        expectSetUidForeground(UID_A, false);
-        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
-        replay();
-        mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
-        future.get();
-        verifyAndReset();
-
-        // POLICY_REJECT should RULE_ALLOW in foreground
-        expectSetUidMeteredNetworkBlacklist(UID_A, false);
-        expectSetUidForeground(UID_A, true);
-        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
-        replay();
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
-        future.get();
-        verifyAndReset();
-
-        // POLICY_REJECT should RULE_REJECT in background
-        expectSetUidMeteredNetworkBlacklist(UID_A, true);
-        expectSetUidForeground(UID_A, false);
-        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
-        replay();
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
-        future.get();
-        verifyAndReset();
-    }
-
-    @Suppress
-    public void testPolicyRejectAddRemove() throws Exception {
-        Future<Void> future;
-
-        // POLICY_NONE should have RULE_ALLOW in background
-        expectSetUidMeteredNetworkBlacklist(UID_A, false);
-        expectSetUidForeground(UID_A, false);
-        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
-        replay();
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
-        mService.setUidPolicy(APP_ID_A, POLICY_NONE);
-        future.get();
-        verifyAndReset();
-
-        // adding POLICY_REJECT should cause RULE_REJECT
-        expectSetUidMeteredNetworkBlacklist(UID_A, true);
-        expectSetUidForeground(UID_A, false);
-        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
-        replay();
-        mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
-        future.get();
-        verifyAndReset();
-
-        // removing POLICY_REJECT should return us to RULE_ALLOW
-        expectSetUidMeteredNetworkBlacklist(UID_A, false);
-        expectSetUidForeground(UID_A, false);
-        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
-        replay();
-        mService.setUidPolicy(APP_ID_A, POLICY_NONE);
-        future.get();
-        verifyAndReset();
     }
 
     public void testLastCycleBoundaryThisMonth() throws Exception {
@@ -544,11 +429,9 @@
         assertTimeEquals(expectedCycle, actualCycle);
     }
 
-    @Suppress
     public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
-        Future<Void> future;
 
         final long TIME_FEB_15 = 1171497600000L;
         final long TIME_MAR_10 = 1173484800000L;
@@ -559,75 +442,39 @@
         // first, pretend that wifi network comes online. no policy active,
         // which means we shouldn't push limit to interface.
         state = new NetworkState[] { buildWifi() };
-        expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+        when(mConnManager.getAllNetworkState()).thenReturn(state);
         expectCurrentTime();
-        expectClearNotifications();
-        expectAdvisePersistThreshold();
-        future = expectMeteredIfacesChanged();
 
-        replay();
+        mPolicyListener.expect().onMeteredIfacesChanged(any());
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
-        future.get();
-        verifyAndReset();
+        mPolicyListener.waitAndVerify().onMeteredIfacesChanged(any());
 
         // now change cycle to be on 15th, and test in early march, to verify we
         // pick cycle day in previous month.
-        expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+        when(mConnManager.getAllNetworkState()).thenReturn(state);
         expectCurrentTime();
 
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
-        expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
-                .andReturn(stats.getTotalBytes()).atLeastOnce();
-        expectPolicyDataEnable(TYPE_WIFI, true);
+        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
+                .thenReturn(stats.getTotalBytes());
 
-        // TODO: consider making strongly ordered mock
-        expectRemoveInterfaceQuota(TEST_IFACE);
-        expectSetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512);
-
-        expectClearNotifications();
-        expectAdvisePersistThreshold();
-        future = expectMeteredIfacesChanged(TEST_IFACE);
-
-        replay();
+        mPolicyListener.expect().onMeteredIfacesChanged(any());
         setNetworkPolicies(new NetworkPolicy(
                 sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
-        future.get();
-        verifyAndReset();
+        mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
+
+        // TODO: consider making strongly ordered mock
+        verifyPolicyDataEnable(TYPE_WIFI, true);
+        verifyRemoveInterfaceQuota(TEST_IFACE);
+        verifySetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512);
     }
 
-    @Suppress
-    public void testUidRemovedPolicyCleared() throws Exception {
-        Future<Void> future;
-
-        // POLICY_REJECT should RULE_REJECT in background
-        expectSetUidMeteredNetworkBlacklist(UID_A, true);
-        expectSetUidForeground(UID_A, false);
-        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
-        replay();
-        mService.setUidPolicy(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
-        future.get();
-        verifyAndReset();
-
-        // uninstall should clear RULE_REJECT
-        expectSetUidMeteredNetworkBlacklist(UID_A, false);
-        expectSetUidForeground(UID_A, false);
-        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
-        replay();
-        final Intent intent = new Intent(ACTION_UID_REMOVED);
-        intent.putExtra(EXTRA_UID, UID_A);
-        mServiceContext.sendBroadcast(intent);
-        future.get();
-        verifyAndReset();
-    }
-
-    @Suppress
     public void testOverWarningLimitNotification() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
-        Future<Void> future;
-        Future<String> tagFuture;
+        Future<String> tagFuture = null;
 
         final long TIME_FEB_15 = 1171497600000L;
         final long TIME_MAR_10 = 1173484800000L;
@@ -642,20 +489,15 @@
 
         {
             expectCurrentTime();
-            expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
-            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
-                    .andReturn(stats.getTotalBytes()).atLeastOnce();
-            expectPolicyDataEnable(TYPE_WIFI, true);
+            when(mConnManager.getAllNetworkState()).thenReturn(state);
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
+                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
 
-            expectClearNotifications();
-            expectAdvisePersistThreshold();
-            future = expectMeteredIfacesChanged();
-
-            replay();
+            mPolicyListener.expect().onMeteredIfacesChanged(any());
             setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1
                     * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
-            future.get();
-            verifyAndReset();
+            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(any());
+            verifyPolicyDataEnable(TYPE_WIFI, true);
         }
 
         // bring up wifi network
@@ -666,22 +508,17 @@
 
         {
             expectCurrentTime();
-            expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
-            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
-                    .andReturn(stats.getTotalBytes()).atLeastOnce();
-            expectPolicyDataEnable(TYPE_WIFI, true);
+            when(mConnManager.getAllNetworkState()).thenReturn(state);
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
+                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
 
-            expectRemoveInterfaceQuota(TEST_IFACE);
-            expectSetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES);
-
-            expectClearNotifications();
-            expectAdvisePersistThreshold();
-            future = expectMeteredIfacesChanged(TEST_IFACE);
-
-            replay();
+            mPolicyListener.expect().onMeteredIfacesChanged(any());
             mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
-            future.get();
-            verifyAndReset();
+            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
+
+            verifyPolicyDataEnable(TYPE_WIFI, true);
+            verifyRemoveInterfaceQuota(TEST_IFACE);
+            verifySetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES);
         }
 
         // go over warning, which should kick notification
@@ -691,18 +528,15 @@
 
         {
             expectCurrentTime();
-            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
-                    .andReturn(stats.getTotalBytes()).atLeastOnce();
-            expectPolicyDataEnable(TYPE_WIFI, true);
-
-            expectForceUpdate();
-            expectClearNotifications();
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
+                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
-            replay();
             mNetworkObserver.limitReached(null, TEST_IFACE);
+
             assertNotificationType(TYPE_WARNING, tagFuture.get());
-            verifyAndReset();
+            verifyPolicyDataEnable(TYPE_WIFI, true);
+
         }
 
         // go over limit, which should kick notification and dialog
@@ -712,18 +546,14 @@
 
         {
             expectCurrentTime();
-            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
-                    .andReturn(stats.getTotalBytes()).atLeastOnce();
-            expectPolicyDataEnable(TYPE_WIFI, false);
-
-            expectForceUpdate();
-            expectClearNotifications();
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
+                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
-            replay();
             mNetworkObserver.limitReached(null, TEST_IFACE);
+
             assertNotificationType(TYPE_LIMIT, tagFuture.get());
-            verifyAndReset();
+            verifyPolicyDataEnable(TYPE_WIFI, false);
         }
 
         // now snooze policy, which should remove quota
@@ -731,35 +561,27 @@
 
         {
             expectCurrentTime();
-            expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
-            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
-                    .andReturn(stats.getTotalBytes()).atLeastOnce();
-            expectPolicyDataEnable(TYPE_WIFI, true);
-
-            // snoozed interface still has high quota so background data is
-            // still restricted.
-            expectRemoveInterfaceQuota(TEST_IFACE);
-            expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
-            expectAdvisePersistThreshold();
-            expectMeteredIfacesChanged(TEST_IFACE);
-
-            future = expectClearNotifications();
+            when(mConnManager.getAllNetworkState()).thenReturn(state);
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
+                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
-            replay();
+            mPolicyListener.expect().onMeteredIfacesChanged(any());
             mService.snoozeLimit(sTemplateWifi);
+            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
+
             assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get());
-            future.get();
-            verifyAndReset();
+            // snoozed interface still has high quota so background data is
+            // still restricted.
+            verifyRemoveInterfaceQuota(TEST_IFACE);
+            verifySetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
+            verifyPolicyDataEnable(TYPE_WIFI, true);
         }
     }
 
-    @Suppress
     public void testMeteredNetworkWithoutLimit() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
-        Future<Void> future;
-        Future<String> tagFuture;
 
         final long TIME_FEB_15 = 1171497600000L;
         final long TIME_MAR_10 = 1173484800000L;
@@ -774,24 +596,19 @@
 
         {
             expectCurrentTime();
-            expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
-            expect(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
-                    .andReturn(stats.getTotalBytes()).atLeastOnce();
-            expectPolicyDataEnable(TYPE_WIFI, true);
+            when(mConnManager.getAllNetworkState()).thenReturn(state);
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
+                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
 
-            expectRemoveInterfaceQuota(TEST_IFACE);
-            expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
-
-            expectClearNotifications();
-            expectAdvisePersistThreshold();
-            future = expectMeteredIfacesChanged(TEST_IFACE);
-
-            replay();
+            mPolicyListener.expect().onMeteredIfacesChanged(any());
             setNetworkPolicies(new NetworkPolicy(
                     sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED,
                     true));
-            future.get();
-            verifyAndReset();
+            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
+
+            verifyPolicyDataEnable(TYPE_WIFI, true);
+            verifyRemoveInterfaceQuota(TEST_IFACE);
+            verifySetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
         }
     }
 
@@ -814,87 +631,36 @@
     }
 
     private void expectCurrentTime() throws Exception {
-        expect(mTime.forceRefresh()).andReturn(false).anyTimes();
-        expect(mTime.hasCache()).andReturn(true).anyTimes();
-        expect(mTime.currentTimeMillis()).andReturn(currentTimeMillis()).anyTimes();
-        expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
-        expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
-    }
-
-    private void expectForceUpdate() throws Exception {
-        mStatsService.forceUpdate();
-        expectLastCall().atLeastOnce();
-    }
-
-    private Future<Void> expectClearNotifications() throws Exception {
-        final FutureAnswer future = new FutureAnswer();
-        mNotifManager.cancelNotificationWithTag(
-                isA(String.class), isA(String.class), anyInt(), anyInt());
-        expectLastCall().andAnswer(future).anyTimes();
-        return future;
+        when(mTime.forceRefresh()).thenReturn(false);
+        when(mTime.hasCache()).thenReturn(true);
+        when(mTime.currentTimeMillis()).thenReturn(currentTimeMillis());
+        when(mTime.getCacheAge()).thenReturn(0L);
+        when(mTime.getCacheCertainty()).thenReturn(0L);
     }
 
     private Future<String> expectEnqueueNotification() throws Exception {
-        final FutureCapture<String> tag = new FutureCapture<String>();
-        mNotifManager.enqueueNotificationWithTag(isA(String.class), isA(String.class),
-                capture(tag.capture), anyInt(),
-                isA(Notification.class), isA(int[].class), UserHandle.myUserId());
-        return tag;
+        final FutureAnswer<String> futureAnswer = new FutureAnswer<String>(2);
+        doAnswer(futureAnswer).when(mNotifManager).enqueueNotificationWithTag(
+                anyString(), anyString(), anyString() /* capture here (index 2)*/,
+                anyInt(), isA(Notification.class), isA(int[].class), anyInt());
+        return futureAnswer;
     }
 
-    private void expectSetInterfaceQuota(String iface, long quotaBytes) throws Exception {
-        mNetworkManager.setInterfaceQuota(iface, quotaBytes);
-        expectLastCall().atLeastOnce();
+    private void verifySetInterfaceQuota(String iface, long quotaBytes) throws Exception {
+        verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(iface, quotaBytes);
     }
 
-    private void expectRemoveInterfaceQuota(String iface) throws Exception {
-        mNetworkManager.removeInterfaceQuota(iface);
-        expectLastCall().atLeastOnce();
+    private void verifyRemoveInterfaceQuota(String iface) throws Exception {
+        verify(mNetworkManager, atLeastOnce()).removeInterfaceQuota(iface);
     }
 
-    private void expectSetInterfaceAlert(String iface, long alertBytes) throws Exception {
-        mNetworkManager.setInterfaceAlert(iface, alertBytes);
-        expectLastCall().atLeastOnce();
-    }
-
-    private void expectRemoveInterfaceAlert(String iface) throws Exception {
-        mNetworkManager.removeInterfaceAlert(iface);
-        expectLastCall().atLeastOnce();
-    }
-
-    private void expectSetUidMeteredNetworkBlacklist(int uid, boolean rejectOnQuotaInterfaces)
-            throws Exception {
-        mNetworkManager.setUidMeteredNetworkBlacklist(uid, rejectOnQuotaInterfaces);
-        expectLastCall().atLeastOnce();
-    }
-
-    private void expectSetUidForeground(int uid, boolean uidForeground) throws Exception {
-        mStatsService.setUidForeground(uid, uidForeground);
-        expectLastCall().atLeastOnce();
-    }
-
-    private Future<Void> expectRulesChanged(int uid, int policy) throws Exception {
-        final FutureAnswer future = new FutureAnswer();
-        mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
-        expectLastCall().andAnswer(future);
-        return future;
-    }
-
-    private Future<Void> expectMeteredIfacesChanged(String... ifaces) throws Exception {
-        final FutureAnswer future = new FutureAnswer();
-        mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
-        expectLastCall().andAnswer(future);
-        return future;
-    }
-
-    private Future<Void> expectPolicyDataEnable(int type, boolean enabled) throws Exception {
+    private Future<Void> verifyPolicyDataEnable(int type, boolean enabled) throws Exception {
         // TODO: bring back this test
         return null;
     }
 
-    private void expectAdvisePersistThreshold() throws Exception {
-        mStatsService.advisePersistThreshold(anyLong());
-        expectLastCall().anyTimes();
+    private void verifyAdvisePersistThreshold() throws Exception {
+        verify(mStatsService).advisePersistThreshold(anyLong());
     }
 
     private static class TestAbstractFuture<T> extends AbstractFuture<T> {
@@ -908,50 +674,21 @@
         }
     }
 
-    private static class FutureAnswer extends TestAbstractFuture<Void> implements IAnswer<Void> {
+    private static class FutureAnswer<T> extends TestAbstractFuture<T> implements Answer<Void> {
+        private final int index;
+
+        FutureAnswer(int index) {
+            this.index = index;
+        }
         @Override
-        public Void answer() {
-            set(null);
+        public Void answer(InvocationOnMock invocation) throws Throwable {
+            @SuppressWarnings("unchecked")
+            T captured = (T) invocation.getArguments()[index];
+            set(captured);
             return null;
         }
     }
 
-    private static class FutureCapture<T> extends TestAbstractFuture<T> {
-        public Capture<T> capture = new Capture<T>() {
-            @Override
-            public void setValue(T value) {
-                super.setValue(value);
-                set(value);
-            }
-        };
-    }
-
-    private static class IdleFuture extends AbstractFuture<Void> implements IdleHandler {
-        @Override
-        public Void get() throws InterruptedException, ExecutionException {
-            try {
-                return get(5, TimeUnit.SECONDS);
-            } catch (TimeoutException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        @Override
-        public boolean queueIdle() {
-            set(null);
-            return false;
-        }
-    }
-
-    /**
-     * Wait until {@link #mService} internal {@link Handler} is idle.
-     */
-    private IdleFuture expectIdle() {
-        final IdleFuture future = new IdleFuture();
-        mService.addIdleHandler(future);
-        return future;
-    }
-
     private static void assertTimeEquals(long expected, long actual) {
         if (expected != actual) {
             fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));
@@ -979,7 +716,7 @@
     }
 
     private static void assertNotificationType(int expected, String actualTag) {
-        assertEquals(
+        assertEquals("notification type mismatch for '" + actualTag +"'",
                 Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
     }
 
@@ -1000,15 +737,78 @@
         mElapsedRealtime += duration;
     }
 
-    private void replay() {
-        EasyMock.replay(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
-                mConnManager, mNotifManager);
+    /**
+     * Creates a mock and registers it to {@link LocalServices}.
+     */
+    private <T> T addLocalServiceMock(Class<T> clazz) {
+        final T mock = mock(clazz);
+        return addLocalServiceMock(clazz, mock);
     }
 
-    private void verifyAndReset() {
-        EasyMock.verify(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
-                mConnManager, mNotifManager);
-        EasyMock.reset(mActivityManager, mStatsService, mPolicyListener, mNetworkManager, mTime,
-                mConnManager, mNotifManager);
+    /**
+     * Registers a mock to {@link LocalServices}.
+     */
+    private <T> T addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.addService(clazz, mock);
+        mLocalServices.add(clazz);
+        return mock;
+    }
+
+    /**
+     * Unregisters all mocks from {@link LocalServices}.
+     */
+    private void removeLocalServiceMocks() {
+        for (Class<?> clazz : mLocalServices) {
+            Log.d(TAG, "removeLocalServiceMock(): " + clazz.getName());
+            LocalServices.removeServiceForTest(clazz);
+        }
+        mLocalServices.clear();
+    }
+
+    /**
+     * Custom Mockito answer used to verify async {@link INetworkPolicyListener} calls.
+     *
+     * <p>Typical usage:
+     * <pre><code>
+     *    mPolicyListener.expect().someCallback(any());
+     *    // do something on objects under test
+     *    mPolicyListener.waitAndVerify().someCallback(eq(expectedValue));
+     * </code></pre>
+     */
+    final class NetworkPolicyListenerAnswer implements Answer<Void> {
+        private CountDownLatch latch;
+        private final INetworkPolicyListener listener;
+
+        NetworkPolicyListenerAnswer(NetworkPolicyManagerService service) {
+            this.listener = mock(INetworkPolicyListener.class);
+            // RemoteCallbackList needs a binder to use as key
+            when(listener.asBinder()).thenReturn(new Binder());
+            service.registerListener(listener);
+        }
+
+        @Override
+        public Void answer(InvocationOnMock invocation) throws Throwable {
+            Log.d(TAG,"counting down on answer: " + invocation);
+            latch.countDown();
+            return null;
+        }
+
+        INetworkPolicyListener expect() {
+            assertNull("expect() called before waitAndVerify()", latch);
+            latch = new CountDownLatch(1);
+            return doAnswer(this).when(listener);
+        }
+
+        INetworkPolicyListener waitAndVerify() {
+            assertNotNull("waitAndVerify() called before expect()", latch);
+            try {
+                assertTrue("callback not called in 5 seconds", latch.await(5, TimeUnit.SECONDS));
+            } catch (InterruptedException e) {
+                fail("Thread interrupted before callback called");
+            } finally {
+                latch = null;
+            }
+            return verify(listener, atLeastOnce());
+        }
     }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 8560651..efd479f 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -563,6 +563,7 @@
                             mAccessoryModeRequestTime + ACCESSORY_REQUEST_TIMEOUT;
 
             if (mConfigured && enteringAccessoryMode) {
+                mAccessoryModeRequestTime = 0;
                 // successfully entered accessory mode
 
                 if (mAccessoryStrings != null) {
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index d7d4e84..f5e422d 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -212,6 +212,20 @@
      */
     public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53;
 
+    /**
+     * The call was terminated because cellular data has been disabled.
+     * Used when in a video call and the user disables cellular data via the settings.
+     * {@hide}
+     */
+    public static final int DATA_DISABLED = 54;
+
+    /**
+     * The call was terminated because the data policy has disabled cellular data.
+     * Used when in a video call and the user has exceeded the device data limit.
+     * {@hide}
+     */
+    public static final int DATA_LIMIT_REACHED = 55;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Please assign the new type the next id value below.
@@ -220,14 +234,14 @@
     // 4) Update toString() with the newly added disconnect type.
     // 5) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause.
     //
-    // NextId: 54
+    // NextId: 56
     //*********************************************************************************************
 
     /** Smallest valid value for call disconnect codes. */
     public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
 
     /** Largest valid value for call disconnect codes. */
-    public static final int MAXIMUM_VALID_VALUE = MAXIMUM_NUMBER_OF_CALLS_REACHED;
+    public static final int MAXIMUM_VALID_VALUE = DATA_LIMIT_REACHED;
 
     /** Private constructor to avoid class instantiation. */
     private DisconnectCause() {
@@ -343,6 +357,10 @@
             return "ANSWERED_ELSEWHERE";
         case MAXIMUM_NUMBER_OF_CALLS_REACHED:
             return "MAXIMUM_NUMER_OF_CALLS_REACHED";
+        case DATA_DISABLED:
+            return "DATA_DISABLED";
+        case DATA_LIMIT_REACHED:
+            return "DATA_LIMIT_REACHED";
         default:
             return "INVALID: " + cause;
         }
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 408ad31..56b8822 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -298,6 +298,16 @@
     public static final int CODE_REMOTE_CALL_DECLINE = 1404;
 
     /**
+     * Indicates the call was disconnected due to the user reaching their data limit.
+     */
+    public static final int CODE_DATA_LIMIT_REACHED = 1405;
+
+    /**
+     * Indicates the call was disconnected due to the user disabling cellular data.
+     */
+    public static final int CODE_DATA_DISABLED = 1406;
+
+    /**
      * Network string error messages.
      * mExtraMessage may have these values.
      */
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
index 0d7f06b..5d3abc6 100644
--- a/tools/aapt/ApkBuilder.h
+++ b/tools/aapt/ApkBuilder.h
@@ -32,7 +32,7 @@
 
 class ApkBuilder : public android::RefBase {
 public:
-    ApkBuilder(const sp<WeakResourceFilter>& configFilter);
+    explicit ApkBuilder(const sp<WeakResourceFilter>& configFilter);
 
     /**
      * Tells the builder to generate a separate APK for resources that
diff --git a/tools/aapt/CacheUpdater.h b/tools/aapt/CacheUpdater.h
index 10a1bbc..6fa96d6 100644
--- a/tools/aapt/CacheUpdater.h
+++ b/tools/aapt/CacheUpdater.h
@@ -51,7 +51,7 @@
 class SystemCacheUpdater : public CacheUpdater {
 public:
     // Constructor to set bundle to pass to preProcessImage
-    SystemCacheUpdater (Bundle* b)
+    explicit SystemCacheUpdater (Bundle* b)
         : bundle(b) { };
 
     // Make sure all the directories along this path exist
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index cbad4b9..d80aaba 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -248,7 +248,7 @@
 }
 
 static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
-        uint32_t attrRes, String8 attrLabel, String8* outError)
+        uint32_t attrRes, const String8& attrLabel, String8* outError)
 {
     Res_value value;
     AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
@@ -399,7 +399,7 @@
             ResTable::normalizeForOutput(reason.string()).string());
 }
 
-Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
+Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
         String8 *outError = NULL)
 {
     Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h
index 4f999a2..09430f2 100644
--- a/tools/aapt/ConfigDescription.h
+++ b/tools/aapt/ConfigDescription.h
@@ -29,7 +29,7 @@
         size = sizeof(android::ResTable_config);
     }
 
-    ConfigDescription(const android::ResTable_config&o) {
+    ConfigDescription(const android::ResTable_config&o) {  // NOLINT(implicit)
         *static_cast<android::ResTable_config*>(this) = o;
         size = sizeof(android::ResTable_config);
     }
diff --git a/tools/aapt/CrunchCache.cpp b/tools/aapt/CrunchCache.cpp
index 0d574cf..7b8a576 100644
--- a/tools/aapt/CrunchCache.cpp
+++ b/tools/aapt/CrunchCache.cpp
@@ -94,7 +94,7 @@
     delete dw;
 }
 
-bool CrunchCache::needsUpdating(String8 relativePath) const
+bool CrunchCache::needsUpdating(const String8& relativePath) const
 {
     // Retrieve modification dates for this file entry under the source and
     // cache directory trees. The vectors will return a modification date of 0
diff --git a/tools/aapt/CrunchCache.h b/tools/aapt/CrunchCache.h
index be3da5c..4d6a169 100644
--- a/tools/aapt/CrunchCache.h
+++ b/tools/aapt/CrunchCache.h
@@ -81,7 +81,7 @@
      *          // Recrunch sourceFile out to destFile.
      *
      */
-    bool needsUpdating(String8 relativePath) const;
+    bool needsUpdating(const String8& relativePath) const;
 
     // DATA MEMBERS ====================================================
 
diff --git a/tools/aapt/FileFinder.cpp b/tools/aapt/FileFinder.cpp
index 18775c0..c9d0744 100644
--- a/tools/aapt/FileFinder.cpp
+++ b/tools/aapt/FileFinder.cpp
@@ -77,7 +77,7 @@
     return true;
 }
 
-void SystemFileFinder::checkAndAddFile(String8 path, const struct stat* stats,
+void SystemFileFinder::checkAndAddFile(const String8& path, const struct stat* stats,
                                        Vector<String8>& extensions,
                                        KeyedVector<String8,time_t>& fileStore)
 {
diff --git a/tools/aapt/FileFinder.h b/tools/aapt/FileFinder.h
index 6974aee..f405381 100644
--- a/tools/aapt/FileFinder.h
+++ b/tools/aapt/FileFinder.h
@@ -72,7 +72,7 @@
      *    time as the value.
      *
      */
-    static void checkAndAddFile(String8 path, const struct stat* stats,
+    static void checkAndAddFile(const String8& path, const struct stat* stats,
                                 Vector<String8>& extensions,
                                 KeyedVector<String8,time_t>& fileStore);
 
diff --git a/tools/aapt/IndentPrinter.h b/tools/aapt/IndentPrinter.h
index 6fc94bc..bd0edcb 100644
--- a/tools/aapt/IndentPrinter.h
+++ b/tools/aapt/IndentPrinter.h
@@ -3,7 +3,7 @@
 
 class IndentPrinter {
 public:
-    IndentPrinter(FILE* stream, int indentSize=2)
+    explicit IndentPrinter(FILE* stream, int indentSize=2)
         : mStream(stream)
         , mIndentSize(indentSize)
         , mIndent(0)
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index b278831..b4c4d05 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -399,7 +399,7 @@
     const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles();
     int N = groups.size();
     for (int i=0; i<N; i++) {
-        String8 leafName = groups.keyAt(i);
+        const String8& leafName = groups.keyAt(i);
         const sp<AaptGroup>& group = groups.valueAt(i);
 
         const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files
@@ -422,7 +422,7 @@
             set->add(leafName, group);
             resources->add(resType, set);
         } else {
-            sp<ResourceTypeSet> set = resources->valueAt(index);
+            const sp<ResourceTypeSet>& set = resources->valueAt(index);
             index = set->indexOfKey(leafName);
             if (index < 0) {
                 if (kIsDebug) {
@@ -457,7 +457,7 @@
     int N = dirs.size();
 
     for (int i=0; i<N; i++) {
-        sp<AaptDir> d = dirs.itemAt(i);
+        const sp<AaptDir>& d = dirs.itemAt(i);
         if (kIsDebug) {
             printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
                     d->getLeaf().string());
@@ -615,7 +615,7 @@
         // get the overlay resources of the requested type
         ssize_t index = overlayRes->indexOfKey(resTypeString);
         if (index >= 0) {
-            sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index);
+            const sp<ResourceTypeSet>& overlaySet = overlayRes->valueAt(index);
 
             // for each of the resources, check for a match in the previously built
             // non-overlay "baseset".
@@ -765,7 +765,7 @@
     return addTagAttribute(node, ns8, attr8, value, errorOnFailedInsert, false);
 }
 
-static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
+static void fullyQualifyClassName(const String8& package, const sp<XMLNode>& node,
         const String16& attrName) {
     XMLNode::attribute_entry* attr = node->editAttribute(
             String16("http://schemas.android.com/apk/res/android"), attrName);
@@ -1350,7 +1350,7 @@
             ResourceDirIterator it(resources->valueAt(index), String8("values"));
             ssize_t res;
             while ((res=it.next()) == NO_ERROR) {
-                sp<AaptFile> file = it.getFile();
+                const sp<AaptFile>& file = it.getFile();
                 res = compileResourceFile(bundle, assets, file, it.getParams(), 
                                           (current!=assets), &table);
                 if (res != NO_ERROR) {
@@ -2688,7 +2688,7 @@
         String8 dest(bundle->getRClassDir());
 
         if (bundle->getMakePackageDirs()) {
-            String8 pkg(package);
+            const String8& pkg(package);
             const char* last = pkg.string();
             const char* s = last-1;
             do {
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index d6430c0..40d5b75 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -78,7 +78,7 @@
 class StrongResourceFilter : public ResourceFilter {
 public:
     StrongResourceFilter() {}
-    StrongResourceFilter(const std::set<ConfigDescription>& configs)
+    explicit StrongResourceFilter(const std::set<ConfigDescription>& configs)
         : mConfigs(configs) {}
 
     android::status_t parse(const android::String8& str);
@@ -106,7 +106,7 @@
  */
 class InverseResourceFilter : public ResourceFilter {
 public:
-    InverseResourceFilter(const android::sp<ResourceFilter>& filter)
+    explicit InverseResourceFilter(const android::sp<ResourceFilter>& filter)
         : mFilter(filter) {}
 
     bool match(const android::ResTable_config& config) const {
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 4d5bb31..76c59dd 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -4081,7 +4081,7 @@
     
     j = 0;
     for (i=0; i<N; i++) {
-        sp<ConfigList> e = origOrder.itemAt(i);
+        const sp<ConfigList>& e = origOrder.itemAt(i);
         // There will always be enough room for the remaining entries.
         while (mOrderedConfigs.itemAt(j) != NULL) {
             j++;
@@ -4203,7 +4203,7 @@
 
     size_t j=0;
     for (i=0; i<N; i++) {
-        sp<Type> t = origOrder.itemAt(i);
+        const sp<Type>& t = origOrder.itemAt(i);
         // There will always be enough room for the remaining types.
         while (mOrderedTypes.itemAt(j) != NULL) {
             j++;
@@ -4636,7 +4636,7 @@
                         c->getEntries();
                 const size_t entryCount = entries.size();
                 for (size_t ei = 0; ei < entryCount; ei++) {
-                    sp<Entry> e = entries.valueAt(ei);
+                    const sp<Entry>& e = entries.valueAt(ei);
                     if (e == NULL || e->getType() != Entry::TYPE_BAG) {
                         continue;
                     }
diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h
index 4b0d920..625b0bf 100644
--- a/tools/aapt/StringPool.h
+++ b/tools/aapt/StringPool.h
@@ -41,7 +41,7 @@
 public:
     struct entry {
         entry() : offset(0) { }
-        entry(const String16& _value) : value(_value), offset(0), hasStyles(false) { }
+        explicit entry(const String16& _value) : value(_value), offset(0), hasStyles(false) { }
         entry(const entry& o) : value(o.value), offset(o.offset),
                 hasStyles(o.hasStyles), indices(o.indices),
                 configTypeName(o.configTypeName), configs(o.configs) { }
diff --git a/tools/aapt/WorkQueue.h b/tools/aapt/WorkQueue.h
index d38f05d..ab5f969 100644
--- a/tools/aapt/WorkQueue.h
+++ b/tools/aapt/WorkQueue.h
@@ -47,7 +47,7 @@
     };
 
     /* Creates a work queue with the specified maximum number of work threads. */
-    WorkQueue(size_t maxThreads, bool canCallJava = true);
+    explicit WorkQueue(size_t maxThreads, bool canCallJava = true);
 
     /* Destroys the work queue.
      * Cancels pending work and waits for all remaining threads to complete.
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 5b215da..15ec4af 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -67,7 +67,7 @@
 static const String16 RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE);
 static const String16 RESOURCES_TOOLS_NAMESPACE("http://schemas.android.com/tools");
 
-String16 getNamespaceResourcePackage(String16 appPackage, String16 namespaceUri, bool* outIsPublic)
+String16 getNamespaceResourcePackage(const String16& appPackage, const String16& namespaceUri, bool* outIsPublic)
 {
     //printf("%s starts with %s?\n", String8(namespaceUri).string(),
     //       String8(RESOURCES_PREFIX).string());
@@ -98,7 +98,7 @@
 
 status_t hasSubstitutionErrors(const char* fileName,
                                ResXMLTree* inXml,
-                               String16 str16)
+                               const String16& str16)
 {
     const char16_t* str = str16.string();
     const char16_t* p = str;
diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h
index 749bf9f..ac92018 100644
--- a/tools/aapt/XMLNode.h
+++ b/tools/aapt/XMLNode.h
@@ -178,7 +178,7 @@
     XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace);
     
     // Creating a CDATA node.
-    XMLNode(const String8& filename);
+    explicit XMLNode(const String8& filename);
     
     status_t collect_strings(StringPool* dest, Vector<uint32_t>* outResIds,
             bool stripComments, bool stripRawValues) const;
diff --git a/tools/aapt/pseudolocalize.h b/tools/aapt/pseudolocalize.h
index 1faecd1..9bb1fd8 100644
--- a/tools/aapt/pseudolocalize.h
+++ b/tools/aapt/pseudolocalize.h
@@ -43,7 +43,7 @@
 
 class Pseudolocalizer {
  public:
-  Pseudolocalizer(PseudolocalizationMethod m);
+  explicit Pseudolocalizer(PseudolocalizationMethod m);
   ~Pseudolocalizer() { if (mImpl) delete mImpl; }
   void setMethod(PseudolocalizationMethod m);
   String16 start() { return mImpl->start(); }
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index ef0d147..6858c62 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -51,7 +51,7 @@
     static void applyVersionForCompatibility(ConfigDescription* config);
 
     ConfigDescription();
-    ConfigDescription(const android::ResTable_config& o);
+    ConfigDescription(const android::ResTable_config& o);  // NOLINT(implicit)
     ConfigDescription(const ConfigDescription& o);
     ConfigDescription(ConfigDescription&& o);
 
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 0ba0345..09a04e0 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -100,7 +100,7 @@
     ResourceNameRef() = default;
     ResourceNameRef(const ResourceNameRef&) = default;
     ResourceNameRef(ResourceNameRef&&) = default;
-    ResourceNameRef(const ResourceName& rhs);
+    ResourceNameRef(const ResourceName& rhs);  // NOLINT(implicit)
     ResourceNameRef(const StringPiece& p, ResourceType t, const StringPiece& e);
     ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
     ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
@@ -126,7 +126,7 @@
 
     ResourceId();
     ResourceId(const ResourceId& rhs);
-    ResourceId(uint32_t resId);
+    ResourceId(uint32_t resId);  // NOLINT(implicit)
     ResourceId(uint8_t p, uint8_t t, uint16_t e);
 
     bool isValid() const;
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 4d418d9..460de0e 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -274,7 +274,7 @@
 }
 
 bool ResourceTable::addResource(const ResourceNameRef& name,
-                                const ResourceId resId,
+                                const ResourceId& resId,
                                 const ConfigDescription& config,
                                 const StringPiece& product,
                                 std::unique_ptr<Value> value,
@@ -325,7 +325,7 @@
 }
 
 bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
-                                            const ResourceId id,
+                                            const ResourceId& id,
                                             const ConfigDescription& config,
                                             const StringPiece& product,
                                             std::unique_ptr<Value> value,
@@ -335,12 +335,12 @@
 }
 
 bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
-                                    const ResourceId resId,
+                                    const ResourceId& resId,
                                     const ConfigDescription& config,
                                     const StringPiece& product,
                                     std::unique_ptr<Value> value,
                                     const char* validChars,
-                                    std::function<int(Value*,Value*)> conflictResolver,
+                                    const std::function<int(Value*,Value*)>& conflictResolver,
                                     IDiagnostics* diag) {
     assert(value && "value can't be nullptr");
     assert(diag && "diagnostics can't be nullptr");
@@ -426,18 +426,18 @@
     return true;
 }
 
-bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId,
+bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId& resId,
                                    const Symbol& symbol, IDiagnostics* diag) {
     return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
 }
 
 bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
-                                               const ResourceId resId,
+                                               const ResourceId& resId,
                                                const Symbol& symbol, IDiagnostics* diag) {
     return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
 }
 
-bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId,
+bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId& resId,
                                        const Symbol& symbol, const char* validChars,
                                        IDiagnostics* diag) {
     assert(diag && "diagnostics can't be nullptr");
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index a5efe35..6b52a43 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -103,7 +103,7 @@
      */
     std::vector<std::unique_ptr<ResourceConfigValue>> values;
 
-    ResourceEntry(const StringPiece& name) : name(name.toString()) { }
+    explicit ResourceEntry(const StringPiece& name) : name(name.toString()) { }
 
     ResourceConfigValue* findValue(const ConfigDescription& config);
     ResourceConfigValue* findValue(const ConfigDescription& config, const StringPiece& product);
@@ -200,7 +200,7 @@
                      IDiagnostics* diag);
 
     bool addResource(const ResourceNameRef& name,
-                     const ResourceId resId,
+                     const ResourceId& resId,
                      const ConfigDescription& config,
                      const StringPiece& product,
                      std::unique_ptr<Value> value,
@@ -231,19 +231,19 @@
                                  IDiagnostics* diag);
 
     bool addResourceAllowMangled(const ResourceNameRef& name,
-                                 const ResourceId id,
+                                 const ResourceId& id,
                                  const ConfigDescription& config,
                                  const StringPiece& product,
                                  std::unique_ptr<Value> value,
                                  IDiagnostics* diag);
 
     bool setSymbolState(const ResourceNameRef& name,
-                        const ResourceId resId,
+                        const ResourceId& resId,
                         const Symbol& symbol,
                         IDiagnostics* diag);
 
     bool setSymbolStateAllowMangled(const ResourceNameRef& name,
-                                    const ResourceId resId,
+                                    const ResourceId& resId,
                                     const Symbol& symbol,
                                     IDiagnostics* diag);
 
@@ -294,16 +294,16 @@
                               IDiagnostics* diag);
 
     bool addResourceImpl(const ResourceNameRef& name,
-                         ResourceId resId,
+                         const ResourceId& resId,
                          const ConfigDescription& config,
                          const StringPiece& product,
                          std::unique_ptr<Value> value,
                          const char* validChars,
-                         std::function<int(Value*,Value*)> conflictResolver,
+                         const std::function<int(Value*,Value*)>& conflictResolver,
                          IDiagnostics* diag);
 
     bool setSymbolStateImpl(const ResourceNameRef& name,
-                            ResourceId resId,
+                            const ResourceId& resId,
                             const Symbol& symbol,
                             const char* validChars,
                             IDiagnostics* diag);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 7dc88ded..773616d 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -545,7 +545,7 @@
 std::unique_ptr<Item> parseItemForAttribute(
         const StringPiece& value,
         uint32_t typeMask,
-        std::function<void(const ResourceName&)> onCreateReference) {
+        const std::function<void(const ResourceName&)>& onCreateReference) {
     std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
     if (nullOrEmpty) {
         return std::move(nullOrEmpty);
@@ -604,7 +604,7 @@
  */
 std::unique_ptr<Item> parseItemForAttribute(
         const StringPiece& str, const Attribute* attr,
-        std::function<void(const ResourceName&)> onCreateReference) {
+        const std::function<void(const ResourceName&)>& onCreateReference) {
     const uint32_t typeMask = attr->typeMask;
     std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference);
     if (value) {
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 31b8e89..a57d89d 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -163,11 +163,11 @@
  */
 std::unique_ptr<Item> parseItemForAttribute(
         const StringPiece& value, const Attribute* attr,
-        std::function<void(const ResourceName&)> onCreateReference = {});
+        const std::function<void(const ResourceName&)>& onCreateReference = {});
 
 std::unique_ptr<Item> parseItemForAttribute(
         const StringPiece& value, uint32_t typeMask,
-        std::function<void(const ResourceName&)> onCreateReference = {});
+        const std::function<void(const ResourceName&)>& onCreateReference = {});
 
 uint32_t androidTypeToAttributeTypeMask(uint16_t type);
 
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 8ae71ad..e6af716 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -198,7 +198,7 @@
 struct RawString : public BaseItem<RawString> {
     StringPool::Ref value;
 
-    RawString(const StringPool::Ref& ref);
+    explicit RawString(const StringPool::Ref& ref);
 
     bool equals(const Value* value) const override;
     bool flatten(android::Res_value* outValue) const override;
@@ -209,7 +209,7 @@
 struct String : public BaseItem<String> {
     StringPool::Ref value;
 
-    String(const StringPool::Ref& ref);
+    explicit String(const StringPool::Ref& ref);
 
     bool equals(const Value* value) const override;
     bool flatten(android::Res_value* outValue) const override;
@@ -220,7 +220,7 @@
 struct StyledString : public BaseItem<StyledString> {
     StringPool::StyleRef value;
 
-    StyledString(const StringPool::StyleRef& ref);
+    explicit StyledString(const StringPool::StyleRef& ref);
 
     bool equals(const Value* value) const override;
     bool flatten(android::Res_value* outValue) const override;
@@ -237,7 +237,7 @@
     io::IFile* file = nullptr;
 
     FileReference() = default;
-    FileReference(const StringPool::Ref& path);
+    explicit FileReference(const StringPool::Ref& path);
 
     bool equals(const Value* value) const override;
     bool flatten(android::Res_value* outValue) const override;
@@ -252,7 +252,7 @@
     android::Res_value value;
 
     BinaryPrimitive() = default;
-    BinaryPrimitive(const android::Res_value& val);
+    explicit BinaryPrimitive(const android::Res_value& val);
     BinaryPrimitive(uint8_t dataType, uint32_t data);
 
     bool equals(const Value* value) const override;
@@ -272,7 +272,7 @@
     int32_t maxInt;
     std::vector<Symbol> symbols;
 
-    Attribute(bool w, uint32_t t = 0u);
+    explicit Attribute(bool w, uint32_t t = 0u);
 
     bool equals(const Value* value) const override;
     Attribute* clone(StringPool* newPool) const override;
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 91e755d..ccf0383 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -52,7 +52,7 @@
     return p.first < entryId;
 }
 
-size_t findAttributeSdkLevel(ResourceId id) {
+size_t findAttributeSdkLevel(const ResourceId& id) {
     if (id.packageId() != 0x01 && id.typeId() != 0x01) {
         return 0;
     }
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index f28679f..c9dbdca 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -47,7 +47,7 @@
     SDK_MARSHMALLOW = 23,
 };
 
-size_t findAttributeSdkLevel(ResourceId id);
+size_t findAttributeSdkLevel(const ResourceId& id);
 size_t findAttributeSdkLevel(const ResourceName& name);
 std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion();
 
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 72ae9d1..13545be 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -65,7 +65,7 @@
     private:
         friend class StringPool;
 
-        Ref(Entry* entry);
+        explicit Ref(Entry* entry);
 
         Entry* mEntry;
     };
@@ -88,7 +88,7 @@
     private:
         friend class StringPool;
 
-        StyleRef(StyleEntry* entry);
+        explicit StyleRef(StyleEntry* entry);
 
         StyleEntry* mEntry;
     };
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 501ae9d..4a3f1e1 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -28,7 +28,7 @@
  * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and ResourceEntry,
  * as long as there is no existing ID or the ID is the same.
  */
-static bool assignId(IDiagnostics* diag, const ResourceId id, const ResourceName& name,
+static bool assignId(IDiagnostics* diag, const ResourceId& id, const ResourceName& name,
                      ResourceTablePackage* pkg, ResourceTableType* type, ResourceEntry* entry) {
     if (pkg->id.value() == id.packageId()) {
         if (!type->id || type->id.value() == id.typeId()) {
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index 0b92ba5..b25bfa7 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -64,7 +64,7 @@
 
     ::testing::AssertionResult exists(ResTable* table,
                                       const StringPiece& expectedName,
-                                      const ResourceId expectedId,
+                                      const ResourceId& expectedId,
                                       const ConfigDescription& expectedConfig,
                                       const uint8_t expectedDataType, const uint32_t expectedData,
                                       const uint32_t expectedSpecFlags) {
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index d26f2e4..4d1e178 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -43,7 +43,7 @@
     }
 
     ::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree,
-                                       XmlFlattenerOptions options = {}) {
+                                       const XmlFlattenerOptions& options = {}) {
         using namespace android; // For NO_ERROR on windows because it is a macro.
 
         BigBuffer buffer(1024);
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index acb0f38..ff777a3 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -528,7 +528,7 @@
 
 static bool writeStableIdMapToPath(IDiagnostics* diag,
                                    const std::unordered_map<ResourceName, ResourceId>& idMap,
-                                   const std::string idMapPath) {
+                                   const std::string& idMapPath) {
     std::ofstream fout(idMapPath, std::ofstream::binary);
     if (!fout) {
         diag->error(DiagMessage(idMapPath) << strerror(errno));
@@ -823,7 +823,7 @@
     }
 
     bool writeJavaFile(ResourceTable* table, const StringPiece& packageNameToGenerate,
-                       const StringPiece& outPackage, JavaClassGeneratorOptions javaOptions) {
+                       const StringPiece& outPackage, const JavaClassGeneratorOptions& javaOptions) {
         if (!mOptions.generateJavaClassPath) {
             return true;
         }
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 8016f3b..379c991 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -135,7 +135,7 @@
                           const bool manglePackage,
                           const bool overlay,
                           const bool allowNewResources,
-                          FileMergeCallback callback) {
+                          const FileMergeCallback& callback) {
     bool error = false;
 
     for (auto& srcType : srcPackage->types) {
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index 6997f93..3473a27 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -115,7 +115,7 @@
                  const bool manglePackage,
                  const bool overlay,
                  const bool allowNewResources,
-                 FileMergeCallback callback);
+                 const FileMergeCallback& callback);
 
     std::unique_ptr<FileReference> cloneAndMangleFile(const std::string& package,
                                                       const FileReference& value);
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 6c506df..0c92718 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -63,7 +63,7 @@
     return nullptr;
 }
 
-const SymbolTable::Symbol* SymbolTable::findById(ResourceId id) {
+const SymbolTable::Symbol* SymbolTable::findById(const ResourceId& id) {
     if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
         return s.get();
     }
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 43f4dd7..bd01b64 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -86,7 +86,7 @@
      * are typically stored in a cache which may evict entries.
      */
     const Symbol* findByName(const ResourceName& name);
-    const Symbol* findById(ResourceId id);
+    const Symbol* findById(const ResourceId& id);
 
     /**
      * Let's the ISymbolSource decide whether looking up by name or ID is faster, if both
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 98ff87f..ca25c6a 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -228,7 +228,7 @@
             }
 
         } else if (pbValue.has_compound_value()) {
-            const pb::CompoundValue pbCompoundValue = pbValue.compound_value();
+            const pb::CompoundValue& pbCompoundValue = pbValue.compound_value();
             if (pbCompoundValue.has_attr()) {
                 const pb::Attribute& pbAttr = pbCompoundValue.attr();
                 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);