• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

frameworks/base


Commit MetaInfo

Revisionb35fa828d0d65ef65aece664b22775cdb9253bd3 (tree)
Time2020-02-11 11:50:54
AuthorChih-Wei Huang <cwhuang@linu...>
CommiterChih-Wei Huang

Log Message

Android 9.0.0 Release 53 (6107734)
-----BEGIN PGP SIGNATURE-----

iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCXji5AAAKCRDorT+BmrEO
eFFiAJ9ybleXTQZYkO6bDI+l12aHMQdl+ACbBeS8G0yTl0+dgYL+i8+kk3I+os4=
=wp0G
-----END PGP SIGNATURE-----

Merge tag 'android-9.0.0_r53' into pie-x86

Android 9.0.0 Release 53 (6107734)

Change Summary

Incremental Difference

--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,6 +16,7 @@
1616
1717 package android.widget;
1818
19+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
1920 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
2021 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
2122 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -31,11 +32,13 @@ import android.annotation.IntRange;
3132 import android.annotation.NonNull;
3233 import android.annotation.Nullable;
3334 import android.annotation.Px;
35+import android.annotation.RequiresPermission;
3436 import android.annotation.Size;
3537 import android.annotation.StringRes;
3638 import android.annotation.StyleRes;
3739 import android.annotation.XmlRes;
3840 import android.app.Activity;
41+import android.app.ActivityManager;
3942 import android.app.PendingIntent;
4043 import android.app.assist.AssistStructure;
4144 import android.content.ClipData;
@@ -72,6 +75,7 @@ import android.os.Parcel;
7275 import android.os.Parcelable;
7376 import android.os.ParcelableParcel;
7477 import android.os.SystemClock;
78+import android.os.UserHandle;
7579 import android.provider.Settings;
7680 import android.text.BoringLayout;
7781 import android.text.DynamicLayout;
@@ -723,6 +727,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
723727
724728 private InputFilter[] mFilters = NO_FILTERS;
725729
730+ /**
731+ * To keep the information to indicate if there is necessary to restrict the power of
732+ * INTERACT_ACROSS_USERS_FULL.
733+ * <p>
734+ * SystemUI always run as user 0 to process all of direct reply. SystemUI has the poer of
735+ * INTERACT_ACROSS_USERS_FULL. However, all of the notifications not only belong to user 0 but
736+ * also to the other users in multiple user environment.
737+ * </p>
738+ *
739+ * @see #setRestrictedAcrossUser(boolean)
740+ */
741+ private boolean mIsRestrictedAcrossUser;
742+
726743 private volatile Locale mCurrentSpellCheckerLocaleCache;
727744
728745 // It is possible to have a selection even when mEditor is null (programmatically set, like when
@@ -10440,6 +10457,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
1044010457 }
1044110458
1044210459 /**
10460+ * To notify the TextView to restricted the power of the app granted INTERACT_ACROSS_USERS_FULL
10461+ * permission.
10462+ * <p>
10463+ * Most of applications should not granted the INTERACT_ACROSS_USERS_FULL permssion.
10464+ * SystemUI is the special one that run in user 0 process to handle multiple user notification.
10465+ * Unforunately, the power of INTERACT_ACROSS_USERS_FULL should be limited or restricted for
10466+ * preventing from information leak.</p>
10467+ * <p>This function call is called for SystemUI Keyguard and Notification.</p>
10468+ *
10469+ * @param isRestricted is true if the power of INTERACT_ACROSS_USERS_FULL should be limited.
10470+ * @hide
10471+ */
10472+ @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
10473+ public final void setRestrictedAcrossUser(boolean isRestricted) {
10474+ mIsRestrictedAcrossUser = isRestricted;
10475+ }
10476+
10477+ /**
1044310478 * This is a temporary method. Future versions may support multi-locale text.
1044410479 * Caveat: This method may not return the latest text services locale, but this should be
1044510480 * acceptable and it's more important to make this method asynchronous.
@@ -11647,6 +11682,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
1164711682 }
1164811683
1164911684 boolean canCut() {
11685+ if (mIsRestrictedAcrossUser
11686+ && UserHandle.myUserId() != ActivityManager.getCurrentUser()) {
11687+ // When it's restricted, and the curren user is not the process user. It can't cut
11688+ // because it may cut the text of the user 10 into the clipboard of user 0.
11689+ return false;
11690+ }
1165011691 if (hasPasswordTransformationMethod()) {
1165111692 return false;
1165211693 }
@@ -11660,6 +11701,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
1166011701 }
1166111702
1166211703 boolean canCopy() {
11704+ if (mIsRestrictedAcrossUser
11705+ && UserHandle.myUserId() != ActivityManager.getCurrentUser()) {
11706+ // When it's restricted, and the curren user is not the process user. It can't copy
11707+ // because it may copy the text of the user 10 to the clipboard of user 0.
11708+ return false;
11709+ }
1166311710 if (hasPasswordTransformationMethod()) {
1166411711 return false;
1166511712 }
@@ -11689,6 +11736,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
1168911736 }
1169011737
1169111738 boolean canPaste() {
11739+ if (mIsRestrictedAcrossUser
11740+ && UserHandle.myUserId() != ActivityManager.getCurrentUser()) {
11741+ // When it's restricted, and the curren user is not the process user. It can't paste
11742+ // because it may copy the text from the user 0 clipboard in current user is 10.
11743+ return false;
11744+ }
1169211745 return (mText instanceof Editable
1169311746 && mEditor != null && mEditor.mKeyListener != null
1169411747 && getSelectionStart() >= 0
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -79,6 +79,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
7979
8080 @Override
8181 protected void resetState() {
82+ mPasswordEntry.setRestrictedAcrossUser(true);
8283 mSecurityMessageDisplay.setMessage("");
8384 final boolean wasDisabled = mPasswordEntry.isEnabled();
8485 setPasswordEntryEnabled(true);
@@ -169,6 +170,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
169170 Context.INPUT_METHOD_SERVICE);
170171
171172 mPasswordEntry = findViewById(getPasswordTextViewId());
173+ mPasswordEntry.setRestrictedAcrossUser(true);
172174 mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
173175 mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
174176 mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy;
1818
1919 import android.animation.Animator;
2020 import android.animation.AnimatorListenerAdapter;
21+import android.app.ActivityManager;
2122 import android.app.Notification;
2223 import android.app.PendingIntent;
2324 import android.app.RemoteInput;
@@ -28,11 +29,14 @@ import android.graphics.Rect;
2829 import android.graphics.drawable.Drawable;
2930 import android.os.Bundle;
3031 import android.os.SystemClock;
32+import android.os.UserHandle;
3133 import android.text.Editable;
34+import android.text.InputType;
3235 import android.text.SpannedString;
3336 import android.text.TextWatcher;
3437 import android.util.AttributeSet;
3538 import android.util.Log;
39+import android.view.ActionMode;
3640 import android.view.KeyEvent;
3741 import android.view.LayoutInflater;
3842 import android.view.MotionEvent;
@@ -45,6 +49,7 @@ import android.view.inputmethod.CompletionInfo;
4549 import android.view.inputmethod.EditorInfo;
4650 import android.view.inputmethod.InputConnection;
4751 import android.view.inputmethod.InputMethodManager;
52+import android.view.textclassifier.TextClassifier;
4853 import android.widget.EditText;
4954 import android.widget.ImageButton;
5055 import android.widget.LinearLayout;
@@ -187,12 +192,36 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
187192 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
188193 v.mController = controller;
189194 v.mEntry = entry;
195+ v.mEditText.setRestrictedAcrossUser(true);
190196 v.setTag(VIEW_TAG);
191197
198+ // Disable the TextClassifier to avoid cross user interactions.
199+ v.mEditText.setTextClassifier(TextClassifier.NO_OP);
200+
192201 return v;
193202 }
194203
195204 @Override
205+ public ActionMode startActionMode(ActionMode.Callback callback, int type) {
206+ try {
207+ UserHandle notificationUser = mEntry.notification.getUser();
208+ UserHandle currentUser = UserHandle.of(ActivityManager.getCurrentUser());
209+ if (!UserHandle.ALL.equals(notificationUser)
210+ && !currentUser.equals(notificationUser)) {
211+ // If this happens to be a selection action mode, a non-NO_OP TextClassifier could
212+ // leak data across users. This widget uses TextClassifier.NO_OP so this is fine.
213+ // Log the security fix.
214+ android.util.EventLog.writeEvent(0x534e4554, "123232892", -1, "");
215+ }
216+ } catch (Throwable t) {
217+ // Avoid crashing because of this log attempt.
218+ Log.i(TAG, "Error attempting to log security fix for bug 123232892", t);
219+
220+ }
221+ return super.startActionMode(callback, type);
222+ }
223+
224+ @Override
196225 public void onClick(View v) {
197226 if (v == mSendButton) {
198227 sendRemoteInput();
@@ -292,6 +321,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
292321 if (mWrapper != null) {
293322 mWrapper.setRemoteInputVisible(true);
294323 }
324+
325+ // Disable suggestions on non-owner (secondary) user.
326+ // SpellCheckerService of primary user runs on secondary as well which shows
327+ // "Add to dictionary" dialog on the primary user. (See b/123232892)
328+ // Note: this doesn't affect work-profile users on P or older versions.
329+ if (UserHandle.myUserId() != ActivityManager.getCurrentUser()) {
330+ mEditText.setInputType(
331+ mEditText.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
332+ }
333+
295334 mEditText.setInnerFocusable(true);
296335 mEditText.mShowImeOnInputConnection = true;
297336 mEditText.setText(mEntry.remoteInputText);
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -26,6 +26,7 @@ import android.support.test.filters.SmallTest;
2626 import android.testing.AndroidTestingRunner;
2727 import android.testing.TestableLooper;
2828 import android.view.View;
29+import android.view.textclassifier.TextClassifier;
2930 import android.widget.EditText;
3031 import android.widget.ImageButton;
3132
@@ -109,4 +110,13 @@ public class RemoteInputViewTest extends SysuiTestCase {
109110 mView.setVisibility(View.INVISIBLE);
110111 mView.setVisibility(View.VISIBLE);
111112 }
113+
114+ @Test
115+ public void testUsesNoOpTextClassifier() {
116+ RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build();
117+ mView.setRemoteInput(new RemoteInput[]{input}, input);
118+
119+ EditText editText = mView.findViewById(R.id.remote_input_text);
120+ assertEquals(TextClassifier.NO_OP, editText.getTextClassifier());
121+ }
112122 }
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2339,9 +2339,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
23392339 }
23402340 }
23412341
2342- private boolean networkRequiresValidation(NetworkAgentInfo nai) {
2343- return NetworkMonitor.isValidationRequired(
2344- mDefaultRequest.networkCapabilities, nai.networkCapabilities);
2342+ private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
2343+ return nai.networkMonitor.isPrivateDnsValidationRequired();
23452344 }
23462345
23472346 private void handlePrivateDnsSettingsChanged() {
@@ -2349,16 +2348,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
23492348
23502349 for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
23512350 handlePerNetworkPrivateDnsConfig(nai, cfg);
2352- if (networkRequiresValidation(nai)) {
2351+ if (networkRequiresPrivateDnsValidation(nai)) {
23532352 handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
23542353 }
23552354 }
23562355 }
23572356
23582357 private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
2359- // Private DNS only ever applies to networks that might provide
2360- // Internet access and therefore also require validation.
2361- if (!networkRequiresValidation(nai)) return;
2358+ if (!networkRequiresPrivateDnsValidation(nai)) return;
23622359
23632360 // Notify the NetworkMonitor thread in case it needs to cancel or
23642361 // schedule DNS resolutions. If a DNS resolution is required the
--- a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
+++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java
@@ -45,7 +45,7 @@ public class ConnectivityConstants {
4545 //
4646 // This ensures that a) the explicitly selected network is never trumped by anything else, and
4747 // b) the explicitly selected network is never torn down.
48- public static final int MAXIMUM_NETWORK_SCORE = 100;
48+ public static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
4949 // VPNs typically have priority over other networks. Give them a score that will
5050 // let them win every single time.
5151 public static final int VPN_DEFAULT_SCORE = 101;
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -432,11 +432,11 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
432432 // down an explicitly selected network before the user gets a chance to prefer it when
433433 // a higher-scoring network (e.g., Ethernet) is available.
434434 if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
435- return ConnectivityConstants.MAXIMUM_NETWORK_SCORE;
435+ return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
436436 }
437437
438438 int score = currentScore;
439- if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
439+ if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
440440 score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
441441 }
442442 if (score < 0) score = 0;
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -243,12 +243,6 @@ public class NetworkMonitor extends StateMachine {
243243
244244 private String mPrivateDnsProviderHostname = "";
245245
246- public static boolean isValidationRequired(
247- NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
248- // TODO: Consider requiring validation for DUN networks.
249- return dfltNetCap.satisfiedByNetworkCapabilities(nc);
250- }
251-
252246 private final Context mContext;
253247 private final Handler mConnectivityServiceHandler;
254248 private final NetworkAgentInfo mNetworkAgentInfo;
@@ -381,11 +375,19 @@ public class NetworkMonitor extends StateMachine {
381375 return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
382376 }
383377
384- private boolean isValidationRequired() {
385- return isValidationRequired(
386- mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
378+ @VisibleForTesting
379+ public boolean isValidationRequired() {
380+ // TODO: Consider requiring validation for DUN networks.
381+ return mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities(
382+ mNetworkAgentInfo.networkCapabilities);
387383 }
388384
385+ public boolean isPrivateDnsValidationRequired() {
386+ // VPNs become the default network for applications even if they do not provide the INTERNET
387+ // capability (e.g., split tunnels; See b/119216095).
388+ // Ensure private DNS works on such VPNs as well.
389+ return isValidationRequired() || mNetworkAgentInfo.isVPN();
390+ }
389391
390392 private void notifyNetworkTestResultInvalid(Object obj) {
391393 mConnectivityServiceHandler.sendMessage(obtainMessage(
@@ -455,7 +457,7 @@ public class NetworkMonitor extends StateMachine {
455457 return HANDLED;
456458 case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
457459 final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj;
458- if (!isValidationRequired() || cfg == null || !cfg.inStrictMode()) {
460+ if (!isPrivateDnsValidationRequired() || cfg == null || !cfg.inStrictMode()) {
459461 // No DNS resolution required.
460462 //
461463 // We don't force any validation in opportunistic mode
@@ -621,9 +623,20 @@ public class NetworkMonitor extends StateMachine {
621623 // the network so don't bother validating here. Furthermore sending HTTP
622624 // packets over the network may be undesirable, for example an extremely
623625 // expensive metered network, or unwanted leaking of the User Agent string.
626+ //
627+ // On networks that need to support private DNS in strict mode (e.g., VPNs, but
628+ // not networks that don't provide Internet access), we still need to perform
629+ // private DNS server resolution.
624630 if (!isValidationRequired()) {
625- validationLog("Network would not satisfy default request, not validating");
626- transitionTo(mValidatedState);
631+ if (isPrivateDnsValidationRequired()) {
632+ validationLog("Network would not satisfy default request, "
633+ + "resolving private DNS");
634+ transitionTo(mEvaluatingPrivateDnsState);
635+ } else {
636+ validationLog("Network would not satisfy default request, "
637+ + "not validating");
638+ transitionTo(mValidatedState);
639+ }
627640 return HANDLED;
628641 }
629642 mAttempts++;
@@ -796,7 +809,7 @@ public class NetworkMonitor extends StateMachine {
796809 try {
797810 // Do a blocking DNS resolution using the network-assigned nameservers.
798811 // Do not set AI_ADDRCONFIG in ai_flags so we get all address families in advance.
799- final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(
812+ final InetAddress[] ips = resolveAllLocally(
800813 mNetwork, mPrivateDnsProviderHostname, 0 /* aiFlags */);
801814 mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
802815 } catch (UnknownHostException uhe) {
@@ -830,7 +843,7 @@ public class NetworkMonitor extends StateMachine {
830843 final String host = UUID.randomUUID().toString().substring(0, 8) +
831844 ONE_TIME_HOSTNAME_SUFFIX;
832845 try {
833- final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
846+ final InetAddress[] ips = getAllByName(mNetworkAgentInfo.network(), host);
834847 return (ips != null && ips.length > 0);
835848 } catch (UnknownHostException uhe) {}
836849 return false;
@@ -1046,7 +1059,7 @@ public class NetworkMonitor extends StateMachine {
10461059 int result;
10471060 String connectInfo;
10481061 try {
1049- InetAddress[] addresses = mNetwork.getAllByName(host);
1062+ InetAddress[] addresses = getAllByName(mNetwork, host);
10501063 StringBuffer buffer = new StringBuffer();
10511064 for (InetAddress address : addresses) {
10521065 buffer.append(',').append(address.getHostAddress());
@@ -1233,6 +1246,18 @@ public class NetworkMonitor extends StateMachine {
12331246 }
12341247 }
12351248
1249+ @VisibleForTesting
1250+ protected InetAddress[] getAllByName(Network network, String host) throws UnknownHostException {
1251+ return network.getAllByName(host);
1252+ }
1253+
1254+ @VisibleForTesting
1255+ protected InetAddress[] resolveAllLocally(Network network, String hostname, int flags)
1256+ throws UnknownHostException {
1257+ // We cannot use this in OneAddressPerFamilyNetwork#getAllByName because that's static.
1258+ return ResolvUtil.blockingResolveAllLocally(network, hostname, flags);
1259+ }
1260+
12361261 private URL makeURL(String url) {
12371262 if (url != null) {
12381263 try {
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2804,6 +2804,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
28042804 attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
28052805 }
28062806 attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
2807+ // Toasts can't be clickable
2808+ attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
28072809 break;
28082810 }
28092811
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -25,6 +25,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE;
2525 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
2626 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
2727 import static android.net.ConnectivityManager.TYPE_NONE;
28+import static android.net.ConnectivityManager.TYPE_VPN;
2829 import static android.net.ConnectivityManager.TYPE_WIFI;
2930 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
3031 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
@@ -162,6 +163,7 @@ import org.mockito.MockitoAnnotations;
162163 import org.mockito.Spy;
163164
164165 import java.net.InetAddress;
166+import java.net.UnknownHostException;
165167 import java.util.ArrayList;
166168 import java.util.Arrays;
167169 import java.util.Collection;
@@ -384,7 +386,7 @@ public class ConnectivityServiceTest {
384386
385387 MockNetworkAgent(int transport, LinkProperties linkProperties) {
386388 final int type = transportToLegacyType(transport);
387- final String typeName = ConnectivityManager.getNetworkTypeName(transport);
389+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
388390 mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
389391 mNetworkCapabilities = new NetworkCapabilities();
390392 mNetworkCapabilities.addTransportType(transport);
@@ -452,6 +454,10 @@ public class ConnectivityServiceTest {
452454 mNetworkAgent.sendNetworkScore(mScore);
453455 }
454456
457+ public int getScore() {
458+ return mScore;
459+ }
460+
455461 public void explicitlySelected(boolean acceptUnvalidated) {
456462 mNetworkAgent.explicitlySelected(acceptUnvalidated);
457463 }
@@ -862,6 +868,7 @@ public class ConnectivityServiceTest {
862868 // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
863869 public int gen204ProbeResult = 500;
864870 public String gen204ProbeRedirectUrl = null;
871+ public volatile InetAddress[] dnsLookupResults = null;
865872
866873 public WrappedNetworkMonitor(Context context, Handler handler,
867874 NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
@@ -876,6 +883,25 @@ public class ConnectivityServiceTest {
876883 if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); }
877884 return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null);
878885 }
886+
887+ private InetAddress[] fakeDnsLookup() throws UnknownHostException {
888+ if (dnsLookupResults == null) {
889+ throw new UnknownHostException();
890+ }
891+ return dnsLookupResults;
892+ }
893+
894+ @Override
895+ protected InetAddress[] getAllByName(Network network, String hostname)
896+ throws UnknownHostException {
897+ return fakeDnsLookup();
898+ }
899+
900+ @Override
901+ protected InetAddress[] resolveAllLocally(Network network, String hostname, int flags)
902+ throws UnknownHostException {
903+ return fakeDnsLookup();
904+ }
879905 }
880906
881907 private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
@@ -1079,6 +1105,8 @@ public class ConnectivityServiceTest {
10791105 return TYPE_WIFI;
10801106 case TRANSPORT_CELLULAR:
10811107 return TYPE_MOBILE;
1108+ case TRANSPORT_VPN:
1109+ return TYPE_VPN;
10821110 default:
10831111 return TYPE_NONE;
10841112 }
@@ -3971,7 +3999,7 @@ public class ConnectivityServiceTest {
39713999 cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
39724000
39734001 mCellNetworkAgent.sendLinkProperties(cellLp);
3974- mCellNetworkAgent.connect(false);
4002+ mCellNetworkAgent.connect(true);
39754003 waitForIdle();
39764004 verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
39774005 anyInt(), mStringArrayCaptor.capture(), any(), any(),
@@ -3989,9 +4017,10 @@ public class ConnectivityServiceTest {
39894017 mCellNetworkAgent);
39904018 CallbackInfo cbi = cellNetworkCallback.expectCallback(
39914019 CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
3992- cellNetworkCallback.assertNoCallback();
39934020 assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
39944021 assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
4022+ cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
4023+ cellNetworkCallback.assertNoCallback();
39954024
39964025 setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
39974026 verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
@@ -4016,14 +4045,53 @@ public class ConnectivityServiceTest {
40164045 reset(mNetworkManagementService);
40174046 cellNetworkCallback.assertNoCallback();
40184047
4048+ // Strict mode.
4049+ mCellNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults = new InetAddress[] {
4050+ InetAddress.getByName("2001:db8::66"),
4051+ InetAddress.getByName("192.0.2.44")
4052+ };
40194053 setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
4020- // Can't test dns configuration for strict mode without properly mocking
4021- // out the DNS lookups, but can test that LinkProperties is updated.
4054+
4055+ // Expect a callback saying that private DNS is now in strict mode.
40224056 cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
40234057 mCellNetworkAgent);
4058+ LinkProperties lp = (LinkProperties) cbi.arg;
4059+ assertTrue(lp.isPrivateDnsActive());
4060+ assertEquals("strict.example.com", lp.getPrivateDnsServerName());
40244061 cellNetworkCallback.assertNoCallback();
4025- assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
4026- assertEquals("strict.example.com", ((LinkProperties)cbi.arg).getPrivateDnsServerName());
4062+
4063+ // When the validation callback arrives, LinkProperties are updated.
4064+ // We need to wait for this callback because the test thread races with the NetworkMonitor
4065+ // thread, and if the test thread wins the race, then the times(2) verify call below will
4066+ // fail.
4067+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
4068+ mCellNetworkAgent.getNetwork().netId, "2001:db8::66", "strict.example.com", true);
4069+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
4070+ mCellNetworkAgent);
4071+ lp = (LinkProperties) cbi.arg;
4072+ assertTrue(lp.isPrivateDnsActive());
4073+ assertEquals(1, lp.getValidatedPrivateDnsServers().size());
4074+
4075+ // setDnsConfigurationForNetwork is called twice: once when private DNS is set to strict
4076+ // mode and once when the hostname resolves.
4077+ verify(mNetworkManagementService, times(2)).setDnsConfigurationForNetwork(
4078+ anyInt(), mStringArrayCaptor.capture(), any(), any(),
4079+ eq("strict.example.com"), tlsServers.capture());
4080+ assertEquals(2, mStringArrayCaptor.getValue().length);
4081+ assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
4082+ new String[]{"2001:db8::1", "192.0.2.1"}));
4083+ assertEquals(2, tlsServers.getValue().length);
4084+ assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
4085+ new String[]{"2001:db8::66", "192.0.2.44"}));
4086+ reset(mNetworkManagementService);
4087+
4088+ // Send the same LinkProperties and expect getting the same result including private dns.
4089+ // b/118518971
4090+ LinkProperties oldLp = (LinkProperties) cbi.arg;
4091+ mCellNetworkAgent.sendLinkProperties(cellLp);
4092+ waitForIdle();
4093+ LinkProperties newLp = mCm.getLinkProperties(cbi.network);
4094+ assertEquals(oldLp, newLp);
40274095 }
40284096
40294097 @Test
@@ -4326,6 +4394,69 @@ public class ConnectivityServiceTest {
43264394 }
43274395
43284396 @Test
4397+ public void testVpnUnvalidated() throws Exception {
4398+ final TestNetworkCallback callback = new TestNetworkCallback();
4399+ mCm.registerDefaultNetworkCallback(callback);
4400+
4401+ // Enable private DNS.
4402+ setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
4403+
4404+ // Bring up Ethernet.
4405+ mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
4406+ mEthernetNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults =
4407+ new InetAddress[]{ InetAddress.getByName("2001:db8::1") };
4408+ mEthernetNetworkAgent.connect(true);
4409+ callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
4410+ callback.assertNoCallback();
4411+
4412+ // Bring up a VPN that has the INTERNET capability but does not validate.
4413+ final int uid = Process.myUid();
4414+ final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
4415+ vpnNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500;
4416+ vpnNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults = null;
4417+
4418+ final ArraySet<UidRange> ranges = new ArraySet<>();
4419+ ranges.add(new UidRange(uid, uid));
4420+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
4421+ mMockVpn.setUids(ranges);
4422+ vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
4423+ mMockVpn.connect();
4424+
4425+ // Even though the VPN is unvalidated, it becomes the default network for our app.
4426+ callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
4427+ // TODO: this looks like a spurious callback.
4428+ callback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
4429+ callback.assertNoCallback();
4430+
4431+ assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
4432+ assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
4433+ assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
4434+
4435+ NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
4436+ assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
4437+ assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
4438+
4439+ assertFalse(vpnNetworkAgent.getWrappedNetworkMonitor().isValidationRequired());
4440+ assertTrue(vpnNetworkAgent.getWrappedNetworkMonitor().isPrivateDnsValidationRequired());
4441+
4442+ // Pretend that the strict mode private DNS hostname now resolves. Even though the
4443+ // connectivity probe still returns 500, the network validates because the connectivity
4444+ // probe is not used on VPNs.
4445+ vpnNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults =
4446+ new InetAddress[]{ InetAddress.getByName("2001:db8::1") };
4447+ mCm.reportNetworkConnectivity(vpnNetworkAgent.getNetwork(), true);
4448+
4449+ // Expect to see the validated capability, but no other changes, because the VPN is already
4450+ // the default network for the app.
4451+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
4452+ callback.assertNoCallback();
4453+
4454+ vpnNetworkAgent.disconnect();
4455+ callback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
4456+ callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
4457+ }
4458+
4459+ @Test
43294460 public void testVpnSetUnderlyingNetworks() {
43304461 final int uid = Process.myUid();
43314462