Merge "Tweaks to ViewOverlay." into nyc-dev
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index 2a8773c..d57d0f5 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -339,7 +339,7 @@
                 }
                 if (mDebugBuild) {
                     debugConfigBuilder =
-                            parseConfigEntry(parser, seenDomains, null, CONFIG_DEBUG).get(0).first;
+                            parseConfigEntry(parser, null, null, CONFIG_DEBUG).get(0).first;
                 } else {
                     XmlUtils.skipCurrentTag(parser);
                 }
@@ -348,6 +348,11 @@
                 XmlUtils.skipCurrentTag(parser);
             }
         }
+        // If debug is true and there was no debug-overrides in the file check for an extra
+        // _debug resource.
+        if (mDebugBuild && debugConfigBuilder == null) {
+            debugConfigBuilder = parseDebugOverridesResource();
+        }
 
         // Use the platform default as the parent of the base config for any values not provided
         // there. If there is no base config use the platform default.
@@ -385,6 +390,43 @@
         mDomainMap = configs;
     }
 
+    private NetworkSecurityConfig.Builder parseDebugOverridesResource()
+            throws IOException, XmlPullParserException, ParserException {
+        Resources resources = mContext.getResources();
+        String packageName = resources.getResourcePackageName(mResourceId);
+        String entryName = resources.getResourceEntryName(mResourceId);
+        int resId = resources.getIdentifier(entryName + "_debug", "xml", packageName);
+        // No debug-overrides resource was found, nothing to parse.
+        if (resId == 0) {
+            return null;
+        }
+        NetworkSecurityConfig.Builder debugConfigBuilder = null;
+        // Parse debug-overrides out of the _debug resource.
+        try (XmlResourceParser parser = resources.getXml(resId)) {
+            XmlUtils.beginDocument(parser, "network-security-config");
+            int outerDepth = parser.getDepth();
+            boolean seenDebugOverrides = false;
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                if ("debug-overrides".equals(parser.getName())) {
+                    if (seenDebugOverrides) {
+                        throw new ParserException(parser, "Only one debug-overrides allowed");
+                    }
+                    if (mDebugBuild) {
+                        debugConfigBuilder =
+                                parseConfigEntry(parser, null, null, CONFIG_DEBUG).get(0).first;
+                    } else {
+                        XmlUtils.skipCurrentTag(parser);
+                    }
+                    seenDebugOverrides = true;
+                } else {
+                    XmlUtils.skipCurrentTag(parser);
+                }
+            }
+        }
+
+        return debugConfigBuilder;
+    }
+
     public static class ParserException extends Exception {
 
         public ParserException(XmlPullParser parser, String message, Throwable cause) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7857107..ce5d07c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2440,9 +2440,13 @@
          flag). -->
     <bool name="config_forceWindowDrawsStatusBarBackground">true</bool>
 
-    <!-- If set, this will force the navigation bar to always be drawn with an opaque
-         background. -->
-    <bool name="config_forceNavBarAlwaysOpaque">false</bool>
+    <!-- Controls the opacity of the navigation bar depending on the visibility of the
+         various workspace stacks.
+         0 - Nav bar is always opaque when either the freeform stack or docked stack is visible.
+         1 - Nav bar is always translucent when the freeform stack is visible, otherwise always
+         opaque.
+         -->
+    <integer name="config_navBarOpacityMode">0</integer>
 
     <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
     <string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cad0e7b..5737380 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2394,7 +2394,7 @@
 
   <java-symbol type="string" name="config_packagedKeyboardName" />
   <java-symbol type="bool" name="config_forceWindowDrawsStatusBarBackground" />
