diff options
14 files changed, 269 insertions, 150 deletions
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index 836624beb3b2..407ff04dc4a3 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -101,6 +101,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { private final boolean mIsBypassable; // Defaults in builder private final boolean mIsMetered; // Defaults in builder private final int mMaxMtu; // Defaults in builder + private final boolean mIsRestrictedToTestNetworks; private Ikev2VpnProfile( int type, @@ -116,7 +117,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull List<String> allowedAlgorithms, boolean isBypassable, boolean isMetered, - int maxMtu) { + int maxMtu, + boolean restrictToTestNetworks) { super(type); checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address"); @@ -140,6 +142,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { mIsBypassable = isBypassable; mIsMetered = isMetered; mMaxMtu = maxMtu; + mIsRestrictedToTestNetworks = restrictToTestNetworks; validate(); } @@ -329,6 +332,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { return mMaxMtu; } + /** + * Returns whether or not this VPN profile is restricted to test networks. + * + * @hide + */ + public boolean isRestrictedToTestNetworks() { + return mIsRestrictedToTestNetworks; + } + @Override public int hashCode() { return Objects.hash( @@ -345,7 +357,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { mAllowedAlgorithms, mIsBypassable, mIsMetered, - mMaxMtu); + mMaxMtu, + mIsRestrictedToTestNetworks); } @Override @@ -368,7 +381,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms) && mIsBypassable == other.mIsBypassable && mIsMetered == other.mIsMetered - && mMaxMtu == other.mMaxMtu; + && mMaxMtu == other.mMaxMtu + && mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks; } /** @@ -381,7 +395,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { */ @NonNull public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException { - final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */); + final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */, + mIsRestrictedToTestNetworks); profile.type = mType; profile.server = mServerAddr; profile.ipsecIdentifier = mUserIdentity; @@ -449,6 +464,9 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { builder.setBypassable(profile.isBypassable); builder.setMetered(profile.isMetered); builder.setMaxMtu(profile.maxMtu); + if (profile.isRestrictedToTestNetworks) { + builder.restrictToTestNetworks(); + } switch (profile.type) { case TYPE_IKEV2_IPSEC_USER_PASS: @@ -621,6 +639,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { private boolean mIsBypassable = false; private boolean mIsMetered = true; private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; + private boolean mIsRestrictedToTestNetworks = false; /** * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN. @@ -842,6 +861,21 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { } /** + * Restricts this profile to use test networks (only). + * + * <p>This method is for testing only, and must not be used by apps. Calling + * provisionVpnProfile() with a profile where test-network usage is enabled will require the + * MANAGE_TEST_NETWORKS permission. + * + * @hide + */ + @NonNull + public Builder restrictToTestNetworks() { + mIsRestrictedToTestNetworks = true; + return this; + } + + /** * Validates, builds and provisions the VpnProfile. * * @throws IllegalArgumentException if any of the required keys or values were invalid @@ -862,7 +896,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { mAllowedAlgorithms, mIsBypassable, mIsMetered, - mMaxMtu); + mMaxMtu, + mIsRestrictedToTestNetworks); } } } diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index 829bd8a5a2a7..8ea5aa815a1c 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -136,13 +136,19 @@ public final class VpnProfile implements Cloneable, Parcelable { public boolean isMetered = false; // 21 public int maxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT; // 22 public boolean areAuthParamsInline = false; // 23 + public final boolean isRestrictedToTestNetworks; // 24 // Helper fields. @UnsupportedAppUsage public transient boolean saveLogin = false; public VpnProfile(String key) { + this(key, false); + } + + public VpnProfile(String key, boolean isRestrictedToTestNetworks) { this.key = key; + this.isRestrictedToTestNetworks = isRestrictedToTestNetworks; } @UnsupportedAppUsage @@ -171,6 +177,7 @@ public final class VpnProfile implements Cloneable, Parcelable { isMetered = in.readBoolean(); maxMtu = in.readInt(); areAuthParamsInline = in.readBoolean(); + isRestrictedToTestNetworks = in.readBoolean(); } /** @@ -220,6 +227,7 @@ public final class VpnProfile implements Cloneable, Parcelable { out.writeBoolean(isMetered); out.writeInt(maxMtu); out.writeBoolean(areAuthParamsInline); + out.writeBoolean(isRestrictedToTestNetworks); } /** @@ -237,12 +245,21 @@ public final class VpnProfile implements Cloneable, Parcelable { String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1); // Acceptable numbers of values are: // 14-19: Standard profile, with option for serverCert, proxy - // 24: Standard profile with serverCert, proxy and platform-VPN parameters. - if ((values.length < 14 || values.length > 19) && values.length != 24) { + // 24: Standard profile with serverCert, proxy and platform-VPN parameters + // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks + if ((values.length < 14 || values.length > 19) + && values.length != 24 && values.length != 25) { return null; } - VpnProfile profile = new VpnProfile(key); + final boolean isRestrictedToTestNetworks; + if (values.length >= 25) { + isRestrictedToTestNetworks = Boolean.parseBoolean(values[24]); + } else { + isRestrictedToTestNetworks = false; + } + + VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks); profile.name = values[0]; profile.type = Integer.parseInt(values[1]); if (profile.type < 0 || profile.type > TYPE_MAX) { @@ -283,6 +300,8 @@ public final class VpnProfile implements Cloneable, Parcelable { profile.areAuthParamsInline = Boolean.parseBoolean(values[23]); } + // isRestrictedToTestNetworks (values[24]) assigned as part of the constructor + profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty(); return profile; } catch (Exception e) { @@ -330,6 +349,7 @@ public final class VpnProfile implements Cloneable, Parcelable { builder.append(VALUE_DELIMITER).append(isMetered); builder.append(VALUE_DELIMITER).append(maxMtu); builder.append(VALUE_DELIMITER).append(areAuthParamsInline); + builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks); return builder.toString().getBytes(StandardCharsets.UTF_8); } @@ -421,7 +441,8 @@ public final class VpnProfile implements Cloneable, Parcelable { return Objects.hash( key, type, server, username, password, dnsServers, searchDomains, routes, mppe, l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert, - proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline); + proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline, + isRestrictedToTestNetworks); } /** Checks VPN profiles for interior equality. */ @@ -453,7 +474,8 @@ public final class VpnProfile implements Cloneable, Parcelable { && isBypassable == other.isBypassable && isMetered == other.isMetered && maxMtu == other.maxMtu - && areAuthParamsInline == other.areAuthParamsInline; + && areAuthParamsInline == other.areAuthParamsInline + && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks; } @NonNull diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp new file mode 100644 index 000000000000..94e6a8096849 --- /dev/null +++ b/data/fonts/Android.bp @@ -0,0 +1,36 @@ +// Copyright (C) 2011 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. + +prebuilt_font { + name: "DroidSansMono.ttf", + src: "DroidSansMono.ttf", + required: [ + "DroidSans.ttf", + "DroidSans-Bold.ttf", + ], +} + +prebuilt_font { + name: "AndroidClock.ttf", + src: "AndroidClock.ttf", +} + +///////////////////////////////// +// Copies the font configuration file into system/etc for the product as fonts.xml. +// Additional fonts should be installed to /product/fonts/ alongside a corresponding +// fonts_customiztion.xml in /product/etc/ +prebuilt_etc { + name: "fonts.xml", + src: "fonts.xml", +} diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index 4226e0882538..e22b72346249 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -12,9 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# We have to use BUILD_PREBUILT instead of PRODUCT_COPY_FIES, -# because MINIMAL_FONT_FOOTPRINT is only available in Android.mks. - LOCAL_PATH := $(call my-dir) ########################################## @@ -37,61 +34,7 @@ endef ########################################## $(eval $(call create-font-symlink,DroidSans.ttf,Roboto-Regular.ttf)) $(eval $(call create-font-symlink,DroidSans-Bold.ttf,Roboto-Bold.ttf)) -$(eval $(call create-font-symlink,DroidSerif-Regular.ttf,NotoSerif-Regular.ttf)) -$(eval $(call create-font-symlink,DroidSerif-Bold.ttf,NotoSerif-Bold.ttf)) -$(eval $(call create-font-symlink,DroidSerif-Italic.ttf,NotoSerif-Italic.ttf)) -$(eval $(call create-font-symlink,DroidSerif-BoldItalic.ttf,NotoSerif-BoldItalic.ttf)) - -extra_font_files := \ - DroidSans.ttf \ - DroidSans-Bold.ttf - -################################ -# Use DroidSansMono to hang extra_font_files on -include $(CLEAR_VARS) -LOCAL_MODULE := DroidSansMono.ttf -LOCAL_SRC_FILES := $(LOCAL_MODULE) -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts -LOCAL_REQUIRED_MODULES := $(extra_font_files) -include $(BUILD_PREBUILT) -extra_font_files := - -################################ -# Build the rest of font files as prebuilt. - -# $(1): The source file name in LOCAL_PATH. -# It also serves as the module name and the dest file name. -define build-one-font-module -$(eval include $(CLEAR_VARS))\ -$(eval LOCAL_MODULE := $(1))\ -$(eval LOCAL_SRC_FILES := $(1))\ -$(eval LOCAL_MODULE_CLASS := ETC)\ -$(eval LOCAL_MODULE_TAGS := optional)\ -$(eval LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts)\ -$(eval include $(BUILD_PREBUILT)) -endef - -font_src_files := \ - AndroidClock.ttf - -$(foreach f, $(font_src_files), $(call build-one-font-module, $(f))) - -build-one-font-module := -font_src_files := - -################################ -# Copies the font configuration file into system/etc for the product as fonts.xml. -# Additional fonts should be installed to /product/fonts/ alongside a corresponding -# fonts_customiztion.xml in /product/etc/ -include $(CLEAR_VARS) - -LOCAL_MODULE := fonts.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_PREBUILT_MODULE_FILE := frameworks/base/data/fonts/fonts.xml -include $(BUILD_PREBUILT) # Run sanity tests on fonts on checkbuild checkbuild: fontchain_lint diff --git a/native/android/Android.bp b/native/android/Android.bp index 9f52d856ea75..8f36decec400 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -93,14 +93,6 @@ cc_library_shared { }, } -// Header-only library used for atrace in platform NDK builds -cc_library_headers { - name: "libandroid_trace", - host_supported: true, - vendor_available: true, - export_include_dirs: ["trace"], -} - // Network library. cc_library_shared { name: "libandroid_net", diff --git a/native/android/trace/android/trace.h b/native/android/trace/android/trace.h deleted file mode 100644 index 4e569e566bc2..000000000000 --- a/native/android/trace/android/trace.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2020 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_ATRACE_HEADERS_H -#define ANDROID_ATRACE_HEADERS_H - -#include <cutils/trace.h> - -inline void ATrace_beginSection(const char* sectionName) { - atrace_begin(ATRACE_TAG_APP, sectionName); -} - -inline void ATrace_endSection() { - atrace_end(ATRACE_TAG_APP); -} - -#endif // ANDROID_ATRACE_HEADERS_H diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 91a6e29a05da..48a600dfe6e1 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -132,7 +132,7 @@ public class TetheringConfiguration { isDunRequired = checkDunRequired(ctx); chooseUpstreamAutomatically = getResourceBoolean( - res, R.bool.config_tether_upstream_automatic, false /** default value */); + res, R.bool.config_tether_upstream_automatic, false /** defaultValue */); preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); legacyDhcpRanges = getLegacyDhcpRanges(res); @@ -375,30 +375,31 @@ public class TetheringConfiguration { // Priority 1: Device config // Priority 2: Resource config // Priority 3: Default value - final boolean resourceValue = getResourceBoolean( + final boolean defaultValue = getResourceBoolean( res, R.bool.config_tether_enable_bpf_offload, true /** default value */); - // Due to the limitation of static mock for testing, using #getProperty directly instead - // of getDeviceConfigBoolean. getDeviceConfigBoolean is not invoked because it uses - // #getBoolean to get the boolean device config. The test can't know that the returned - // boolean value comes from device config or default value (because of null property - // string). Because the test would like to verify null property boolean string case, - // use DeviceConfig.getProperty here. See also the test case testBpfOffload{*} in - // TetheringConfigurationTest.java. - final String value = DeviceConfig.getProperty( - NAMESPACE_CONNECTIVITY, OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD); - return (value != null) ? Boolean.parseBoolean(value) : resourceValue; + return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue); } private boolean getEnableLegacyDhcpServer(final Resources res) { return getResourceBoolean( - res, R.bool.config_tether_enable_legacy_dhcp_server, false /** default value */) - || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER); + res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */) + || getDeviceConfigBoolean( + TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */); + } + + private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) { + // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead + // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the + // returned boolean value comes from device config or default value (because of null + // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java. + final String value = getDeviceConfigProperty(name); + return value != null ? Boolean.parseBoolean(value) : defaultValue; } @VisibleForTesting - protected boolean getDeviceConfigBoolean(final String name) { - return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, false /** defaultValue */); + protected String getDeviceConfigProperty(String name) { + return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name); } private Resources getResources(Context ctx, int subId) { diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index cdd0e243e31f..72fa916b9e42 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -147,9 +147,8 @@ public final class EntitlementManagerTest { doReturn(false).when( () -> SystemProperties.getBoolean( eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean())); - doReturn(false).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn(null).when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString())); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index fbfa871f76c6..1999ad786ed4 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -30,7 +30,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -109,9 +108,9 @@ public class TetheringConfigurationTest { .mockStatic(DeviceConfig.class) .strictness(Strictness.WARN) .startMocking(); - doReturn(false).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn(null).when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn( new String[0]); @@ -328,9 +327,9 @@ public class TetheringConfigurationTest { public void testNewDhcpServerDisabled() { when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( true); - doReturn(false).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn("false").when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); final TetheringConfiguration enableByRes = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -338,9 +337,9 @@ public class TetheringConfigurationTest { when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); - doReturn(true).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn("true").when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); final TetheringConfiguration enableByDevConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -351,9 +350,9 @@ public class TetheringConfigurationTest { public void testNewDhcpServerEnabled() { when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); - doReturn(false).when( - () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), - eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); + doReturn("false").when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index b665acc2718d..7734a3c61eaf 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -312,8 +312,8 @@ public class TetheringTest { } @Override - protected boolean getDeviceConfigBoolean(final String name) { - return false; + protected String getDeviceConfigProperty(final String name) { + return null; } @Override diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 0114b5bb1331..730da28ba8a2 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -65,6 +65,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkProvider; +import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.UidRange; import android.net.VpnManager; @@ -2225,12 +2226,27 @@ public class Vpn { @Override public void run() { - // Explicitly use only the network that ConnectivityService thinks is the "best." In - // other words, only ever use the currently selected default network. This does mean - // that in both onLost() and onConnected(), any old sessions MUST be torn down. This - // does NOT include VPNs. + // Unless the profile is restricted to test networks, explicitly use only the network + // that ConnectivityService thinks is the "best." In other words, only ever use the + // currently selected default network. This does mean that in both onLost() and + // onConnected(), any old sessions MUST be torn down. This does NOT include VPNs. + // + // When restricted to test networks, select any network with TRANSPORT_TEST. Since the + // creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS, + // this is considered safe. final ConnectivityManager cm = ConnectivityManager.from(mContext); - cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback); + final NetworkRequest req; + + if (mProfile.isRestrictedToTestNetworks()) { + req = new NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_TEST) + .build(); + } else { + req = cm.getDefaultRequest(); + } + + cm.requestNetwork(req, mNetworkCallback); } private boolean isActiveNetwork(@Nullable Network network) { @@ -2868,6 +2884,11 @@ public class Vpn { verifyCallingUidAndPackage(packageName); enforceNotRestrictedUser(); + if (profile.isRestrictedToTestNetworks) { + mContext.enforceCallingPermission(Manifest.permission.MANAGE_TEST_NETWORKS, + "Test-mode profiles require the MANAGE_TEST_NETWORKS permission"); + } + final byte[] encodedProfile = profile.encode(); if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) { throw new IllegalArgumentException("Profile too big"); diff --git a/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java new file mode 100644 index 000000000000..6c6375586225 --- /dev/null +++ b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.provider.Telephony; +import android.text.TextUtils; +import android.util.Log; + +/** + * This class provides utility functions related to CellBroadcast. + */ +public class CellBroadcastUtils { + private static final String TAG = "CellBroadcastUtils"; + private static final boolean VDBG = false; + + /** + * Utility method to query the default CBR's package name. + */ + public static String getDefaultCellBroadcastReceiverPackageName(Context context) { + PackageManager packageManager = context.getPackageManager(); + ResolveInfo resolveInfo = packageManager.resolveActivity( + new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION), + PackageManager.MATCH_SYSTEM_ONLY); + String packageName; + + if (resolveInfo == null) { + Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: no package found"); + return null; + } + + packageName = resolveInfo.activityInfo.applicationInfo.packageName; + + if (VDBG) { + Log.d(TAG, "getDefaultCellBroadcastReceiverPackageName: found package: " + packageName); + } + + if (TextUtils.isEmpty(packageName) || packageManager.checkPermission( + android.Manifest.permission.READ_CELL_BROADCASTS, packageName) + == PackageManager.PERMISSION_DENIED) { + Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: returning null; " + + "permission check failed for : " + packageName); + return null; + } + + return packageName; + } +} diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java index ceca6f028866..e5daa71c30ea 100644 --- a/tests/net/java/com/android/internal/net/VpnProfileTest.java +++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java @@ -33,7 +33,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** Unit tests for {@link VpnProfile}. */ @SmallTest @@ -41,6 +43,9 @@ import java.util.Arrays; public class VpnProfileTest { private static final String DUMMY_PROFILE_KEY = "Test"; + private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23; + private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24; + @Test public void testDefaults() throws Exception { final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY); @@ -67,10 +72,11 @@ public class VpnProfileTest { assertFalse(p.isMetered); assertEquals(1360, p.maxMtu); assertFalse(p.areAuthParamsInline); + assertFalse(p.isRestrictedToTestNetworks); } private VpnProfile getSampleIkev2Profile(String key) { - final VpnProfile p = new VpnProfile(key); + final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */); p.name = "foo"; p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; @@ -116,7 +122,7 @@ public class VpnProfileTest { @Test public void testParcelUnparcel() { - assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22); + assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23); } @Test @@ -159,14 +165,41 @@ public class VpnProfileTest { assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues)); } + private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) { + // Sort to ensure when we remove, we can do it from greatest first. + Arrays.sort(missingIndices); + + final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode()); + final List<String> parts = + new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER))); + + // Remove from back first to ensure indexing is consistent. + for (int i = missingIndices.length - 1; i >= 0; i--) { + parts.remove(missingIndices[i]); + } + + return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0])); + } + @Test public void testEncodeDecodeInvalidNumberOfValues() { - final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); - final String encoded = new String(profile.encode()); - final byte[] tooFewValues = - encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes(); + final String tooFewValues = + getEncodedDecodedIkev2ProfileMissingValues( + ENCODED_INDEX_AUTH_PARAMS_INLINE, + ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */); - assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues)); + assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes())); + } + + @Test + public void testEncodeDecodeMissingIsRestrictedToTestNetworks() { + final String tooFewValues = + getEncodedDecodedIkev2ProfileMissingValues( + ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */); + + // Verify decoding without isRestrictedToTestNetworks defaults to false + final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()); + assertFalse(decoded.isRestrictedToTestNetworks); } @Test diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 5e2ef0476ce8..e66e26452602 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -5494,6 +5494,8 @@ public class ConnectivityServiceTest { assertFalse(nc.hasTransport(TRANSPORT_WIFI)); // For safety reasons a VPN without underlying networks is considered metered. assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); + // A VPN without underlying networks is not suspended. + assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); // Connect cell and use it as an underlying network. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); |