diff options
22 files changed, 610 insertions, 70 deletions
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 3e325b748f51..e3259ffa9d47 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -57,6 +57,9 @@ public final class NetworkCapabilities implements Parcelable { private static final String TAG = "NetworkCapabilities"; private static final int INVALID_UID = -1; + // Set to true when private DNS is broken. + private boolean mPrivateDnsBroken; + /** * @hide */ @@ -86,6 +89,7 @@ public final class NetworkCapabilities implements Parcelable { mUids = null; mEstablishingVpnAppUid = INVALID_UID; mSSID = null; + mPrivateDnsBroken = false; } /** @@ -104,6 +108,7 @@ public final class NetworkCapabilities implements Parcelable { mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; mSSID = nc.mSSID; + mPrivateDnsBroken = nc.mPrivateDnsBroken; } /** @@ -557,6 +562,9 @@ public final class NetworkCapabilities implements Parcelable { } if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth"; if (hasSignalStrength()) return "signalStrength"; + if (isPrivateDnsBroken()) { + return "privateDnsBroken"; + } return null; } @@ -1443,7 +1451,8 @@ public final class NetworkCapabilities implements Parcelable { && equalsSpecifier(that) && equalsTransportInfo(that) && equalsUids(that) - && equalsSSID(that)); + && equalsSSID(that) + && equalsPrivateDnsBroken(that)); } @Override @@ -1460,7 +1469,8 @@ public final class NetworkCapabilities implements Parcelable { + (mSignalStrength * 29) + Objects.hashCode(mUids) * 31 + Objects.hashCode(mSSID) * 37 - + Objects.hashCode(mTransportInfo) * 41; + + Objects.hashCode(mTransportInfo) * 41 + + Objects.hashCode(mPrivateDnsBroken) * 43; } @Override @@ -1479,6 +1489,7 @@ public final class NetworkCapabilities implements Parcelable { dest.writeInt(mSignalStrength); dest.writeArraySet(mUids); dest.writeString(mSSID); + dest.writeBoolean(mPrivateDnsBroken); } public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR = @@ -1498,6 +1509,7 @@ public final class NetworkCapabilities implements Parcelable { netCap.mUids = (ArraySet<UidRange>) in.readArraySet( null /* ClassLoader, null for default */); netCap.mSSID = in.readString(); + netCap.mPrivateDnsBroken = in.readBoolean(); return netCap; } @Override @@ -1555,6 +1567,10 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" SSID: ").append(mSSID); } + if (mPrivateDnsBroken) { + sb.append(" Private DNS is broken"); + } + sb.append("]"); return sb.toString(); } @@ -1706,4 +1722,28 @@ public final class NetworkCapabilities implements Parcelable { public boolean isMetered() { return !hasCapability(NET_CAPABILITY_NOT_METERED); } + + /** + * Check if private dns is broken. + * + * @return {@code true} if {@code mPrivateDnsBroken} is set when private DNS is broken. + * @hide + */ + public boolean isPrivateDnsBroken() { + return mPrivateDnsBroken; + } + + /** + * Set mPrivateDnsBroken to true when private dns is broken. + * + * @param broken the status of private DNS to be set. + * @hide + */ + public void setPrivateDnsBroken(boolean broken) { + mPrivateDnsBroken = broken; + } + + private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) { + return mPrivateDnsBroken == nc.mPrivateDnsBroken; + } } diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java index 9ba3bd940a96..4ad52d5aa1bc 100644 --- a/core/java/android/net/NetworkMisc.java +++ b/core/java/android/net/NetworkMisc.java @@ -77,6 +77,12 @@ public class NetworkMisc implements Parcelable { */ public boolean skip464xlat; + /** + * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network. + * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode. + */ + public boolean hasShownBroken; + public NetworkMisc() { } diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 13b1ce588d13..6efa2d653b4c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3434,6 +3434,15 @@ <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. --> <string name="wifi_no_internet_detailed">Tap for options</string> + <!-- A notification is shown when the user connects to a mobile network without internet access. This is the notification's title. --> + <string name="mobile_no_internet">Mobile network has no internet access</string> + + <!-- A notification is shown when the user connects to a non-mobile and non-wifi network without internet access. This is the notification's title. --> + <string name="other_networks_no_internet">Network has no internet access</string> + + <!-- A notification is shown when connected network without internet due to private dns validation failed. This is the notification's message. [CHAR LIMIT=NONE] --> + <string name="private_dns_broken_detailed">Private DNS server cannot be accessed</string> + <!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] --> <string name="captive_portal_logged_in_detailed">Connected</string> <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cf85d0c1f25d..0804703a585b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -590,9 +590,12 @@ <java-symbol type="string" name="menu_space_shortcut_label" /> <java-symbol type="string" name="menu_shift_shortcut_label" /> <java-symbol type="string" name="menu_sym_shortcut_label" /> + <java-symbol type="string" name="mobile_no_internet" /> <java-symbol type="string" name="notification_title" /> + <java-symbol type="string" name="other_networks_no_internet" /> <java-symbol type="string" name="permission_request_notification_with_subtitle" /> <java-symbol type="string" name="prepend_shortcut_label" /> + <java-symbol type="string" name="private_dns_broken_detailed" /> <java-symbol type="string" name="paste_as_plain_text" /> <java-symbol type="string" name="replace" /> <java-symbol type="string" name="undo" /> diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 53bc65d711b0..bb731a8189f9 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -16,6 +16,7 @@ package android.media; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; @@ -26,6 +27,8 @@ import android.media.audiofx.AudioEffect; import android.media.audiopolicy.AudioMix; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Map; @@ -431,6 +434,50 @@ public class AudioSystem public static final int DEAD_OBJECT = -6; public static final int WOULD_BLOCK = -7; + /** @hide */ + @IntDef({ + SUCCESS, + ERROR, + BAD_VALUE, + INVALID_OPERATION, + PERMISSION_DENIED, + NO_INIT, + DEAD_OBJECT, + WOULD_BLOCK + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AudioSystemError {} + + /** + * Convert an int error value to its String value for readability. + * Accepted error values are the java AudioSystem errors, matching android_media_AudioErrors.h, + * which map onto the native status_t type. + * @param error one of the java AudioSystem errors + * @return a human-readable string + */ + public static String audioSystemErrorToString(@AudioSystemError int error) { + switch(error) { + case SUCCESS: + return "SUCCESS"; + case ERROR: + return "ERROR"; + case BAD_VALUE: + return "BAD_VALUE"; + case INVALID_OPERATION: + return "INVALID_OPERATION"; + case PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case NO_INIT: + return "NO_INIT"; + case DEAD_OBJECT: + return "DEAD_OBJECT"; + case WOULD_BLOCK: + return "WOULD_BLOCK"; + default: + return ("unknown error:" + error); + } + } + /* * AudioPolicyService methods */ diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 39474e13a2d0..01f12500b77b 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -853,6 +853,10 @@ public class AudioPolicy { Log.v(TAG, "notifyVolumeAdjust: " + adjustment); } } + + public void notifyUnregistration() { + setRegistration(null); + } }; //================================================== diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl index 107e7cd59ca2..1d581516e7b1 100644 --- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl +++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl @@ -34,4 +34,8 @@ oneway interface IAudioPolicyCallback { // callback for volume events void notifyVolumeAdjust(int adjustment); + + // callback for unregistration (e.g. if policy couldn't automatically be re-registered after + // an audioserver crash) + void notifyUnregistration(); } diff --git a/media/java/android/media/midi/MidiDeviceInfo.aidl b/media/java/android/media/midi/MidiDeviceInfo.aidl index 5b2ac9b64663..a2482040526d 100644 --- a/media/java/android/media/midi/MidiDeviceInfo.aidl +++ b/media/java/android/media/midi/MidiDeviceInfo.aidl @@ -16,4 +16,4 @@ package android.media.midi; -parcelable MidiDeviceInfo cpp_header "media/MidiDeviceInfo.h"; +parcelable MidiDeviceInfo cpp_header "MidiDeviceInfo.h"; diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp index 58317edbea68..8f3e12e98bab 100644 --- a/media/native/midi/Android.bp +++ b/media/native/midi/Android.bp @@ -17,6 +17,7 @@ cc_library_shared { srcs: [ "amidi.cpp", + "MidiDeviceInfo.cpp", ":IMidiDeviceServer.aidl", ], @@ -31,12 +32,14 @@ cc_library_shared { "-fvisibility=hidden", ], + header_libs: [ + "media_ndk_headers", + ], + shared_libs: [ "liblog", "libbinder", "libutils", - "libmedia", - "libmediandk", "libandroid_runtime", ], diff --git a/media/native/midi/MidiDeviceInfo.cpp b/media/native/midi/MidiDeviceInfo.cpp new file mode 100644 index 000000000000..ac68d26c935b --- /dev/null +++ b/media/native/midi/MidiDeviceInfo.cpp @@ -0,0 +1,138 @@ +/* + * 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. + */ + +#define LOG_TAG "MidiDeviceInfo" + +#include <MidiDeviceInfo.h> + +#include <binder/Parcel.h> +#include <log/log.h> +#include <utils/Errors.h> +#include <utils/String16.h> + +namespace android { +namespace media { +namespace midi { + +// The constant values need to be kept in sync with MidiDeviceInfo.java. +// static +const char* const MidiDeviceInfo::PROPERTY_NAME = "name"; +const char* const MidiDeviceInfo::PROPERTY_MANUFACTURER = "manufacturer"; +const char* const MidiDeviceInfo::PROPERTY_PRODUCT = "product"; +const char* const MidiDeviceInfo::PROPERTY_VERSION = "version"; +const char* const MidiDeviceInfo::PROPERTY_SERIAL_NUMBER = "serial_number"; +const char* const MidiDeviceInfo::PROPERTY_ALSA_CARD = "alsa_card"; +const char* const MidiDeviceInfo::PROPERTY_ALSA_DEVICE = "alsa_device"; + +String16 MidiDeviceInfo::getProperty(const char* propertyName) { + String16 value; + if (mProperties.getString(String16(propertyName), &value)) { + return value; + } else { + return String16(); + } +} + +#define RETURN_IF_FAILED(calledOnce) \ + { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } + +status_t MidiDeviceInfo::writeToParcel(Parcel* parcel) const { + // Needs to be kept in sync with code in MidiDeviceInfo.java + RETURN_IF_FAILED(parcel->writeInt32(mType)); + RETURN_IF_FAILED(parcel->writeInt32(mId)); + RETURN_IF_FAILED(parcel->writeInt32((int32_t)mInputPortNames.size())); + RETURN_IF_FAILED(parcel->writeInt32((int32_t)mOutputPortNames.size())); + RETURN_IF_FAILED(writeStringVector(parcel, mInputPortNames)); + RETURN_IF_FAILED(writeStringVector(parcel, mOutputPortNames)); + RETURN_IF_FAILED(parcel->writeInt32(mIsPrivate ? 1 : 0)); + RETURN_IF_FAILED(mProperties.writeToParcel(parcel)); + // This corresponds to "extra" properties written by Java code + RETURN_IF_FAILED(mProperties.writeToParcel(parcel)); + return OK; +} + +status_t MidiDeviceInfo::readFromParcel(const Parcel* parcel) { + // Needs to be kept in sync with code in MidiDeviceInfo.java + RETURN_IF_FAILED(parcel->readInt32(&mType)); + RETURN_IF_FAILED(parcel->readInt32(&mId)); + int32_t inputPortCount; + RETURN_IF_FAILED(parcel->readInt32(&inputPortCount)); + int32_t outputPortCount; + RETURN_IF_FAILED(parcel->readInt32(&outputPortCount)); + RETURN_IF_FAILED(readStringVector(parcel, &mInputPortNames, inputPortCount)); + RETURN_IF_FAILED(readStringVector(parcel, &mOutputPortNames, outputPortCount)); + int32_t isPrivate; + RETURN_IF_FAILED(parcel->readInt32(&isPrivate)); + mIsPrivate = isPrivate == 1; + RETURN_IF_FAILED(mProperties.readFromParcel(parcel)); + // Ignore "extra" properties as they may contain Java Parcelables + return OK; +} + +status_t MidiDeviceInfo::readStringVector( + const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength) { + std::unique_ptr<std::vector<std::unique_ptr<String16>>> v; + status_t result = parcel->readString16Vector(&v); + if (result != OK) return result; + vectorPtr->clear(); + if (v.get() != nullptr) { + for (const auto& iter : *v) { + if (iter.get() != nullptr) { + vectorPtr->push_back(*iter); + } else { + vectorPtr->push_back(String16()); + } + } + } else { + vectorPtr->resize(defaultLength); + } + return OK; +} + +status_t MidiDeviceInfo::writeStringVector(Parcel* parcel, const Vector<String16>& vector) const { + std::vector<String16> v; + for (size_t i = 0; i < vector.size(); ++i) { + v.push_back(vector[i]); + } + return parcel->writeString16Vector(v); +} + +// Vector does not define operator== +static inline bool areVectorsEqual(const Vector<String16>& lhs, const Vector<String16>& rhs) { + if (lhs.size() != rhs.size()) return false; + for (size_t i = 0; i < lhs.size(); ++i) { + if (lhs[i] != rhs[i]) return false; + } + return true; +} + +bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) { + return (lhs.mType == rhs.mType && lhs.mId == rhs.mId && + areVectorsEqual(lhs.mInputPortNames, rhs.mInputPortNames) && + areVectorsEqual(lhs.mOutputPortNames, rhs.mOutputPortNames) && + lhs.mProperties == rhs.mProperties && + lhs.mIsPrivate == rhs.mIsPrivate); +} + +} // namespace midi +} // namespace media +} // namespace android diff --git a/media/native/midi/MidiDeviceInfo.h b/media/native/midi/MidiDeviceInfo.h new file mode 100644 index 000000000000..5b4a241323d7 --- /dev/null +++ b/media/native/midi/MidiDeviceInfo.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef ANDROID_MEDIA_MIDI_DEVICE_INFO_H +#define ANDROID_MEDIA_MIDI_DEVICE_INFO_H + +#include <binder/Parcelable.h> +#include <binder/PersistableBundle.h> +#include <utils/String16.h> +#include <utils/Vector.h> + +namespace android { +namespace media { +namespace midi { + +class MidiDeviceInfo : public Parcelable { +public: + MidiDeviceInfo() = default; + virtual ~MidiDeviceInfo() = default; + MidiDeviceInfo(const MidiDeviceInfo& midiDeviceInfo) = default; + + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + + int getType() const { return mType; } + int getUid() const { return mId; } + bool isPrivate() const { return mIsPrivate; } + const Vector<String16>& getInputPortNames() const { return mInputPortNames; } + const Vector<String16>& getOutputPortNames() const { return mOutputPortNames; } + String16 getProperty(const char* propertyName); + + // The constants need to be kept in sync with MidiDeviceInfo.java + enum { + TYPE_USB = 1, + TYPE_VIRTUAL = 2, + TYPE_BLUETOOTH = 3, + }; + static const char* const PROPERTY_NAME; + static const char* const PROPERTY_MANUFACTURER; + static const char* const PROPERTY_PRODUCT; + static const char* const PROPERTY_VERSION; + static const char* const PROPERTY_SERIAL_NUMBER; + static const char* const PROPERTY_ALSA_CARD; + static const char* const PROPERTY_ALSA_DEVICE; + + friend bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs); + friend bool operator!=(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) { + return !(lhs == rhs); + } + +private: + status_t readStringVector( + const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength); + status_t writeStringVector(Parcel* parcel, const Vector<String16>& vector) const; + + int32_t mType; + int32_t mId; + Vector<String16> mInputPortNames; + Vector<String16> mOutputPortNames; + os::PersistableBundle mProperties; + bool mIsPrivate; +}; + +} // namespace midi +} // namespace media +} // namespace android + +#endif // ANDROID_MEDIA_MIDI_DEVICE_INFO_H diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp index 1e9a194d76c8..e7c4d9983f74 100644 --- a/media/native/midi/amidi.cpp +++ b/media/native/midi/amidi.cpp @@ -26,7 +26,7 @@ #include <core_jni_helpers.h> #include "android/media/midi/BpMidiDeviceServer.h" -#include "media/MidiDeviceInfo.h" +#include "MidiDeviceInfo.h" #include "include/amidi/AMidi.h" #include "amidi_internal.h" diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 0e9183940f22..bdfa21742854 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -127,6 +127,9 @@ <!-- Summary for Connected wifi network without internet --> <string name="wifi_connected_no_internet">Connected, no internet</string> + <!-- Summary for connected network without internet due to private dns validation failed [CHAR LIMIT=NONE] --> + <string name="private_dns_broken">Private DNS server cannot be accessed</string> + <!-- Summary for connected wifi network with partial internet connectivity [CHAR LIMIT=50] --> <string name="wifi_limited_connection">Limited connection</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 6b1ceae4bed8..386ff65a4bf4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -50,6 +50,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; +import android.provider.Settings; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; @@ -1569,7 +1570,13 @@ public class AccessPoint implements Comparable<AccessPoint> { NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { return context.getString(R.string.wifi_limited_connection); } else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { - return context.getString(R.string.wifi_connected_no_internet); + final String mode = Settings.Global.getString(context.getContentResolver(), + Settings.Global.PRIVATE_DNS_MODE); + if (nc.isPrivateDnsBroken()) { + return context.getString(R.string.private_dns_broken); + } else { + return context.getString(R.string.wifi_connected_no_internet); + } } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index 5352936d7224..b11585a73946 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -31,6 +31,7 @@ import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiSsid; import android.os.Handler; import android.os.Looper; +import android.provider.Settings; import com.android.settingslib.R; @@ -163,7 +164,13 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { statusLabel = mContext.getString(R.string.wifi_limited_connection); return; } else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { - statusLabel = mContext.getString(R.string.wifi_status_no_internet); + final String mode = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.PRIVATE_DNS_MODE); + if (networkCapabilities.isPrivateDnsBroken()) { + statusLabel = mContext.getString(R.string.private_dns_broken); + } else { + statusLabel = mContext.getString(R.string.wifi_status_no_internet); + } return; } } diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index ffbf1ae38635..ef071a43aa3e 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -249,6 +249,8 @@ message SystemMessage { NOTE_NETWORK_LOGGED_IN = 744; // A partial connectivity network was detected during network validation NOTE_NETWORK_PARTIAL_CONNECTIVITY = 745; + // Private DNS is broken in strict mode + NOTE_NETWORK_PRIVATE_DNS_BROKEN = 746; // Notify the user that their work profile has been deleted // Package: android diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 0bb72cb9d554..ce0e9e7e56cf 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -26,6 +26,7 @@ import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; @@ -528,6 +529,15 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45; /** + * Event for NetworkMonitor to inform ConnectivityService that the probe status has changed. + * Both of the arguments are bitmasks, and the value of bits come from + * INetworkMonitor.NETWORK_VALIDATION_PROBE_*. + * arg1 = A bitmask to describe which probes are completed. + * arg2 = A bitmask to describe which probes are successful. + */ + public static final int EVENT_PROBE_STATUS_CHANGED = 46; + + /** * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification * should be shown. */ @@ -2663,6 +2673,41 @@ public class ConnectivityService extends IConnectivityManager.Stub switch (msg.what) { default: return false; + case EVENT_PROBE_STATUS_CHANGED: { + final Integer netId = (Integer) msg.obj; + final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId); + if (nai == null) { + break; + } + final boolean probePrivateDnsCompleted = + ((msg.arg1 & NETWORK_VALIDATION_PROBE_PRIVDNS) != 0); + final boolean privateDnsBroken = + ((msg.arg2 & NETWORK_VALIDATION_PROBE_PRIVDNS) == 0); + if (probePrivateDnsCompleted) { + if (nai.networkCapabilities.isPrivateDnsBroken() != privateDnsBroken) { + nai.networkCapabilities.setPrivateDnsBroken(privateDnsBroken); + final int oldScore = nai.getCurrentScore(); + updateCapabilities(oldScore, nai, nai.networkCapabilities); + } + // Only show the notification when the private DNS is broken and the + // PRIVATE_DNS_BROKEN notification hasn't shown since last valid. + if (privateDnsBroken && !nai.networkMisc.hasShownBroken) { + showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN); + } + nai.networkMisc.hasShownBroken = privateDnsBroken; + } else if (nai.networkCapabilities.isPrivateDnsBroken()) { + // If probePrivateDnsCompleted is false but nai.networkCapabilities says + // private DNS is broken, it means this network is being reevaluated. + // Either probing private DNS is not necessary any more or it hasn't been + // done yet. In either case, the networkCapabilities should be updated to + // reflect the new status. + nai.networkCapabilities.setPrivateDnsBroken(false); + final int oldScore = nai.getCurrentScore(); + updateCapabilities(oldScore, nai, nai.networkCapabilities); + nai.networkMisc.hasShownBroken = false; + } + break; + } case EVENT_NETWORK_TESTED: { final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2); if (nai == null) break; @@ -2705,14 +2750,20 @@ public class ConnectivityService extends IConnectivityManager.Stub if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); if (valid) { handleFreshlyValidatedNetwork(nai); - // Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET - // notifications if network becomes valid. + // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and + // LOST_INTERNET notifications if network becomes valid. mNotifier.clearNotification(nai.network.netId, NotificationType.NO_INTERNET); mNotifier.clearNotification(nai.network.netId, NotificationType.LOST_INTERNET); mNotifier.clearNotification(nai.network.netId, NotificationType.PARTIAL_CONNECTIVITY); + mNotifier.clearNotification(nai.network.netId, + NotificationType.PRIVATE_DNS_BROKEN); + // If network becomes valid, the hasShownBroken should be reset for + // that network so that the notification will be fired when the private + // DNS is broken again. + nai.networkMisc.hasShownBroken = false; } } else if (partialConnectivityChanged) { updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); @@ -2863,6 +2914,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override + public void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) { + mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage( + EVENT_PROBE_STATUS_CHANGED, + probesCompleted, probesSucceeded, new Integer(mNetId))); + } + + @Override public void showProvisioningNotification(String action, String packageName) { final Intent intent = new Intent(action); intent.setPackage(packageName); @@ -3679,6 +3737,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // High priority because it is only displayed for explicitly selected networks. highPriority = true; break; + case PRIVATE_DNS_BROKEN: + action = Settings.ACTION_WIRELESS_SETTINGS; + // High priority because we should let user know why there is no internet. + highPriority = true; + break; case LOST_INTERNET: action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION; // High priority because it could help the user avoid unexpected data usage. @@ -3696,7 +3759,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } Intent intent = new Intent(action); - if (type != NotificationType.LOGGED_IN) { + if (type != NotificationType.LOGGED_IN && type != NotificationType.PRIVATE_DNS_BROKEN) { intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClassName("com.android.settings", @@ -5162,6 +5225,13 @@ public class ConnectivityService extends IConnectivityManager.Stub ns.assertValidFromUid(Binder.getCallingUid()); } + private void ensureValid(NetworkCapabilities nc) { + ensureValidNetworkSpecifier(nc); + if (nc.isPrivateDnsBroken()) { + throw new IllegalArgumentException("Can't request broken private DNS"); + } + } + @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { @@ -5195,7 +5265,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (timeoutMs < 0) { throw new IllegalArgumentException("Bad timeout specified"); } - ensureValidNetworkSpecifier(networkCapabilities); + ensureValid(networkCapabilities); NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId(), type); @@ -5337,7 +5407,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE // can't request networks. restrictBackgroundRequestForCaller(nc); - ensureValidNetworkSpecifier(nc); + ensureValid(nc); NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); @@ -5355,7 +5425,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!hasWifiNetworkListenPermission(networkCapabilities)) { enforceAccessPermission(); } - ensureValidNetworkSpecifier(networkCapabilities); + ensureValid(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid()); @@ -5841,6 +5911,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { newNc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY); } + newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken()); return newNc; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e26cbc4487f9..055a4bde8a78 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1018,7 +1018,14 @@ public class AudioService extends IAudioService.Stub synchronized (mAudioPolicies) { for (AudioPolicyProxy policy : mAudioPolicies.values()) { - policy.connectMixes(); + final int status = policy.connectMixes(); + if (status != AudioSystem.SUCCESS) { + // note that PERMISSION_DENIED may also indicate trouble getting to APService + Log.e(TAG, "onAudioServerDied: error " + + AudioSystem.audioSystemErrorToString(status) + + " when connecting mixes for policy " + policy.toLogFriendlyString()); + policy.release(); + } } } @@ -7007,16 +7014,8 @@ public class AudioService extends IAudioService.Stub } public void binderDied() { - synchronized (mAudioPolicies) { - Log.i(TAG, "audio policy " + mPolicyCallback + " died"); - release(); - mAudioPolicies.remove(mPolicyCallback.asBinder()); - } - if (mIsVolumeController) { - synchronized (mExtVolumeControllerLock) { - mExtVolumeController = null; - } - } + Log.i(TAG, "audio policy " + mPolicyCallback + " died"); + release(); } String getRegistrationId() { @@ -7040,9 +7039,20 @@ public class AudioService extends IAudioService.Stub Log.e(TAG, "Fail to unregister Audiopolicy callback from MediaProjection"); } } + if (mIsVolumeController) { + synchronized (mExtVolumeControllerLock) { + mExtVolumeController = null; + } + } final long identity = Binder.clearCallingIdentity(); AudioSystem.registerPolicyMixes(mMixes, false); Binder.restoreCallingIdentity(identity); + synchronized (mAudioPolicies) { + mAudioPolicies.remove(mPolicyCallback.asBinder()); + } + try { + mPolicyCallback.notifyUnregistration(); + } catch (RemoteException e) { } } boolean hasMixAffectingUsage(int usage, int excludedFlags) { @@ -7093,7 +7103,7 @@ public class AudioService extends IAudioService.Stub } } - int connectMixes() { + @AudioSystem.AudioSystemError int connectMixes() { final long identity = Binder.clearCallingIdentity(); int status = AudioSystem.registerPolicyMixes(mMixes, true); Binder.restoreCallingIdentity(identity); diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 077c4057a3a0..d13e6753d1b0 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -53,7 +53,8 @@ public class NetworkNotificationManager { NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET), LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN), PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY), - SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN); + SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN), + PRIVATE_DNS_BROKEN(SystemMessage.NOTE_NETWORK_PRIVATE_DNS_BROKEN); public final int eventId; @@ -175,13 +176,23 @@ public class NetworkNotificationManager { } Resources r = Resources.getSystem(); - CharSequence title; - CharSequence details; + final CharSequence title; + final CharSequence details; int icon = getIcon(transportType, notifyType); if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) { title = r.getString(R.string.wifi_no_internet, WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID())); details = r.getString(R.string.wifi_no_internet_detailed); + } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) { + if (transportType == TRANSPORT_CELLULAR) { + title = r.getString(R.string.mobile_no_internet); + } else if (transportType == TRANSPORT_WIFI) { + title = r.getString(R.string.wifi_no_internet, + WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID())); + } else { + title = r.getString(R.string.other_networks_no_internet); + } + details = r.getString(R.string.private_dns_broken_detailed); } else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY && transportType == TRANSPORT_WIFI) { title = r.getString(R.string.network_partial_connectivity, @@ -357,8 +368,10 @@ public class NetworkNotificationManager { } switch (t) { case SIGN_IN: - return 5; + return 6; case PARTIAL_CONNECTIVITY: + return 5; + case PRIVATE_DNS_BROKEN: return 4; case NO_INTERNET: return 3; diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 3f348a4bda8c..60290e3b785d 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -2049,6 +2049,10 @@ public final class Call { return "DISCONNECTING"; case STATE_SELECT_PHONE_ACCOUNT: return "SELECT_PHONE_ACCOUNT"; + case STATE_SIMULATED_RINGING: + return "SIMULATED_RINGING"; + case STATE_AUDIO_PROCESSING: + return "AUDIO_PROCESSING"; default: Log.w(Call.class, "Unknown state %d", state); return "UNKNOWN"; diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 2ca0d1a81e13..15691127cab7 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -271,7 +271,7 @@ public class NetworkCapabilitiesTest { .addCapability(NET_CAPABILITY_NOT_METERED); assertParcelingIsLossless(netCap); netCap.setSSID(TEST_SSID); - assertParcelSane(netCap, 11); + assertParcelSane(netCap, 12); } @Test diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index cf3fba8bef91..61f37fd6c7e2 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -33,6 +33,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; @@ -492,6 +493,8 @@ public class ConnectivityServiceTest { private INetworkMonitor mNetworkMonitor; private INetworkMonitorCallbacks mNmCallbacks; private int mNmValidationResult = VALIDATION_RESULT_BASE; + private int mProbesCompleted; + private int mProbesSucceeded; private String mNmValidationRedirectUrl = null; private boolean mNmProvNotificationRequested = false; @@ -559,6 +562,7 @@ public class ConnectivityServiceTest { mNmProvNotificationRequested = false; } + mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded); mNmCallbacks.notifyNetworkTested( mNmValidationResult, mNmValidationRedirectUrl); @@ -581,7 +585,7 @@ public class ConnectivityServiceTest { * @param validated Indicate if network should pretend to be validated. */ public void connect(boolean validated) { - connect(validated, true); + connect(validated, true, false /* isStrictMode */); } /** @@ -589,13 +593,13 @@ public class ConnectivityServiceTest { * @param validated Indicate if network should pretend to be validated. * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET. */ - public void connect(boolean validated, boolean hasInternet) { + public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) { assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET)); ConnectivityManager.NetworkCallback callback = null; final ConditionVariable validatedCv = new ConditionVariable(); if (validated) { - setNetworkValid(); + setNetworkValid(isStrictMode); NetworkRequest request = new NetworkRequest.Builder() .addTransportType(getNetworkCapabilities().getTransportTypes()[0]) .clearCapabilities() @@ -620,15 +624,15 @@ public class ConnectivityServiceTest { if (validated) { // Wait for network to validate. waitFor(validatedCv); - setNetworkInvalid(); + setNetworkInvalid(isStrictMode); } if (callback != null) mCm.unregisterNetworkCallback(callback); } - public void connectWithCaptivePortal(String redirectUrl) { - setNetworkPortal(redirectUrl); - connect(false); + public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) { + setNetworkPortal(redirectUrl, isStrictMode); + connect(false, true /* hasInternet */, isStrictMode); } public void connectWithPartialConnectivity() { @@ -636,34 +640,75 @@ public class ConnectivityServiceTest { connect(false); } - public void connectWithPartialValidConnectivity() { - setNetworkPartialValid(); - connect(false); + public void connectWithPartialValidConnectivity(boolean isStrictMode) { + setNetworkPartialValid(isStrictMode); + connect(false, true /* hasInternet */, isStrictMode); } - void setNetworkValid() { + void setNetworkValid(boolean isStrictMode) { mNmValidationResult = VALIDATION_RESULT_VALID; mNmValidationRedirectUrl = null; + int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTP; + if (isStrictMode) { + probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS; + } + // The probesCompleted equals to probesSucceeded for the case of valid network, so put + // the same value into two different parameter of the method. + setProbesStatus(probesSucceeded, probesSucceeded); } - void setNetworkInvalid() { + void setNetworkInvalid(boolean isStrictMode) { mNmValidationResult = VALIDATION_RESULT_INVALID; mNmValidationRedirectUrl = null; + int probesCompleted = VALIDATION_RESULT_BASE; + int probesSucceeded = VALIDATION_RESULT_INVALID; + // If the isStrictMode is true, it means the network is invalid when NetworkMonitor + // tried to validate the private DNS but failed. + if (isStrictMode) { + probesCompleted &= ~NETWORK_VALIDATION_PROBE_HTTP; + probesSucceeded = probesCompleted; + probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; + } + setProbesStatus(probesCompleted, probesSucceeded); } - void setNetworkPortal(String redirectUrl) { - setNetworkInvalid(); + void setNetworkPortal(String redirectUrl, boolean isStrictMode) { + setNetworkInvalid(isStrictMode); mNmValidationRedirectUrl = redirectUrl; + // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP + // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet. + int probesCompleted = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS; + int probesSucceeded = VALIDATION_RESULT_INVALID; + if (isStrictMode) { + probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; + } + setProbesStatus(probesCompleted, probesSucceeded); } void setNetworkPartial() { mNmValidationResult = VALIDATION_RESULT_PARTIAL; mNmValidationRedirectUrl = null; + int probesCompleted = VALIDATION_RESULT_BASE; + int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS; + setProbesStatus(probesCompleted, probesSucceeded); } - void setNetworkPartialValid() { - mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID; - mNmValidationRedirectUrl = null; + void setNetworkPartialValid(boolean isStrictMode) { + setNetworkPartial(); + mNmValidationResult |= VALIDATION_RESULT_VALID; + int probesCompleted = VALIDATION_RESULT_BASE; + int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS; + // Suppose the partial network cannot pass the private DNS validation as well, so only + // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded. + if (isStrictMode) { + probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; + } + setProbesStatus(probesCompleted, probesSucceeded); + } + + void setProbesStatus(int probesCompleted, int probesSucceeded) { + mProbesCompleted = probesCompleted; + mProbesSucceeded = probesSucceeded; } public String waitForRedirectUrl() { @@ -2226,7 +2271,7 @@ public class ConnectivityServiceTest { // With HTTPS probe disabled, NetworkMonitor should pass the network validation with http // probe. - mWiFiNetworkAgent.setNetworkPartialValid(); + mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */); // If the user chooses yes to use this partial connectivity wifi, switch the default // network to wifi and check if wifi becomes valid or not. mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, @@ -2299,7 +2344,7 @@ public class ConnectivityServiceTest { callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); - mWiFiNetworkAgent.setNetworkValid(); + mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is // validated. @@ -2317,7 +2362,7 @@ public class ConnectivityServiceTest { // NetworkMonitor will immediately (once the HTTPS probe fails...) report the network as // valid, because ConnectivityService calls setAcceptPartialConnectivity before it calls // notifyNetworkConnected. - mWiFiNetworkAgent.connectWithPartialValidConnectivity(); + mWiFiNetworkAgent.connectWithPartialValidConnectivity(false /* isStrictMode */); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); @@ -2343,7 +2388,7 @@ public class ConnectivityServiceTest { // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); String redirectUrl = "http://android.com/path"; - mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl); + mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */); captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl); @@ -2361,7 +2406,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent); // Report partial connectivity is accepted. - mWiFiNetworkAgent.setNetworkPartialValid(); + mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */); mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, false /* always */); waitForIdle(); @@ -2392,7 +2437,7 @@ public class ConnectivityServiceTest { // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); String firstRedirectUrl = "http://example.com/firstPath"; - mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); + mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */); captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl); @@ -2405,13 +2450,13 @@ public class ConnectivityServiceTest { // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); String secondRedirectUrl = "http://example.com/secondPath"; - mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl); + mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl, false /* isStrictMode */); captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl); // Make captive portal disappear then revalidate. // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent.setNetworkValid(); + mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); @@ -2423,7 +2468,7 @@ public class ConnectivityServiceTest { // Break network connectivity. // Expect NET_CAPABILITY_VALIDATED onLost callback. - mWiFiNetworkAgent.setNetworkInvalid(); + mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false); validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); } @@ -2454,7 +2499,7 @@ public class ConnectivityServiceTest { mServiceContext.expectNoStartActivityIntent(fastTimeoutMs); // Turn into a captive portal. - mWiFiNetworkAgent.setNetworkPortal("http://example.com"); + mWiFiNetworkAgent.setNetworkPortal("http://example.com", false /* isStrictMode */); mCm.reportNetworkConnectivity(wifiNetwork, false); captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); @@ -2475,7 +2520,7 @@ public class ConnectivityServiceTest { assertEquals(testValue, signInIntent.getStringExtra(testKey)); // Report that the captive portal is dismissed, and check that callbacks are fired - mWiFiNetworkAgent.setNetworkValid(); + mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); @@ -2504,7 +2549,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); String firstRedirectUrl = "http://example.com/firstPath"; - mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); + mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */); mWiFiNetworkAgent.expectDisconnected(); mWiFiNetworkAgent.expectPreventReconnectReceived(); @@ -3219,7 +3264,7 @@ public class ConnectivityServiceTest { Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); // Fail validation on wifi. - mWiFiNetworkAgent.setNetworkInvalid(); + mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); mCm.reportNetworkConnectivity(wifiNetwork, false); defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); @@ -3263,7 +3308,7 @@ public class ConnectivityServiceTest { wifiNetwork = mWiFiNetworkAgent.getNetwork(); // Fail validation on wifi and expect the dialog to appear. - mWiFiNetworkAgent.setNetworkInvalid(); + mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); mCm.reportNetworkConnectivity(wifiNetwork, false); defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); @@ -4299,7 +4344,7 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(request, callback); // Bring up wifi aware network. - wifiAware.connect(false, false); + wifiAware.connect(false, false, false /* isStrictMode */); callback.expectAvailableCallbacksUnvalidated(wifiAware); assertNull(mCm.getActiveNetworkInfo()); @@ -4537,6 +4582,44 @@ public class ConnectivityServiceTest { } @Test + public void testPrivateDnsNotification() throws Exception { + NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) + .build(); + TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + // Bring up wifi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + // Private DNS resolution failed, checking if the notification will be shown or not. + mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + // If network validation failed, NetworkMonitor will re-evaluate the network. + // ConnectivityService should filter the redundant notification. This part is trying to + // simulate that situation and check if ConnectivityService could filter that case. + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notifyAsUser(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL)); + // If private DNS resolution successful, the PRIVATE_DNS_BROKEN notification shouldn't be + // shown. + mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancelAsUser(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), eq(UserHandle.ALL)); + // If private DNS resolution failed again, the PRIVATE_DNS_BROKEN notification should be + // shown again. + mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notifyAsUser(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL)); + } + + @Test public void testPrivateDnsSettingsChange() throws Exception { // Clear any interactions that occur as a result of CS starting up. reset(mMockDnsResolver); @@ -4793,7 +4876,7 @@ public class ConnectivityServiceTest { // by NetworkMonitor assertFalse(NetworkMonitorUtils.isValidationRequired( vpnNetworkAgent.getNetworkCapabilities())); - vpnNetworkAgent.setNetworkValid(); + vpnNetworkAgent.setNetworkValid(false /* isStrictMode */); vpnNetworkAgent.connect(false); mMockVpn.connect(); @@ -4882,7 +4965,8 @@ public class ConnectivityServiceTest { ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */); + vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + false /* isStrictMode */); mMockVpn.connect(); defaultCallback.assertNoCallback(); @@ -4913,7 +4997,8 @@ public class ConnectivityServiceTest { ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */); + vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */, + false /* isStrictMode */); mMockVpn.connect(); defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); @@ -4945,7 +5030,8 @@ public class ConnectivityServiceTest { ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */); + vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */, + false /* isStrictMode */); mMockVpn.connect(); // Even though the VPN is unvalidated, it becomes the default network for our app. @@ -4968,7 +5054,7 @@ public class ConnectivityServiceTest { vpnNetworkAgent.getNetworkCapabilities())); // Pretend that the VPN network validates. - vpnNetworkAgent.setNetworkValid(); + vpnNetworkAgent.setNetworkValid(false /* isStrictMode */); vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); // Expect to see the validated capability, but no other changes, because the VPN is already // the default network for the app. @@ -5000,7 +5086,8 @@ public class ConnectivityServiceTest { mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.connect(); mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */); + vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + false /* isStrictMode */); vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); @@ -5099,7 +5186,8 @@ public class ConnectivityServiceTest { mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.connect(); mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */); + vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + false /* isStrictMode */); vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); |