-  <java-symbol type="bool" name="config_forceNavBarAlwaysOpaque" />
+  <java-symbol type="integer" name="config_navBarOpacityMode" />
   <java-symbol type="color" name="system_bar_background_semi_transparent" />
 
   <!-- EditText suggestion popup. -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 6ad7aad..9f97c71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -4494,7 +4494,20 @@
         }
 
         private void handlePulseWhileDozing(@NonNull PulseCallback callback, int reason) {
-            mDozeScrimController.pulse(callback, reason);
+            mDozeScrimController.pulse(new PulseCallback() {
+
+                @Override
+                public void onPulseStarted() {
+                    callback.onPulseStarted();
+                    mStackScroller.setPulsing(true);
+                }
+
+                @Override
+                public void onPulseFinished() {
+                    callback.onPulseFinished();
+                    mStackScroller.setPulsing(false);
+                }
+            }, reason);
         }
 
         private void handleStopDozing() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index c6659d1..d9e8bd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -248,9 +248,11 @@
         }
 
         @Override
-        protected void onFocusLost() {
-            super.onFocusLost();
-            defocusIfNeeded();
+        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+            super.onFocusChanged(focused, direction, previouslyFocusedRect);
+            if (!focused) {
+                defocusIfNeeded();
+            }
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 824b8b7..2ea9507 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -324,6 +324,7 @@
         }
     };
     private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
+    private boolean mPulsing;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -2194,7 +2195,7 @@
     }
 
     private void updateNotificationAnimationStates() {
-        boolean running = mAnimationsEnabled;
+        boolean running = mAnimationsEnabled || mPulsing;
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -2204,7 +2205,8 @@
     }
 
     private void updateAnimationState(View child) {
-        updateAnimationState(mAnimationsEnabled && (mIsExpanded || isPinnedHeadsUp(child)), child);
+        updateAnimationState((mAnimationsEnabled || mPulsing)
+                && (mIsExpanded || isPinnedHeadsUp(child)), child);
     }
 
 
@@ -3322,6 +3324,11 @@
         return mIsExpanded;
     }
 
