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) {
+ }
+ }
}