frameworks/base
Revision | b35fa828d0d65ef65aece664b22775cdb9253bd3 (tree) |
---|---|
Time | 2020-02-11 11:50:54 |
Author | Chih-Wei Huang <cwhuang@linu...> |
Commiter | Chih-Wei Huang |
Merge tag 'android-9.0.0_r53' into pie-x86
Android 9.0.0 Release 53 (6107734)
@@ -16,6 +16,7 @@ | ||
16 | 16 | |
17 | 17 | package android.widget; |
18 | 18 | |
19 | +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; | |
19 | 20 | import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH; |
20 | 21 | import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX; |
21 | 22 | import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY; |
@@ -31,11 +32,13 @@ import android.annotation.IntRange; | ||
31 | 32 | import android.annotation.NonNull; |
32 | 33 | import android.annotation.Nullable; |
33 | 34 | import android.annotation.Px; |
35 | +import android.annotation.RequiresPermission; | |
34 | 36 | import android.annotation.Size; |
35 | 37 | import android.annotation.StringRes; |
36 | 38 | import android.annotation.StyleRes; |
37 | 39 | import android.annotation.XmlRes; |
38 | 40 | import android.app.Activity; |
41 | +import android.app.ActivityManager; | |
39 | 42 | import android.app.PendingIntent; |
40 | 43 | import android.app.assist.AssistStructure; |
41 | 44 | import android.content.ClipData; |
@@ -72,6 +75,7 @@ import android.os.Parcel; | ||
72 | 75 | import android.os.Parcelable; |
73 | 76 | import android.os.ParcelableParcel; |
74 | 77 | import android.os.SystemClock; |
78 | +import android.os.UserHandle; | |
75 | 79 | import android.provider.Settings; |
76 | 80 | import android.text.BoringLayout; |
77 | 81 | import android.text.DynamicLayout; |
@@ -723,6 +727,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener | ||
723 | 727 | |
724 | 728 | private InputFilter[] mFilters = NO_FILTERS; |
725 | 729 | |
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 | + | |
726 | 743 | private volatile Locale mCurrentSpellCheckerLocaleCache; |
727 | 744 | |
728 | 745 | // 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 | ||
10440 | 10457 | } |
10441 | 10458 | |
10442 | 10459 | /** |
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 | + /** | |
10443 | 10478 | * This is a temporary method. Future versions may support multi-locale text. |
10444 | 10479 | * Caveat: This method may not return the latest text services locale, but this should be |
10445 | 10480 | * acceptable and it's more important to make this method asynchronous. |
@@ -11647,6 +11682,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener | ||
11647 | 11682 | } |
11648 | 11683 | |
11649 | 11684 | 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 | + } | |
11650 | 11691 | if (hasPasswordTransformationMethod()) { |
11651 | 11692 | return false; |
11652 | 11693 | } |
@@ -11660,6 +11701,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener | ||
11660 | 11701 | } |
11661 | 11702 | |
11662 | 11703 | 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 | + } | |
11663 | 11710 | if (hasPasswordTransformationMethod()) { |
11664 | 11711 | return false; |
11665 | 11712 | } |
@@ -11689,6 +11736,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener | ||
11689 | 11736 | } |
11690 | 11737 | |
11691 | 11738 | 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 | + } | |
11692 | 11745 | return (mText instanceof Editable |
11693 | 11746 | && mEditor != null && mEditor.mKeyListener != null |
11694 | 11747 | && getSelectionStart() >= 0 |
@@ -79,6 +79,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView | ||
79 | 79 | |
80 | 80 | @Override |
81 | 81 | protected void resetState() { |
82 | + mPasswordEntry.setRestrictedAcrossUser(true); | |
82 | 83 | mSecurityMessageDisplay.setMessage(""); |
83 | 84 | final boolean wasDisabled = mPasswordEntry.isEnabled(); |
84 | 85 | setPasswordEntryEnabled(true); |
@@ -169,6 +170,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView | ||
169 | 170 | Context.INPUT_METHOD_SERVICE); |
170 | 171 | |
171 | 172 | mPasswordEntry = findViewById(getPasswordTextViewId()); |
173 | + mPasswordEntry.setRestrictedAcrossUser(true); | |
172 | 174 | mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry); |
173 | 175 | mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); |
174 | 176 | mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT |
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy; | ||
18 | 18 | |
19 | 19 | import android.animation.Animator; |
20 | 20 | import android.animation.AnimatorListenerAdapter; |
21 | +import android.app.ActivityManager; | |
21 | 22 | import android.app.Notification; |
22 | 23 | import android.app.PendingIntent; |
23 | 24 | import android.app.RemoteInput; |
@@ -28,11 +29,14 @@ import android.graphics.Rect; | ||
28 | 29 | import android.graphics.drawable.Drawable; |
29 | 30 | import android.os.Bundle; |
30 | 31 | import android.os.SystemClock; |
32 | +import android.os.UserHandle; | |
31 | 33 | import android.text.Editable; |
34 | +import android.text.InputType; | |
32 | 35 | import android.text.SpannedString; |
33 | 36 | import android.text.TextWatcher; |
34 | 37 | import android.util.AttributeSet; |
35 | 38 | import android.util.Log; |
39 | +import android.view.ActionMode; | |
36 | 40 | import android.view.KeyEvent; |
37 | 41 | import android.view.LayoutInflater; |
38 | 42 | import android.view.MotionEvent; |
@@ -45,6 +49,7 @@ import android.view.inputmethod.CompletionInfo; | ||
45 | 49 | import android.view.inputmethod.EditorInfo; |
46 | 50 | import android.view.inputmethod.InputConnection; |
47 | 51 | import android.view.inputmethod.InputMethodManager; |
52 | +import android.view.textclassifier.TextClassifier; | |
48 | 53 | import android.widget.EditText; |
49 | 54 | import android.widget.ImageButton; |
50 | 55 | import android.widget.LinearLayout; |
@@ -187,12 +192,36 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene | ||
187 | 192 | LayoutInflater.from(context).inflate(R.layout.remote_input, root, false); |
188 | 193 | v.mController = controller; |
189 | 194 | v.mEntry = entry; |
195 | + v.mEditText.setRestrictedAcrossUser(true); | |
190 | 196 | v.setTag(VIEW_TAG); |
191 | 197 | |
198 | + // Disable the TextClassifier to avoid cross user interactions. | |
199 | + v.mEditText.setTextClassifier(TextClassifier.NO_OP); | |
200 | + | |
192 | 201 | return v; |
193 | 202 | } |
194 | 203 | |
195 | 204 | @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 | |
196 | 225 | public void onClick(View v) { |
197 | 226 | if (v == mSendButton) { |
198 | 227 | sendRemoteInput(); |
@@ -292,6 +321,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene | ||
292 | 321 | if (mWrapper != null) { |
293 | 322 | mWrapper.setRemoteInputVisible(true); |
294 | 323 | } |
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 | + | |
295 | 334 | mEditText.setInnerFocusable(true); |
296 | 335 | mEditText.mShowImeOnInputConnection = true; |
297 | 336 | mEditText.setText(mEntry.remoteInputText); |
@@ -26,6 +26,7 @@ import android.support.test.filters.SmallTest; | ||
26 | 26 | import android.testing.AndroidTestingRunner; |
27 | 27 | import android.testing.TestableLooper; |
28 | 28 | import android.view.View; |
29 | +import android.view.textclassifier.TextClassifier; | |
29 | 30 | import android.widget.EditText; |
30 | 31 | import android.widget.ImageButton; |
31 | 32 |
@@ -109,4 +110,13 @@ public class RemoteInputViewTest extends SysuiTestCase { | ||
109 | 110 | mView.setVisibility(View.INVISIBLE); |
110 | 111 | mView.setVisibility(View.VISIBLE); |
111 | 112 | } |
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 | + } | |
112 | 122 | } |
@@ -2339,9 +2339,8 @@ public class ConnectivityService extends IConnectivityManager.Stub | ||
2339 | 2339 | } |
2340 | 2340 | } |
2341 | 2341 | |
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(); | |
2345 | 2344 | } |
2346 | 2345 | |
2347 | 2346 | private void handlePrivateDnsSettingsChanged() { |
@@ -2349,16 +2348,14 @@ public class ConnectivityService extends IConnectivityManager.Stub | ||
2349 | 2348 | |
2350 | 2349 | for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { |
2351 | 2350 | handlePerNetworkPrivateDnsConfig(nai, cfg); |
2352 | - if (networkRequiresValidation(nai)) { | |
2351 | + if (networkRequiresPrivateDnsValidation(nai)) { | |
2353 | 2352 | handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); |
2354 | 2353 | } |
2355 | 2354 | } |
2356 | 2355 | } |
2357 | 2356 | |
2358 | 2357 | 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; | |
2362 | 2359 | |
2363 | 2360 | // Notify the NetworkMonitor thread in case it needs to cancel or |
2364 | 2361 | // schedule DNS resolutions. If a DNS resolution is required the |
@@ -45,7 +45,7 @@ public class ConnectivityConstants { | ||
45 | 45 | // |
46 | 46 | // This ensures that a) the explicitly selected network is never trumped by anything else, and |
47 | 47 | // 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; | |
49 | 49 | // VPNs typically have priority over other networks. Give them a score that will |
50 | 50 | // let them win every single time. |
51 | 51 | public static final int VPN_DEFAULT_SCORE = 101; |
@@ -432,11 +432,11 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { | ||
432 | 432 | // down an explicitly selected network before the user gets a chance to prefer it when |
433 | 433 | // a higher-scoring network (e.g., Ethernet) is available. |
434 | 434 | if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) { |
435 | - return ConnectivityConstants.MAXIMUM_NETWORK_SCORE; | |
435 | + return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE; | |
436 | 436 | } |
437 | 437 | |
438 | 438 | int score = currentScore; |
439 | - if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) { | |
439 | + if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) { | |
440 | 440 | score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY; |
441 | 441 | } |
442 | 442 | if (score < 0) score = 0; |
@@ -243,12 +243,6 @@ public class NetworkMonitor extends StateMachine { | ||
243 | 243 | |
244 | 244 | private String mPrivateDnsProviderHostname = ""; |
245 | 245 | |
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 | - | |
252 | 246 | private final Context mContext; |
253 | 247 | private final Handler mConnectivityServiceHandler; |
254 | 248 | private final NetworkAgentInfo mNetworkAgentInfo; |
@@ -381,11 +375,19 @@ public class NetworkMonitor extends StateMachine { | ||
381 | 375 | return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION; |
382 | 376 | } |
383 | 377 | |
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); | |
387 | 383 | } |
388 | 384 | |
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 | + } | |
389 | 391 | |
390 | 392 | private void notifyNetworkTestResultInvalid(Object obj) { |
391 | 393 | mConnectivityServiceHandler.sendMessage(obtainMessage( |
@@ -455,7 +457,7 @@ public class NetworkMonitor extends StateMachine { | ||
455 | 457 | return HANDLED; |
456 | 458 | case CMD_PRIVATE_DNS_SETTINGS_CHANGED: { |
457 | 459 | final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj; |
458 | - if (!isValidationRequired() || cfg == null || !cfg.inStrictMode()) { | |
460 | + if (!isPrivateDnsValidationRequired() || cfg == null || !cfg.inStrictMode()) { | |
459 | 461 | // No DNS resolution required. |
460 | 462 | // |
461 | 463 | // We don't force any validation in opportunistic mode |
@@ -621,9 +623,20 @@ public class NetworkMonitor extends StateMachine { | ||
621 | 623 | // the network so don't bother validating here. Furthermore sending HTTP |
622 | 624 | // packets over the network may be undesirable, for example an extremely |
623 | 625 | // 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. | |
624 | 630 | 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 | + } | |
627 | 640 | return HANDLED; |
628 | 641 | } |
629 | 642 | mAttempts++; |
@@ -796,7 +809,7 @@ public class NetworkMonitor extends StateMachine { | ||
796 | 809 | try { |
797 | 810 | // Do a blocking DNS resolution using the network-assigned nameservers. |
798 | 811 | // 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( | |
800 | 813 | mNetwork, mPrivateDnsProviderHostname, 0 /* aiFlags */); |
801 | 814 | mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips); |
802 | 815 | } catch (UnknownHostException uhe) { |
@@ -830,7 +843,7 @@ public class NetworkMonitor extends StateMachine { | ||
830 | 843 | final String host = UUID.randomUUID().toString().substring(0, 8) + |
831 | 844 | ONE_TIME_HOSTNAME_SUFFIX; |
832 | 845 | try { |
833 | - final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host); | |
846 | + final InetAddress[] ips = getAllByName(mNetworkAgentInfo.network(), host); | |
834 | 847 | return (ips != null && ips.length > 0); |
835 | 848 | } catch (UnknownHostException uhe) {} |
836 | 849 | return false; |
@@ -1046,7 +1059,7 @@ public class NetworkMonitor extends StateMachine { | ||
1046 | 1059 | int result; |
1047 | 1060 | String connectInfo; |
1048 | 1061 | try { |
1049 | - InetAddress[] addresses = mNetwork.getAllByName(host); | |
1062 | + InetAddress[] addresses = getAllByName(mNetwork, host); | |
1050 | 1063 | StringBuffer buffer = new StringBuffer(); |
1051 | 1064 | for (InetAddress address : addresses) { |
1052 | 1065 | buffer.append(',').append(address.getHostAddress()); |
@@ -1233,6 +1246,18 @@ public class NetworkMonitor extends StateMachine { | ||
1233 | 1246 | } |
1234 | 1247 | } |
1235 | 1248 | |
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 | + | |
1236 | 1261 | private URL makeURL(String url) { |
1237 | 1262 | if (url != null) { |
1238 | 1263 | try { |
@@ -2804,6 +2804,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { | ||
2804 | 2804 | attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT; |
2805 | 2805 | } |
2806 | 2806 | attrs.windowAnimations = com.android.internal.R.style.Animation_Toast; |
2807 | + // Toasts can't be clickable | |
2808 | + attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; | |
2807 | 2809 | break; |
2808 | 2810 | } |
2809 | 2811 |
@@ -25,6 +25,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE; | ||
25 | 25 | import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; |
26 | 26 | import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; |
27 | 27 | import static android.net.ConnectivityManager.TYPE_NONE; |
28 | +import static android.net.ConnectivityManager.TYPE_VPN; | |
28 | 29 | import static android.net.ConnectivityManager.TYPE_WIFI; |
29 | 30 | import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; |
30 | 31 | import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; |
@@ -162,6 +163,7 @@ import org.mockito.MockitoAnnotations; | ||
162 | 163 | import org.mockito.Spy; |
163 | 164 | |
164 | 165 | import java.net.InetAddress; |
166 | +import java.net.UnknownHostException; | |
165 | 167 | import java.util.ArrayList; |
166 | 168 | import java.util.Arrays; |
167 | 169 | import java.util.Collection; |
@@ -384,7 +386,7 @@ public class ConnectivityServiceTest { | ||
384 | 386 | |
385 | 387 | MockNetworkAgent(int transport, LinkProperties linkProperties) { |
386 | 388 | final int type = transportToLegacyType(transport); |
387 | - final String typeName = ConnectivityManager.getNetworkTypeName(transport); | |
389 | + final String typeName = ConnectivityManager.getNetworkTypeName(type); | |
388 | 390 | mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock"); |
389 | 391 | mNetworkCapabilities = new NetworkCapabilities(); |
390 | 392 | mNetworkCapabilities.addTransportType(transport); |
@@ -452,6 +454,10 @@ public class ConnectivityServiceTest { | ||
452 | 454 | mNetworkAgent.sendNetworkScore(mScore); |
453 | 455 | } |
454 | 456 | |
457 | + public int getScore() { | |
458 | + return mScore; | |
459 | + } | |
460 | + | |
455 | 461 | public void explicitlySelected(boolean acceptUnvalidated) { |
456 | 462 | mNetworkAgent.explicitlySelected(acceptUnvalidated); |
457 | 463 | } |
@@ -862,6 +868,7 @@ public class ConnectivityServiceTest { | ||
862 | 868 | // HTTP response code fed back to NetworkMonitor for Internet connectivity probe. |
863 | 869 | public int gen204ProbeResult = 500; |
864 | 870 | public String gen204ProbeRedirectUrl = null; |
871 | + public volatile InetAddress[] dnsLookupResults = null; | |
865 | 872 | |
866 | 873 | public WrappedNetworkMonitor(Context context, Handler handler, |
867 | 874 | NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest, |
@@ -876,6 +883,25 @@ public class ConnectivityServiceTest { | ||
876 | 883 | if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); } |
877 | 884 | return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null); |
878 | 885 | } |
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 | + } | |
879 | 905 | } |
880 | 906 | |
881 | 907 | private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker { |
@@ -1079,6 +1105,8 @@ public class ConnectivityServiceTest { | ||
1079 | 1105 | return TYPE_WIFI; |
1080 | 1106 | case TRANSPORT_CELLULAR: |
1081 | 1107 | return TYPE_MOBILE; |
1108 | + case TRANSPORT_VPN: | |
1109 | + return TYPE_VPN; | |
1082 | 1110 | default: |
1083 | 1111 | return TYPE_NONE; |
1084 | 1112 | } |
@@ -3971,7 +3999,7 @@ public class ConnectivityServiceTest { | ||
3971 | 3999 | cellLp.addDnsServer(InetAddress.getByName("192.0.2.1")); |
3972 | 4000 | |
3973 | 4001 | mCellNetworkAgent.sendLinkProperties(cellLp); |
3974 | - mCellNetworkAgent.connect(false); | |
4002 | + mCellNetworkAgent.connect(true); | |
3975 | 4003 | waitForIdle(); |
3976 | 4004 | verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork( |
3977 | 4005 | anyInt(), mStringArrayCaptor.capture(), any(), any(), |
@@ -3989,9 +4017,10 @@ public class ConnectivityServiceTest { | ||
3989 | 4017 | mCellNetworkAgent); |
3990 | 4018 | CallbackInfo cbi = cellNetworkCallback.expectCallback( |
3991 | 4019 | CallbackState.LINK_PROPERTIES, mCellNetworkAgent); |
3992 | - cellNetworkCallback.assertNoCallback(); | |
3993 | 4020 | assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive()); |
3994 | 4021 | assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName()); |
4022 | + cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent); | |
4023 | + cellNetworkCallback.assertNoCallback(); | |
3995 | 4024 | |
3996 | 4025 | setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); |
3997 | 4026 | verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork( |
@@ -4016,14 +4045,53 @@ public class ConnectivityServiceTest { | ||
4016 | 4045 | reset(mNetworkManagementService); |
4017 | 4046 | cellNetworkCallback.assertNoCallback(); |
4018 | 4047 | |
4048 | + // Strict mode. | |
4049 | + mCellNetworkAgent.getWrappedNetworkMonitor().dnsLookupResults = new InetAddress[] { | |
4050 | + InetAddress.getByName("2001:db8::66"), | |
4051 | + InetAddress.getByName("192.0.2.44") | |
4052 | + }; | |
4019 | 4053 | 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. | |
4022 | 4056 | cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, |
4023 | 4057 | mCellNetworkAgent); |
4058 | + LinkProperties lp = (LinkProperties) cbi.arg; | |
4059 | + assertTrue(lp.isPrivateDnsActive()); | |
4060 | + assertEquals("strict.example.com", lp.getPrivateDnsServerName()); | |
4024 | 4061 | 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); | |
4027 | 4095 | } |
4028 | 4096 | |
4029 | 4097 | @Test |
@@ -4326,6 +4394,69 @@ public class ConnectivityServiceTest { | ||
4326 | 4394 | } |
4327 | 4395 | |
4328 | 4396 | @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 | |
4329 | 4460 | public void testVpnSetUnderlyingNetworks() { |
4330 | 4461 | final int uid = Process.myUid(); |
4331 | 4462 |