+    public void setPulsing(boolean pulsing) {
+        mPulsing = pulsing;
+        updateNotificationAnimationStates();
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7ff3e66..9392165 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -537,9 +537,11 @@
     final ActivityStarter mActivityStarter;
 
     /** Task stack change listeners. */
-    private RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
+    private final RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
             new RemoteCallbackList<ITaskStackListener>();
 
+    final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
+
     public IntentFirewall mIntentFirewall;
 
     // Whether we should show our dialogs (ANR, crash, etc) or just perform their
@@ -17796,17 +17798,17 @@
             } catch (RemoteException e) {
             }
             if (ii == null) {
-                reportStartInstrumentationFailure(watcher, className,
+                reportStartInstrumentationFailureLocked(watcher, className,
                         "Unable to find instrumentation info for: " + className);
                 return false;
             }
             if (ai == null) {
-                reportStartInstrumentationFailure(watcher, className,
+                reportStartInstrumentationFailureLocked(watcher, className,
                         "Unable to find instrumentation target package: " + ii.targetPackage);
                 return false;
             }
             if (!ai.hasCode()) {
-                reportStartInstrumentationFailure(watcher, className,
+                reportStartInstrumentationFailureLocked(watcher, className,
                         "Instrumentation target has no code: " + ii.targetPackage);
                 return false;
             }
@@ -17821,7 +17823,7 @@
                         + " not allowed because package " + ii.packageName
                         + " does not have a signature matching the target "
                         + ii.targetPackage;
-                reportStartInstrumentationFailure(watcher, className, msg);
+                reportStartInstrumentationFailureLocked(watcher, className, msg);
                 throw new SecurityException(msg);
             }
 
@@ -17852,31 +17854,21 @@
      * @param cn The component name of the instrumentation.
      * @param report The error report.
      */
-    private void reportStartInstrumentationFailure(IInstrumentationWatcher watcher,
+    private void reportStartInstrumentationFailureLocked(IInstrumentationWatcher watcher,
             ComponentName cn, String report) {
         Slog.w(TAG, report);
-        try {
-            if (watcher != null) {
-                Bundle results = new Bundle();
-                results.putString(Instrumentation.REPORT_KEY_IDENTIFIER, "ActivityManagerService");
-                results.putString("Error", report);
-                watcher.instrumentationStatus(cn, -1, results);
-            }
-        } catch (RemoteException e) {
-            Slog.w(TAG, e);
+        if (watcher != null) {
+            Bundle results = new Bundle();
+            results.putString(Instrumentation.REPORT_KEY_IDENTIFIER, "ActivityManagerService");
+            results.putString("Error", report);
+            mInstrumentationReporter.reportStatus(watcher, cn, -1, results);
         }
     }
 
     void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) {
         if (app.instrumentationWatcher != null) {
-            try {
-                // NOTE:  IInstrumentationWatcher *must* be oneway here
-                app.instrumentationWatcher.instrumentationFinished(
-                    app.instrumentationClass,
-                    resultCode,
-                    results);
-            } catch (RemoteException e) {
-            }
+            mInstrumentationReporter.reportFinished(app.instrumentationWatcher,
+                    app.instrumentationClass, resultCode, results);
         }
 
         // Can't call out of the system process with a lock held, so post a message.
diff --git a/services/core/java/com/android/server/am/InstrumentationReporter.java b/services/core/java/com/android/server/am/InstrumentationReporter.java
new file mode 100644
index 0000000..fdbb73e
--- /dev/null
+++ b/services/core/java/com/android/server/am/InstrumentationReporter.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.IInstrumentationWatcher;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+public class InstrumentationReporter {
+    static final boolean DEBUG = false;
+    static final String TAG = ActivityManagerDebugConfig.TAG_AM;
+
+    static final int REPORT_TYPE_STATUS = 0;
+    static final int REPORT_TYPE_FINISHED = 1;
+
+    final Object mLock = new Object();
+    ArrayList<Report> mPendingReports;
+    Thread mThread;
+
+    final class MyThread extends Thread {
+        public MyThread() {
+            super("InstrumentationReporter");
+            if (DEBUG) Slog.d(TAG, "Starting InstrumentationReporter: " + this);
+        }
+
+        @Override
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+            boolean waited = false;
+            while (true) {
+                ArrayList<Report> reports;
+                synchronized (mLock) {
+                    reports = mPendingReports;
+                    mPendingReports = null;
+                    if (reports == null || reports.isEmpty()) {
+                        if (!waited) {
+                            // Sleep for a little bit, to avoid thrashing through threads.
+                            try {
+                                mLock.wait(10000); // 10 seconds
+                            } catch (InterruptedException e) {
+                            }
+                            waited = true;
+                            continue;
+                        } else {
+                            mThread = null;
+                            if (DEBUG) Slog.d(TAG, "Exiting InstrumentationReporter: " + this);
+                            return;
+                        }
+                    }
+                }
+
+                waited = false;
+
+                for (int i=0; i<reports.size(); i++) {
+                    final Report rep = reports.get(i);
+                    try {
+                        if (rep.mType == REPORT_TYPE_STATUS) {
+                            if (DEBUG) Slog.d(TAG, "Dispatch status to " + rep.mWatcher
+                                    + ": " + rep.mName.flattenToShortString()
+                                    + " code=" + rep.mResultCode + " result=" + rep.mResults);
+                            rep.mWatcher.instrumentationStatus(rep.mName, rep.mResultCode,
+                                    rep.mResults);
+                        } else {
+                            if (DEBUG) Slog.d(TAG, "Dispatch finished to " + rep.mWatcher
+                                    + ": " + rep.mName.flattenToShortString()
+                                    + " code=" + rep.mResultCode + " result=" + rep.mResults);
+                            rep.mWatcher.instrumentationFinished(rep.mName, rep.mResultCode,
+                                    rep.mResults);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.i(TAG, "Failure reporting to instrumentation watcher: comp="
+                                + rep.mName + " results=" + rep.mResults);
+                    }
+                }
+            }
+        }
+    }
+
+    final class Report {
+        final int mType;
+        final IInstrumentationWatcher mWatcher;
+        final ComponentName mName;
+        final int mResultCode;
+        final Bundle mResults;
+
+        Report(int type, IInstrumentationWatcher watcher, ComponentName name, int resultCode,
+                Bundle results) {
+            mType = type;
+            mWatcher = watcher;
+            mName = name;
+            mResultCode = resultCode;
+            mResults = results;
+        }
+    }
+
+    public void reportStatus(IInstrumentationWatcher watcher, ComponentName name, int resultCode,
+            Bundle results) {
+        if (DEBUG) Slog.d(TAG, "Report status to " + watcher
+                + ": " + name.flattenToShortString()
+                + " code=" + resultCode + " result=" + results);
+        report(new Report(REPORT_TYPE_STATUS, watcher, name, resultCode, results));
+    }
+
+    public void reportFinished(IInstrumentationWatcher watcher, ComponentName name, int resultCode,
+            Bundle results) {
+        if (DEBUG) Slog.d(TAG, "Report finished to " + watcher
+                + ": " + name.flattenToShortString()
+                + " code=" + resultCode + " result=" + results);
+        report(new Report(REPORT_TYPE_FINISHED, watcher, name, resultCode, results));
+    }
+
+    private void report(Report report) {
+        synchronized (mLock) {
+            if (mThread == null) {
+                mThread = new MyThread();
+                mThread.start();
+            }
+            if (mPendingReports == null) {
+                mPendingReports = new ArrayList<>();
+            }
+            mPendingReports.add(report);
+            mLock.notifyAll();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index ab3cb83..b13fec1 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -26,7 +26,9 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.notification.Condition;
@@ -63,15 +65,18 @@
     private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>();
     private final SparseArray<CalendarTracker> mTrackers = new SparseArray<>();
     private final Handler mWorker;
+    private final HandlerThread mThread;
 
     private boolean mConnected;
     private boolean mRegistered;
     private boolean mBootComplete;  // don't hammer the calendar provider until boot completes.
     private long mNextAlarmTime;
 
-    public EventConditionProvider(Looper worker) {
+    public EventConditionProvider() {
         if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()");
-        mWorker = new Handler(worker);
+        mThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+        mThread.start();
+        mWorker = new Handler(mThread.getLooper());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 9cdece5..0945065 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -50,7 +50,7 @@
             mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
         }
         if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) {
-            mConditionProviders.addSystemProvider(new EventConditionProvider(helper.getLooper()));
+            mConditionProviders.addSystemProvider(new EventConditionProvider());
         }
         mConditionProviders.setCallback(this);
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1603f1c..0cd69c4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -211,6 +211,13 @@
     static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
     static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;
 
+    // Controls navigation bar opacity depending on which workspace stacks are currently
+    // visible.
+    // Nav bar is always opaque when either the freeform stack or docked stack is visible.
+    static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0;
+    // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque.
+    static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1;
+
     static final int APPLICATION_MEDIA_SUBLAYER = -2;
     static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
     static final int APPLICATION_PANEL_SUBLAYER = 1;
@@ -539,7 +546,7 @@
     boolean mForceStatusBar;
     boolean mForceStatusBarFromKeyguard;
     private boolean mForceStatusBarTransparent;
-    boolean mForceNavBarOpaque;
+    int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
     boolean mHideLockScreen;
     boolean mForcingShowNavBar;
     int mForcingShowNavBarLayer;
@@ -1729,8 +1736,8 @@
             mShortPressWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
         }
 
-        mForceNavBarOpaque = res.getBoolean(
-                com.android.internal.R.bool.config_forceNavBarAlwaysOpaque);
+        mNavBarOpacityMode = res.getInteger(
+                com.android.internal.R.integer.config_navBarOpacityMode);
     }
 
     @Override
@@ -7093,7 +7100,7 @@
         // is visible but also when we are resizing for the transitions when docked stack
         // visibility changes.
         mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
-        final boolean forceOpaqueSystemBars = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
+        final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
 
         // apply translucent bar vis flags
         WindowState transWin = isStatusBarKeyguard() && !mHideLockScreen
@@ -7118,14 +7125,11 @@
         }
 
         if ((!areTranslucentBarsAllowed() && transWin != mStatusBar)
-                || forceOpaqueSystemBars) {
-            vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT
-                    | View.SYSTEM_UI_TRANSPARENT);
+                || forceOpaqueStatusBar) {
+            vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
         }
 
-        if (mForceNavBarOpaque) {
-            vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
-        }
+        vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
 
         if (mForceWindowDrawsStatusBarBackground) {
             vis |= View.STATUS_BAR_TRANSPARENT;
@@ -7199,6 +7203,41 @@
         return vis;
     }
 
+    /**
+     * @return the current visibility flags with the nav-bar opacity related flags toggled based
+     *         on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
+     */
+    private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
+            boolean freeformStackVisible, boolean isDockedDividerResizing) {
+        if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
+            if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
+                visibility = setNavBarOpaqueFlag(visibility);
+            }
+        } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
+            if (isDockedDividerResizing) {
+                visibility = setNavBarOpaqueFlag(visibility);
+            } else if (freeformStackVisible) {
+                visibility = setNavBarTranslucentFlag(visibility);
+            } else {
+                visibility = setNavBarOpaqueFlag(visibility);
+            }
+        }
+
+        if (!areTranslucentBarsAllowed()) {
+            visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
+        }
+        return visibility;
+    }
+
+    private int setNavBarOpaqueFlag(int visibility) {
+        return visibility &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
+    }
+
+    private int setNavBarTranslucentFlag(int visibility) {
+        visibility &= ~View.NAVIGATION_BAR_TRANSPARENT;
+        return visibility |= View.NAVIGATION_BAR_TRANSLUCENT;
+    }
+
     private void clearClearableFlagsLw() {
         int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
         if (newVal != mResettingSystemUiFlags) {
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource.xml
new file mode 100644
index 0000000..8093b9d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource_debug.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource_debug.xml
new file mode 100644
index 0000000..fc24df5
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource_debug.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- debug-overrides not inside network-security-config should cause a parsing error -->
+<debug-overrides>
+  <trust-anchors>
+    <certificates src="system" />
+  </trust-anchors>
+</debug-overrides>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource.xml b/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource.xml
new file mode 100644
index 0000000..8093b9d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource_debug.xml b/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource_debug.xml
new file mode 100644
index 0000000..6a2ad37
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource_debug.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <debug-overrides>
+    <trust-anchors>
+      <certificates src="system" />
+    </trust-anchors>
+  </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index 35e3ef4..10bcc18 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -431,4 +431,37 @@
         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
     }
+
+    public void testExtraDebugResource() throws Exception {
+        XmlConfigSource source =
+                new XmlConfigSource(getContext(), R.xml.extra_debug_resource, true);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertFalse(appConfig.hasPerDomainConfigs());
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+        MoreAsserts.assertNotEmpty(config.getTrustAnchors());
+
+        // Check that the _debug file is ignored if debug is false.
+        source = new XmlConfigSource(getContext(), R.xml.extra_debug_resource, false);
+        appConfig = new ApplicationConfig(source);
+        assertFalse(appConfig.hasPerDomainConfigs());
+        config = appConfig.getConfigForHostname("");
+        MoreAsserts.assertEmpty(config.getTrustAnchors());
+    }
+
+    public void testExtraDebugResourceIgnored() throws Exception {
+        // Verify that parsing the extra debug config resource fails only when debugging is true.
+        XmlConfigSource source =
+                new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, false);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        // Force parsing the config file.
+        appConfig.getConfigForHostname("");
+
+        source = new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, true);
+        appConfig = new ApplicationConfig(source);
+        try {
+            appConfig.getConfigForHostname("");
+            fail("Bad extra debug resource did not fail to parse");
+        } catch (RuntimeException expected) {
+        }
+    }
 }