diff options
23 files changed, 782 insertions, 219 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index c9725710daf8..353cf3aea82d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1442,6 +1442,7 @@ package android.net { public class EthernetManager { method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback); + method public void setIncludeTestInterfaces(boolean); } public static interface EthernetManager.TetheredInterfaceCallback { diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java index 83b5f63576f2..d975017f9c8e 100644 --- a/core/java/android/net/EthernetManager.java +++ b/core/java/android/net/EthernetManager.java @@ -200,6 +200,21 @@ public class EthernetManager { } /** + * Whether to treat interfaces created by {@link TestNetworkManager#createTapInterface} + * as Ethernet interfaces. The effects of this method apply to any test interfaces that are + * already present on the system. + * @hide + */ + @TestApi + public void setIncludeTestInterfaces(boolean include) { + try { + mService.setIncludeTestInterfaces(include); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * A request for a tethered interface. */ public static class TetheredInterfaceRequest { diff --git a/core/java/android/net/IEthernetManager.aidl b/core/java/android/net/IEthernetManager.aidl index ccc6e352098f..e058e5a70c71 100644 --- a/core/java/android/net/IEthernetManager.aidl +++ b/core/java/android/net/IEthernetManager.aidl @@ -33,6 +33,7 @@ interface IEthernetManager boolean isAvailable(String iface); void addListener(in IEthernetServiceListener listener); void removeListener(in IEthernetServiceListener listener); + void setIncludeTestInterfaces(boolean include); void requestTetheredInterface(in ITetheredInterfaceCallback callback); void releaseTetheredInterface(in ITetheredInterfaceCallback callback); } diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index febc730480e4..4edbcd0ac8b8 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -25,6 +25,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.service.NetworkIdentityProto; +import android.telephony.Annotation.NetworkType; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -39,16 +40,6 @@ import java.util.Objects; public class NetworkIdentity implements Comparable<NetworkIdentity> { private static final String TAG = "NetworkIdentity"; - /** - * When enabled, combine all {@link #mSubType} together under - * {@link #SUBTYPE_COMBINED}. - * - * @deprecated we no longer offer to collect statistics on a per-subtype - * basis; this is always disabled. - */ - @Deprecated - public static final boolean COMBINE_SUBTYPE_ENABLED = true; - public static final int SUBTYPE_COMBINED = -1; final int mType; @@ -63,7 +54,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { int type, int subType, String subscriberId, String networkId, boolean roaming, boolean metered, boolean defaultNetwork) { mType = type; - mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType; + mSubType = subType; mSubscriberId = subscriberId; mNetworkId = networkId; mRoaming = roaming; @@ -95,7 +86,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { final StringBuilder builder = new StringBuilder("{"); builder.append("type=").append(getNetworkTypeName(mType)); builder.append(", subType="); - if (COMBINE_SUBTYPE_ENABLED) { + if (mSubType == SUBTYPE_COMBINED) { builder.append("COMBINED"); } else { builder.append(mSubType); @@ -187,13 +178,14 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } /** - * Build a {@link NetworkIdentity} from the given {@link NetworkState}, - * assuming that any mobile networks are using the current IMSI. + * Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType}, + * assuming that any mobile networks are using the current IMSI. The subType if applicable, + * should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or + * {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not. */ public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state, - boolean defaultNetwork) { + boolean defaultNetwork, @NetworkType int subType) { final int type = state.networkInfo.getType(); - final int subType = state.networkInfo.getSubtype(); String subscriberId = null; String networkId = null; diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 5498f74ba2cc..cb9463a59d34 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -34,9 +34,13 @@ import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.ROAMING_YES; import static android.net.wifi.WifiInfo.sanitizeSsid; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Annotation.NetworkType; +import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.BackupUtils; import android.util.Log; @@ -73,6 +77,14 @@ public class NetworkTemplate implements Parcelable { public static final int MATCH_BLUETOOTH = 8; public static final int MATCH_PROXY = 9; + /** + * Include all network types when filtering. This is meant to merge in with the + * {@code TelephonyManager.NETWORK_TYPE_*} constants, and thus needs to stay in sync. + * + * @hide + */ + public static final int NETWORK_TYPE_ALL = -1; + private static boolean isKnownMatchRule(final int rule) { switch (rule) { case MATCH_MOBILE: @@ -117,7 +129,22 @@ public class NetworkTemplate implements Parcelable { } /** - * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks, + * Template to match cellular networks with the given IMSI and {@code ratType}. + * Use {@link #NETWORK_TYPE_ALL} to include all network types when filtering. + * See {@code TelephonyManager.NETWORK_TYPE_*}. + */ + public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId, + @NetworkType int ratType) { + if (TextUtils.isEmpty(subscriberId)) { + return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType); + } + return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType); + } + + /** + * Template to match metered {@link ConnectivityManager#TYPE_MOBILE} networks, * regardless of IMSI. */ @UnsupportedAppUsage @@ -126,7 +153,7 @@ public class NetworkTemplate implements Parcelable { } /** - * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks, + * Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks, * regardless of SSID. */ @UnsupportedAppUsage @@ -192,6 +219,7 @@ public class NetworkTemplate implements Parcelable { private final int mMetered; private final int mRoaming; private final int mDefaultNetwork; + private final int mSubType; @UnsupportedAppUsage public NetworkTemplate(int matchRule, String subscriberId, String networkId) { @@ -201,11 +229,11 @@ public class NetworkTemplate implements Parcelable { public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, String networkId) { this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL); + DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL); } public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, - String networkId, int metered, int roaming, int defaultNetwork) { + String networkId, int metered, int roaming, int defaultNetwork, int subType) { mMatchRule = matchRule; mSubscriberId = subscriberId; mMatchSubscriberIds = matchSubscriberIds; @@ -213,6 +241,7 @@ public class NetworkTemplate implements Parcelable { mMetered = metered; mRoaming = roaming; mDefaultNetwork = defaultNetwork; + mSubType = subType; if (!isKnownMatchRule(matchRule)) { Log.e(TAG, "Unknown network template rule " + matchRule @@ -228,6 +257,7 @@ public class NetworkTemplate implements Parcelable { mMetered = in.readInt(); mRoaming = in.readInt(); mDefaultNetwork = in.readInt(); + mSubType = in.readInt(); } @Override @@ -239,6 +269,7 @@ public class NetworkTemplate implements Parcelable { dest.writeInt(mMetered); dest.writeInt(mRoaming); dest.writeInt(mDefaultNetwork); + dest.writeInt(mSubType); } @Override @@ -271,13 +302,16 @@ public class NetworkTemplate implements Parcelable { builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString( mDefaultNetwork)); } + if (mSubType != NETWORK_TYPE_ALL) { + builder.append(", subType=").append(mSubType); + } return builder.toString(); } @Override public int hashCode() { return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming, - mDefaultNetwork); + mDefaultNetwork, mSubType); } @Override @@ -289,7 +323,8 @@ public class NetworkTemplate implements Parcelable { && Objects.equals(mNetworkId, other.mNetworkId) && mMetered == other.mMetered && mRoaming == other.mRoaming - && mDefaultNetwork == other.mDefaultNetwork; + && mDefaultNetwork == other.mDefaultNetwork + && mSubType == other.mSubType; } return false; } @@ -376,6 +411,11 @@ public class NetworkTemplate implements Parcelable { || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork); } + private boolean matchesCollapsedRatType(NetworkIdentity ident) { + return mSubType == NETWORK_TYPE_ALL + || getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType); + } + public boolean matchesSubscriberId(String subscriberId) { return ArrayUtils.contains(mMatchSubscriberIds, subscriberId); } @@ -388,9 +428,52 @@ public class NetworkTemplate implements Parcelable { // TODO: consider matching against WiMAX subscriber identity return true; } else { + // Only metered mobile network would be matched regardless of metered filter. + // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650. + // TODO: Respect metered filter and remove mMetered condition. return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) && !ArrayUtils.isEmpty(mMatchSubscriberIds) - && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); + && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId) + && matchesCollapsedRatType(ident); + } + } + + /** + * Get a Radio Access Technology(RAT) type that is representative of a group of RAT types. + * The mapping is corresponding to {@code TelephonyManager#NETWORK_CLASS_BIT_MASK_*}. + * + * @param ratType An integer defined in {@code TelephonyManager#NETWORK_TYPE_*}. + */ + // TODO: 1. Consider move this to TelephonyManager if used by other modules. + // 2. Consider make this configurable. + // 3. Use TelephonyManager APIs when available. + public static int getCollapsedRatType(int ratType) { + switch (ratType) { + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_GSM: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_IDEN: + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_1xRTT: + return TelephonyManager.NETWORK_TYPE_GSM; + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + return TelephonyManager.NETWORK_TYPE_UMTS; + case TelephonyManager.NETWORK_TYPE_LTE: + case TelephonyManager.NETWORK_TYPE_IWLAN: + return TelephonyManager.NETWORK_TYPE_LTE; + case TelephonyManager.NETWORK_TYPE_NR: + return TelephonyManager.NETWORK_TYPE_NR; + default: + return TelephonyManager.NETWORK_TYPE_UNKNOWN; } } @@ -421,7 +504,8 @@ public class NetworkTemplate implements Parcelable { if (ident.mType == TYPE_WIMAX) { return true; } else { - return sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered); + return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) + && matchesCollapsedRatType(ident); } } diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java index c3284df39713..a0a563b37025 100644 --- a/core/java/android/net/TestNetworkManager.java +++ b/core/java/android/net/TestNetworkManager.java @@ -30,6 +30,18 @@ import com.android.internal.util.Preconditions; */ @TestApi public class TestNetworkManager { + /** + * Prefix for tun interfaces created by this class. + * @hide + */ + public static final String TEST_TUN_PREFIX = "testtun"; + + /** + * Prefix for tap interfaces created by this class. + * @hide + */ + public static final String TEST_TAP_PREFIX = "testtap"; + @NonNull private static final String TAG = TestNetworkManager.class.getSimpleName(); @NonNull private final ITestNetworkManager mService; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d6869c549dcf..1701a349da0f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10197,6 +10197,8 @@ public final class Settings { public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled"; /** {@hide} */ public static final String NETSTATS_AUGMENT_ENABLED = "netstats_augment_enabled"; + /** {@hide} */ + public static final String NETSTATS_COMBINE_SUBTYPE_ENABLED = "netstats_combine_subtype_enabled"; /** {@hide} */ public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration"; diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 7d752cdcd2d8..802b106a107c 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -346,6 +346,7 @@ public class SettingsBackupTest { Settings.Global.NETSTATS_POLL_INTERVAL, Settings.Global.NETSTATS_SAMPLE_ENABLED, Settings.Global.NETSTATS_AUGMENT_ENABLED, + Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED, Settings.Global.NETSTATS_TIME_CACHE_MAX_AGE, Settings.Global.NETSTATS_UID_BUCKET_DURATION, Settings.Global.NETSTATS_UID_DELETE_AGE, diff --git a/packages/Tethering/res/values-mcc310-mnc120/strings.xml b/packages/Tethering/res/values-mcc310-mnc120/strings.xml new file mode 100644 index 000000000000..618df90c7105 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc120/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- String for tethered notification title with client number info. --> + <plurals name="tethered_notification_title_with_client_number"> + <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item> + <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item> + </plurals> +</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values-mcc311-mnc490/strings.xml b/packages/Tethering/res/values-mcc311-mnc490/strings.xml new file mode 100644 index 000000000000..618df90c7105 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc490/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- String for tethered notification title with client number info. --> + <plurals name="tethered_notification_title_with_client_number"> + <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item> + <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item> + </plurals> +</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values-mcc312-mnc530/strings.xml b/packages/Tethering/res/values-mcc312-mnc530/strings.xml new file mode 100644 index 000000000000..618df90c7105 --- /dev/null +++ b/packages/Tethering/res/values-mcc312-mnc530/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- String for tethered notification title with client number info. --> + <plurals name="tethered_notification_title_with_client_number"> + <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item> + <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item> + </plurals> +</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml index d50884b2a7b0..981524e4daea 100644 --- a/packages/Tethering/res/values/strings.xml +++ b/packages/Tethering/res/values/strings.xml @@ -13,12 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Shown when the device is tethered --> <!-- String for tethered notification title [CHAR LIMIT=200] --> <string name="tethered_notification_title">Tethering or hotspot active</string> <!-- String for tethered notification message [CHAR LIMIT=200] --> <string name="tethered_notification_message">Tap to set up.</string> + <!-- String for tethered notification title with client number info. --> + <plurals name="tethered_notification_title_with_client_number"> + </plurals> <!-- This notification is shown when tethering has been disabled on a user's device. The device is managed by the user's employer. Tethering can't be turned on unless the diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 76e0c137d2f5..8099f8f37bc7 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -1,5 +1,5 @@ # Connectivity / Networking -per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com +per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, junyulai@google.com, lorenzo@google.com, reminv@google.com, satk@google.com # Vibrator / Threads per-file VibratorService.java, DisplayThread.java = michaelwr@google.com diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index f772a4a5a300..5045361ee0c5 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -16,6 +16,9 @@ package com.android.server; +import static android.net.TestNetworkManager.TEST_TAP_PREFIX; +import static android.net.TestNetworkManager.TEST_TUN_PREFIX; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -61,8 +64,6 @@ import java.util.concurrent.atomic.AtomicInteger; class TestNetworkService extends ITestNetworkManager.Stub { @NonNull private static final String TAG = TestNetworkService.class.getSimpleName(); @NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK"; - @NonNull private static final String TEST_TUN_PREFIX = "testtun"; - @NonNull private static final String TEST_TAP_PREFIX = "testtap"; @NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger(); @NonNull private final Context mContext; diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java index 04c792ae482b..d5488712aa34 100644 --- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java +++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java @@ -25,6 +25,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; +import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -220,7 +221,7 @@ public class MultipathPolicyTracker { mNetworkTemplate = new NetworkTemplate( NetworkTemplate.MATCH_MOBILE, subscriberId, new String[] { subscriberId }, null, NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, - NetworkStats.DEFAULT_NETWORK_NO); + NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL); mUsageCallback = new UsageCallback() { @Override public void onThresholdReached(int networkType, String subscriberId) { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index a2a4e91aa9fe..0392807bcb37 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -1869,8 +1869,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNetIdToSubId.put(state.network.netId, parseSubId(state)); } if (state.networkInfo != null && state.networkInfo.isConnected()) { + // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype + // in the object created here is never used and its value doesn't matter, so use + // NETWORK_TYPE_UNKNOWN. final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, - true); + true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */); identified.put(state, ident); } } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 88fc0a6b11f7..26808587437c 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -27,6 +27,7 @@ import static android.content.Intent.EXTRA_UID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.isNetworkTypeMobile; +import static android.net.NetworkIdentity.SUBTYPE_COMBINED; import static android.net.NetworkStack.checkNetworkStackPermission; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.IFACE_ALL; @@ -45,10 +46,12 @@ import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStatsHistory.FIELD_ALL; import static android.net.NetworkTemplate.buildTemplateMobileWildcard; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; +import static android.net.NetworkTemplate.getCollapsedRatType; import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import static android.os.Trace.TRACE_TAG_NETWORK; import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED; +import static android.provider.Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED; import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE; import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES; @@ -64,6 +67,9 @@ import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE; import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES; import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE; +import static android.telephony.PhoneStateListener.LISTEN_NONE; +import static android.telephony.PhoneStateListener.LISTEN_SERVICE_STATE; +import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -109,6 +115,7 @@ import android.os.Binder; import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.IBinder; import android.os.INetworkManagementService; @@ -125,6 +132,8 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.service.NetworkInterfaceProto; import android.service.NetworkStatsServiceDumpProto; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.text.format.DateUtils; @@ -157,6 +166,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -173,6 +183,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static final int MSG_PERFORM_POLL = 1; // Perform polling, persist network, and register the global alert again. private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2; + private static final int MSG_UPDATE_IFACES = 3; /** Flags to control detail level of poll event. */ private static final int FLAG_PERSIST_NETWORK = 0x1; @@ -229,12 +240,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Settings that can be changed externally. */ public interface NetworkStatsSettings { - public long getPollInterval(); - public long getPollDelay(); - public boolean getSampleEnabled(); - public boolean getAugmentEnabled(); + long getPollInterval(); + long getPollDelay(); + boolean getSampleEnabled(); + boolean getAugmentEnabled(); + /** + * When enabled, all mobile data is reported under {@link NetworkIdentity#SUBTYPE_COMBINED}. + * When disabled, mobile data is broken down by a granular subtype representative of the + * actual subtype. {@see NetworkTemplate#getCollapsedRatType}. + * Enabling this decreases the level of detail but saves performance, disk space and + * amount of data logged. + */ + boolean getCombineSubtypeEnabled(); - public static class Config { + class Config { public final long bucketDuration; public final long rotateAgeMillis; public final long deleteAgeMillis; @@ -246,16 +265,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - public Config getDevConfig(); - public Config getXtConfig(); - public Config getUidConfig(); - public Config getUidTagConfig(); + Config getDevConfig(); + Config getXtConfig(); + Config getUidConfig(); + Config getUidTagConfig(); - public long getGlobalAlertBytes(long def); - public long getDevPersistBytes(long def); - public long getXtPersistBytes(long def); - public long getUidPersistBytes(long def); - public long getUidTagPersistBytes(long def); + long getGlobalAlertBytes(long def); + long getDevPersistBytes(long def); + long getXtPersistBytes(long def); + long getUidPersistBytes(long def); + long getUidTagPersistBytes(long def); } private final Object mStatsLock = new Object(); @@ -280,6 +299,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @GuardedBy("mStatsLock") private Network[] mDefaultNetworks = new Network[0]; + /** Last states of all networks sent from ConnectivityService. */ + @GuardedBy("mStatsLock") + @Nullable + private NetworkState[] mLastNetworkStates = null; + private final DropBoxNonMonotonicObserver mNonMonotonicObserver = new DropBoxNonMonotonicObserver(); @@ -355,6 +379,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { performPoll(FLAG_PERSIST_ALL); break; } + case MSG_UPDATE_IFACES: { + // If no cached states, ignore. + if (mLastNetworkStates == null) break; + updateIfaces(mDefaultNetworks, mLastNetworkStates, mActiveIface); + break; + } case MSG_PERFORM_POLL_REGISTER_ALERT: { performPoll(FLAG_PERSIST_NETWORK); registerGlobalAlert(); @@ -407,6 +437,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final HandlerThread handlerThread = mDeps.makeHandlerThread(); handlerThread.start(); mHandler = new NetworkStatsHandler(handlerThread.getLooper()); + mPhoneListener = new NetworkTypeListener(new HandlerExecutor(mHandler)); } /** @@ -486,6 +517,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime, mSettings.getPollInterval(), pollIntent); + // TODO: 1. listen to changes from all subscriptions. + // 2. listen to settings changed to support dynamically enable/disable. + // watch for networkType changes + if (!mSettings.getCombineSubtypeEnabled()) { + mTeleManager.listen(mPhoneListener, LISTEN_SERVICE_STATE); + } + registerGlobalAlert(); } @@ -506,6 +544,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext.unregisterReceiver(mUserReceiver); mContext.unregisterReceiver(mShutdownReceiver); + mTeleManager.listen(mPhoneListener, LISTEN_NONE); + final long currentTime = mClock.millis(); // persist any pending stats @@ -1156,6 +1196,38 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } }; + /** + * Receiver that watches for {@link TelephonyManager} changes, such as + * transitioning between Radio Access Technology(RAT) types. + */ + @NonNull + private final NetworkTypeListener mPhoneListener; + + class NetworkTypeListener extends PhoneStateListener { + private volatile int mLastCollapsedRatType = NETWORK_TYPE_UNKNOWN; + + NetworkTypeListener(@NonNull Executor executor) { + super(executor); + } + + @Override + public void onServiceStateChanged(@NonNull ServiceState ss) { + final int networkType = ss.getDataNetworkType(); + final int collapsedRatType = getCollapsedRatType(networkType); + if (collapsedRatType == mLastCollapsedRatType) return; + + if (LOGD) { + Log.d(TAG, "subtype changed for mobile: " + + mLastCollapsedRatType + " -> " + collapsedRatType); + } + // Protect service from frequently updating. Remove pending messages if any. + mHandler.removeMessages(MSG_UPDATE_IFACES); + mLastCollapsedRatType = collapsedRatType; + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_UPDATE_IFACES), mSettings.getPollDelay()); + } + } + private void updateIfaces( Network[] defaultNetworks, NetworkState[] networkStates, @@ -1177,7 +1249,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * they are combined under a single {@link NetworkIdentitySet}. */ @GuardedBy("mStatsLock") - private void updateIfacesLocked(Network[] defaultNetworks, NetworkState[] states) { + private void updateIfacesLocked(@Nullable Network[] defaultNetworks, + @NonNull NetworkState[] states) { if (!mSystemReady) return; if (LOGV) Slog.v(TAG, "updateIfacesLocked()"); @@ -1197,13 +1270,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mDefaultNetworks = defaultNetworks; } + mLastNetworkStates = states; + + final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled(); final ArraySet<String> mobileIfaces = new ArraySet<>(); for (NetworkState state : states) { if (state.networkInfo.isConnected()) { final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType()); final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network); + final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED + : getSubTypeForState(state); final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, - isDefault); + isDefault, subType); // Traffic occurring on the base interface is always counted for // both total usage and UID details. @@ -1264,6 +1342,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]); } + /** + * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through + * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different + * transport types do not actually fill this value. + */ + private int getSubTypeForState(@NonNull NetworkState state) { + if (!state.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return 0; + } + + // TODO: return different subType for different subscriptions. + return mPhoneListener.mLastCollapsedRatType; + } + private static <K> NetworkIdentitySet findOrCreateNetworkIdentitySet( ArrayMap<K, NetworkIdentitySet> map, K key) { NetworkIdentitySet ident = map.get(key); @@ -1617,6 +1709,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return; } + pw.println("Configs:"); + pw.increaseIndent(); + pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled()); + pw.println(); + pw.decreaseIndent(); + pw.println("Active interfaces:"); pw.increaseIndent(); for (int i = 0; i < mActiveIfaces.size(); i++) { @@ -2045,6 +2143,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true); } @Override + public boolean getCombineSubtypeEnabled() { + return getGlobalBoolean(NETSTATS_COMBINE_SUBTYPE_ENABLED, false); + } + @Override public Config getDevConfig() { return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS), getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS), diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 1a55521cfa8b..15e2fd579cc4 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -114,18 +114,17 @@ public class StagingManager { * Validates the signature used to sign the container of the new apex package * * @param newApexPkg The new apex package that is being installed - * @param installFlags flags related to the session * @throws PackageManagerException */ - private void validateApexSignature(PackageInfo newApexPkg, int installFlags) + private void validateApexSignature(PackageInfo newApexPkg) throws PackageManagerException { // Get signing details of the new package final String apexPath = newApexPkg.applicationInfo.sourceDir; final String packageName = newApexPkg.packageName; - final SigningDetails signingDetails; + final SigningDetails newSigningDetails; try { - signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR); + newSigningDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "Failed to parse APEX package " + apexPath, e); @@ -150,16 +149,10 @@ public class StagingManager { } // Verify signing details for upgrade - if (signingDetails.checkCapability(existingSigningDetails, - PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) { - return; - } - - // Verify signing details for downgrade - // Allow downgrading from B to A iff it is possible to upgrade from A to B - if (existingApexPkg.getLongVersionCode() > newApexPkg.getLongVersionCode() - && existingSigningDetails.checkCapability(signingDetails, - PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) { + if (newSigningDetails.checkCapability(existingSigningDetails, + SigningDetails.CertCapabilities.INSTALLED_DATA) + || existingSigningDetails.checkCapability(newSigningDetails, + SigningDetails.CertCapabilities.ROLLBACK)) { return; } @@ -319,7 +312,7 @@ public class StagingManager { // The APEX part of the session is activated, proceed with the installation of APKs. try { Slog.d(TAG, "Installing APK packages in session " + session.sessionId); - installApksInSession(session, /* preReboot */ false); + installApksInSession(session); } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); @@ -410,72 +403,23 @@ public class StagingManager { } } - private void commitApkSession(@NonNull PackageInstallerSession apkSession, - PackageInstallerSession originalSession, boolean preReboot) - throws PackageManagerException { - final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED - : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; - if (preReboot) { - final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( - (Intent result) -> { - int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - final String errorMessage = result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE); - Slog.e(TAG, "Failure to install APK staged session " - + originalSession.sessionId + " [" + errorMessage + "]"); - originalSession.setStagedSessionFailed(errorCode, errorMessage); - return; - } - mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( - originalSession.sessionId); - }); - apkSession.commit(receiver.getIntentSender(), false); - return; - } - - if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { - // If rollback is available for this session, notify the rollback - // manager of the apk session so it can properly enable rollback. - final IRollbackManager rm = IRollbackManager.Stub.asInterface( - ServiceManager.getService(Context.ROLLBACK_SERVICE)); - try { - rm.notifyStagedApkSession(originalSession.sessionId, apkSession.sessionId); - } catch (RemoteException re) { - Slog.e(TAG, "Failed to notifyStagedApkSession for session: " - + originalSession.sessionId, re); - } - } - - final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync(); - apkSession.commit(receiver.getIntentSender(), false); - final Intent result = receiver.getResult(); - final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - final String errorMessage = result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE); - Slog.e(TAG, "Failure to install APK staged session " - + originalSession.sessionId + " [" + errorMessage + "]"); - throw new PackageManagerException(errorCode, errorMessage); - } - } - - private void installApksInSession(@NonNull PackageInstallerSession session, - boolean preReboot) throws PackageManagerException { + /** + * Extract apks in the given session into a new session. Returns {@code null} if there is no + * apks in the given session. Only parent session is returned for multi-package session. + */ + @Nullable + private PackageInstallerSession extractApksInSession(PackageInstallerSession session, + boolean preReboot) throws PackageManagerException { final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; if (!session.isMultiPackage() && !isApexSession(session)) { - // APK single-packaged staged session. Do a regular install. - PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot); - commitApkSession(apkSession, session, preReboot); + return createAndWriteApkSession(session, preReboot); } else if (session.isMultiPackage()) { // For multi-package staged sessions containing APKs, we identify which child sessions // contain an APK, and with those then create a new multi-package group of sessions, // carrying over all the session parameters and unmarking them as staged. On commit the // sessions will be installed atomically. - List<PackageInstallerSession> childSessions; + final List<PackageInstallerSession> childSessions; synchronized (mStagedSessions) { childSessions = Arrays.stream(session.getChildSessionIds()) @@ -487,18 +431,18 @@ public class StagingManager { } if (childSessions.isEmpty()) { // APEX-only multi-package staged session, nothing to do. - return; + return null; } - PackageInstaller.SessionParams params = session.params.copy(); + final PackageInstaller.SessionParams params = session.params.copy(); params.isStaged = false; if (preReboot) { params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK; } // TODO(b/129744602): use the userid from the original session. - int apkParentSessionId = mPi.createSession( + final int apkParentSessionId = mPi.createSession( params, session.getInstallerPackageName(), 0 /* UserHandle.SYSTEM */); - PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId); + final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId); try { apkParentSession.open(); } catch (IOException e) { @@ -519,9 +463,75 @@ public class StagingManager { "Failed to add a child session " + apkChildSession.sessionId); } } - commitApkSession(apkParentSession, session, preReboot); + return apkParentSession; + } + return null; + } + + private void verifyApksInSession(PackageInstallerSession session) + throws PackageManagerException { + + final PackageInstallerSession apksToVerify = extractApksInSession( + session, /* preReboot */ true); + if (apksToVerify == null) { + return; + } + + final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( + (Intent result) -> { + int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + final String errorMessage = result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE); + Slog.e(TAG, "Failure to verify APK staged session " + + session.sessionId + " [" + errorMessage + "]"); + session.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage); + return; + } + mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( + session.sessionId); + }); + + apksToVerify.commit(receiver.getIntentSender(), false); + } + + private void installApksInSession(@NonNull PackageInstallerSession session) + throws PackageManagerException { + + final PackageInstallerSession apksToInstall = extractApksInSession( + session, /* preReboot */ false); + if (apksToInstall == null) { + return; + } + + if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { + // If rollback is available for this session, notify the rollback + // manager of the apk session so it can properly enable rollback. + final IRollbackManager rm = IRollbackManager.Stub.asInterface( + ServiceManager.getService(Context.ROLLBACK_SERVICE)); + try { + rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId); + } catch (RemoteException re) { + Slog.e(TAG, "Failed to notifyStagedApkSession for session: " + + session.sessionId, re); + } + } + + final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync(); + apksToInstall.commit(receiver.getIntentSender(), false); + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + final String errorMessage = result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE); + Slog.e(TAG, "Failure to install APK staged session " + + session.sessionId + " [" + errorMessage + "]"); + throw new PackageManagerException( + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage); } - // APEX single-package staged session, nothing to do. } void commitSession(@NonNull PackageInstallerSession session) { @@ -808,8 +818,7 @@ public class StagingManager { final List<PackageInfo> apexPackages = submitSessionToApexService(session); for (PackageInfo apexPackage : apexPackages) { - validateApexSignature( - apexPackage, session.params.installFlags); + validateApexSignature(apexPackage); } } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); @@ -836,8 +845,8 @@ public class StagingManager { Slog.d(TAG, "Running a pre-reboot verification for APKs in session " + session.sessionId + " by performing a dry-run install"); - // installApksInSession will notify the handler when APK verification is complete - installApksInSession(session, /* preReboot */ true); + // verifyApksInSession will notify the handler when APK verification is complete + verifyApksInSession(session); // TODO(b/118865310): abort the session on apexd. } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index cc33fb0f5008..d318b1a4732c 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -276,18 +276,6 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat return; } - // Special case handling for uninitialized devices. This should only happen once. - String newZoneId = bestTelephonySuggestion.suggestion.getZoneId(); - if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) { - String cause = "Device has no time zone set. Attempting to set the device to the best" - + " available suggestion." - + " bestTelephonySuggestion=" + bestTelephonySuggestion - + ", detectionReason=" + detectionReason; - Slog.i(LOG_TAG, cause); - setDeviceTimeZoneIfRequired(ORIGIN_TELEPHONY, newZoneId, cause); - return; - } - boolean suggestionGoodEnough = bestTelephonySuggestion.score >= TELEPHONY_SCORE_USAGE_THRESHOLD; if (!suggestionGoodEnough) { @@ -301,6 +289,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time // zone ID. + String newZoneId = bestTelephonySuggestion.suggestion.getZoneId(); if (newZoneId == null) { Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:" + " bestTelephonySuggestion=" + bestTelephonySuggestion diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index ba309679e47a..30bb12e3e616 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -128,43 +128,69 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testFirstPlausibleTelephonySuggestionAcceptedWhenTimeZoneUninitialized() { + public void testTelephonySuggestionsWhenTimeZoneUninitialized() { + assertTrue(TELEPHONY_SCORE_LOW < TELEPHONY_SCORE_USAGE_THRESHOLD); + assertTrue(TELEPHONY_SCORE_HIGH >= TELEPHONY_SCORE_USAGE_THRESHOLD); SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW); - TelephonyTimeZoneSuggestion lowQualitySuggestion = - testCase.createSuggestion(SLOT_INDEX1, "America/New_York"); + SuggestionTestCase testCase2 = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, + QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH); - // The device time zone setting is left uninitialized. Script script = new Script() .initializeAutoTimeZoneDetection(true); - // The very first suggestion will be taken. - script.suggestTelephonyTimeZone(lowQualitySuggestion) - .verifyTimeZoneSetAndReset(lowQualitySuggestion); + // A low quality suggestions will not be taken: The device time zone setting is left + // uninitialized. + { + TelephonyTimeZoneSuggestion lowQualitySuggestion = + testCase.createSuggestion(SLOT_INDEX1, "America/New_York"); - // Assert internal service state. - QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion = - new QualifiedTelephonyTimeZoneSuggestion( - lowQualitySuggestion, testCase.expectedScore); - assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); - assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); + script.suggestTelephonyTimeZone(lowQualitySuggestion) + .verifyTimeZoneNotSet(); - // Another low quality suggestion will be ignored now that the setting is initialized. - TelephonyTimeZoneSuggestion lowQualitySuggestion2 = - testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles"); - script.suggestTelephonyTimeZone(lowQualitySuggestion2) - .verifyTimeZoneNotSet(); + // Assert internal service state. + QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion( + lowQualitySuggestion, testCase.expectedScore); + assertEquals(expectedScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); + } - // Assert internal service state. - QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion2 = - new QualifiedTelephonyTimeZoneSuggestion( - lowQualitySuggestion2, testCase.expectedScore); - assertEquals(expectedScoredSuggestion2, - mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); - assertEquals(expectedScoredSuggestion2, - mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); + // A good quality suggestion will be used. + { + TelephonyTimeZoneSuggestion goodQualitySuggestion = + testCase2.createSuggestion(SLOT_INDEX1, "Europe/London"); + script.suggestTelephonyTimeZone(goodQualitySuggestion) + .verifyTimeZoneSetAndReset(goodQualitySuggestion); + + // Assert internal service state. + QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion( + goodQualitySuggestion, testCase2.expectedScore); + assertEquals(expectedScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); + } + + // A low quality suggestion will be accepted, but not used to set the device time zone. + { + TelephonyTimeZoneSuggestion lowQualitySuggestion2 = + testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles"); + script.suggestTelephonyTimeZone(lowQualitySuggestion2) + .verifyTimeZoneNotSet(); + + // Assert internal service state. + QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion( + lowQualitySuggestion2, testCase.expectedScore); + assertEquals(expectedScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); + } } /** diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt new file mode 100644 index 000000000000..5dd0fda4da28 --- /dev/null +++ b/tests/net/java/android/net/NetworkTemplateTest.kt @@ -0,0 +1,155 @@ +/* + * 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 android.net + +import android.content.Context +import android.net.ConnectivityManager.TYPE_MOBILE +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.NetworkIdentity.SUBTYPE_COMBINED +import android.net.NetworkIdentity.buildNetworkIdentity +import android.net.NetworkStats.DEFAULT_NETWORK_ALL +import android.net.NetworkStats.METERED_ALL +import android.net.NetworkStats.ROAMING_ALL +import android.net.NetworkTemplate.MATCH_MOBILE +import android.net.NetworkTemplate.MATCH_WIFI +import android.net.NetworkTemplate.NETWORK_TYPE_ALL +import android.net.NetworkTemplate.buildTemplateMobileWithRatType +import android.telephony.TelephonyManager +import com.android.testutils.assertParcelSane +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations +import kotlin.test.assertFalse +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue + +private const val TEST_IMSI1 = "imsi1" +private const val TEST_IMSI2 = "imsi2" +private const val TEST_SSID1 = "ssid1" + +@RunWith(JUnit4::class) +class NetworkTemplateTest { + private val mockContext = mock(Context::class.java) + + private fun buildMobileNetworkState(subscriberId: String): NetworkState = + buildNetworkState(TYPE_MOBILE, subscriberId = subscriberId) + private fun buildWifiNetworkState(ssid: String): NetworkState = + buildNetworkState(TYPE_WIFI, ssid = ssid) + + private fun buildNetworkState( + type: Int, + subscriberId: String? = null, + ssid: String? = null + ): NetworkState { + val info = mock(NetworkInfo::class.java) + doReturn(type).`when`(info).type + doReturn(NetworkInfo.State.CONNECTED).`when`(info).state + val lp = LinkProperties() + val caps = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) + } + return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid) + } + + private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) = + assertTrue(matches(ident), "$this does not match $ident") + + private fun NetworkTemplate.assertDoesNotMatch(ident: NetworkIdentity) = + assertFalse(matches(ident), "$this should match $ident") + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testRatTypeGroupMatches() { + val stateMobile = buildMobileNetworkState(TEST_IMSI1) + // Build UMTS template that matches mobile identities with RAT in the same + // group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}. + val templateUmts = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS) + // Build normal template that matches mobile identities with any RAT and IMSI. + val templateAll = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL) + // Build template with UNKNOWN RAT that matches mobile identities with RAT that + // cannot be determined. + val templateUnknown = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN) + + val identUmts = buildNetworkIdentity( + mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_UMTS) + val identHsdpa = buildNetworkIdentity( + mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_HSDPA) + val identLte = buildNetworkIdentity( + mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_LTE) + val identCombined = buildNetworkIdentity( + mockContext, stateMobile, false, SUBTYPE_COMBINED) + val identImsi2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2), + false, TelephonyManager.NETWORK_TYPE_UMTS) + val identWifi = buildNetworkIdentity( + mockContext, buildWifiNetworkState(TEST_SSID1), true, 0) + + // Assert that identity with the same RAT matches. + templateUmts.assertMatches(identUmts) + templateAll.assertMatches(identUmts) + templateUnknown.assertDoesNotMatch(identUmts) + // Assert that identity with the RAT within the same group matches. + templateUmts.assertMatches(identHsdpa) + templateAll.assertMatches(identHsdpa) + templateUnknown.assertDoesNotMatch(identHsdpa) + // Assert that identity with the RAT out of the same group only matches template with + // NETWORK_TYPE_ALL. + templateUmts.assertDoesNotMatch(identLte) + templateAll.assertMatches(identLte) + templateUnknown.assertDoesNotMatch(identLte) + // Assert that identity with combined RAT only matches with template with NETWORK_TYPE_ALL + // and NETWORK_TYPE_UNKNOWN. + templateUmts.assertDoesNotMatch(identCombined) + templateAll.assertMatches(identCombined) + templateUnknown.assertMatches(identCombined) + // Assert that identity with different IMSI matches. + templateUmts.assertMatches(identImsi2) + templateAll.assertMatches(identImsi2) + templateUnknown.assertDoesNotMatch(identImsi2) + // Assert that wifi identity does not match. + templateUmts.assertDoesNotMatch(identWifi) + templateAll.assertDoesNotMatch(identWifi) + templateUnknown.assertDoesNotMatch(identWifi) + } + + @Test + fun testParcelUnparcel() { + val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL, + ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE) + val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL, + ROAMING_ALL, DEFAULT_NETWORK_ALL, 0) + assertParcelSane(templateMobile, 8) + assertParcelSane(templateWifi, 8) + } + + // Verify NETWORK_TYPE_ALL does not conflict with TelephonyManager#NETWORK_TYPE_* constants. + @Test + fun testNetworkTypeAll() { + for (ratType in TelephonyManager.getAllNetworkTypes()) { + assertNotEquals(NETWORK_TYPE_ALL, ratType) + } + } +} diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java index 8f90f13ce7ef..551498f2c0cc 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java @@ -319,33 +319,33 @@ public class NetworkStatsCollectionTest { assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); - assertEntry(10747, 50, 16838, 55, history.getValues(i++, null)); - assertEntry(10747, 49, 16838, 54, history.getValues(i++, null)); + assertEntry(10747, 50, 16839, 55, history.getValues(i++, null)); + assertEntry(10747, 49, 16837, 54, history.getValues(i++, null)); assertEntry(89191, 151, 18021, 140, history.getValues(i++, null)); assertEntry(89190, 150, 18020, 139, history.getValues(i++, null)); - assertEntry(3821, 22, 4525, 26, history.getValues(i++, null)); - assertEntry(3820, 22, 4524, 26, history.getValues(i++, null)); - assertEntry(91686, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(8289, 35, 6863, 38, history.getValues(i++, null)); - assertEntry(8289, 35, 6863, 38, history.getValues(i++, null)); + assertEntry(3821, 23, 4525, 26, history.getValues(i++, null)); + assertEntry(3820, 21, 4524, 26, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); + assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); - assertEntry(11378, 49, 9261, 49, history.getValues(i++, null)); - assertEntry(11377, 48, 9261, 49, history.getValues(i++, null)); - assertEntry(201765, 328, 41808, 291, history.getValues(i++, null)); - assertEntry(201765, 328, 41807, 290, history.getValues(i++, null)); - assertEntry(106106, 218, 39917, 201, history.getValues(i++, null)); - assertEntry(106105, 217, 39917, 201, history.getValues(i++, null)); + assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); + assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); + assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); + assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); + assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); + assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); assertEquals(history.size(), i); // Slice from middle should be untouched history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, TIME_B + HOUR_IN_MILLIS); i = 0; - assertEntry(3821, 22, 4525, 26, history.getValues(i++, null)); - assertEntry(3820, 22, 4524, 26, history.getValues(i++, null)); - assertEntry(91686, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18575, 146, history.getValues(i++, null)); + assertEntry(3821, 23, 4525, 26, history.getValues(i++, null)); + assertEntry(3820, 21, 4524, 26, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); assertEquals(history.size(), i); } @@ -373,25 +373,25 @@ public class NetworkStatsCollectionTest { assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); // Cycle point; start data normalization assertEntry(7507, 0, 11763, 0, history.getValues(i++, null)); - assertEntry(7507, 0, 11763, 0, history.getValues(i++, null)); + assertEntry(7507, 0, 11762, 0, history.getValues(i++, null)); assertEntry(62309, 0, 12589, 0, history.getValues(i++, null)); assertEntry(62309, 0, 12588, 0, history.getValues(i++, null)); assertEntry(2669, 0, 3161, 0, history.getValues(i++, null)); assertEntry(2668, 0, 3160, 0, history.getValues(i++, null)); // Anchor point; end data normalization - assertEntry(91686, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(8289, 35, 6863, 38, history.getValues(i++, null)); - assertEntry(8289, 35, 6863, 38, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); + assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); // Cycle point - assertEntry(11378, 49, 9261, 49, history.getValues(i++, null)); - assertEntry(11377, 48, 9261, 49, history.getValues(i++, null)); - assertEntry(201765, 328, 41808, 291, history.getValues(i++, null)); - assertEntry(201765, 328, 41807, 290, history.getValues(i++, null)); - assertEntry(106106, 218, 39917, 201, history.getValues(i++, null)); - assertEntry(106105, 217, 39917, 201, history.getValues(i++, null)); + assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); + assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); + assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); + assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); + assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); + assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); assertEquals(history.size(), i); // Slice from middle should be augmented @@ -399,8 +399,8 @@ public class NetworkStatsCollectionTest { TIME_B + HOUR_IN_MILLIS); i = 0; assertEntry(2669, 0, 3161, 0, history.getValues(i++, null)); assertEntry(2668, 0, 3160, 0, history.getValues(i++, null)); - assertEntry(91686, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18575, 146, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); assertEquals(history.size(), i); } @@ -427,34 +427,34 @@ public class NetworkStatsCollectionTest { assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); // Cycle point; start data normalization - assertEntry(15015, 0, 23526, 0, history.getValues(i++, null)); - assertEntry(15015, 0, 23526, 0, history.getValues(i++, null)); + assertEntry(15015, 0, 23527, 0, history.getValues(i++, null)); + assertEntry(15015, 0, 23524, 0, history.getValues(i++, null)); assertEntry(124619, 0, 25179, 0, history.getValues(i++, null)); assertEntry(124618, 0, 25177, 0, history.getValues(i++, null)); assertEntry(5338, 0, 6322, 0, history.getValues(i++, null)); assertEntry(5337, 0, 6320, 0, history.getValues(i++, null)); // Anchor point; end data normalization - assertEntry(91686, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(8289, 35, 6863, 38, history.getValues(i++, null)); - assertEntry(8289, 35, 6863, 38, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); + assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); // Cycle point - assertEntry(11378, 49, 9261, 49, history.getValues(i++, null)); - assertEntry(11377, 48, 9261, 49, history.getValues(i++, null)); - assertEntry(201765, 328, 41808, 291, history.getValues(i++, null)); - assertEntry(201765, 328, 41807, 290, history.getValues(i++, null)); - assertEntry(106106, 218, 39917, 201, history.getValues(i++, null)); - assertEntry(106105, 217, 39917, 201, history.getValues(i++, null)); + assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); + assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); + assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); + assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); + assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); + assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); // Slice from middle should be augmented history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, TIME_B + HOUR_IN_MILLIS); i = 0; assertEntry(5338, 0, 6322, 0, history.getValues(i++, null)); assertEntry(5337, 0, 6320, 0, history.getValues(i++, null)); - assertEntry(91686, 159, 18575, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18575, 146, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); assertEquals(history.size(), i); } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index b346c923914e..6e6331312eac 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -42,6 +42,7 @@ import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStatsHistory.FIELD_ALL; import static android.net.NetworkTemplate.buildTemplateMobileAll; +import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.UID_REMOVED; @@ -60,11 +61,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.usage.NetworkStatsManager; import android.content.Context; @@ -92,6 +95,8 @@ import android.os.Message; import android.os.Messenger; import android.os.PowerManager; import android.os.SimpleClock; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; import android.telephony.TelephonyManager; import androidx.test.InstrumentationRegistry; @@ -163,11 +168,14 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private @Mock NetworkStatsSettings mSettings; private @Mock IBinder mBinder; private @Mock AlarmManager mAlarmManager; + private @Mock TelephonyManager mTelephonyManager; private HandlerThread mHandlerThread; private NetworkStatsService mService; private INetworkStatsSession mSession; private INetworkManagementEventObserver mNetworkObserver; + @Nullable + private PhoneStateListener mPhoneStateListener; private final Clock mClock = new SimpleClock(ZoneOffset.UTC) { @Override @@ -195,7 +203,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mHandlerThread = new HandlerThread("HandlerThread"); final NetworkStatsService.Dependencies deps = makeDependencies(); mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock, - mClock, mServiceContext.getSystemService(TelephonyManager.class), mSettings, + mClock, mTelephonyManager, mSettings, mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir), deps); mElapsedRealtime = 0L; @@ -216,6 +224,12 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { ArgumentCaptor.forClass(INetworkManagementEventObserver.class); verify(mNetManager).registerObserver(networkObserver.capture()); mNetworkObserver = networkObserver.getValue(); + + // Capture the phone state listener that created by service. + final ArgumentCaptor<PhoneStateListener> phoneStateListenerCaptor = + ArgumentCaptor.forClass(PhoneStateListener.class); + verify(mTelephonyManager).listen(phoneStateListenerCaptor.capture(), anyInt()); + mPhoneStateListener = phoneStateListenerCaptor.getValue(); } @NonNull @@ -534,7 +548,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } @Test - public void testUid3g4gCombinedByTemplate() throws Exception { + public void testUid3gWimaxCombinedByTemplate() throws Exception { // pretend that network comes online expectDefaultSettings(); NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)}; @@ -558,10 +572,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5); - // now switch over to 4g network + // now switch over to wimax network incrementCurrentTime(HOUR_IN_MILLIS); expectDefaultSettings(); - states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)}; + states = new NetworkState[] {buildWimaxState(TEST_IFACE2)}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) @@ -589,6 +603,89 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } @Test + public void testMobileStatsByRatType() throws Exception { + final NetworkTemplate template3g = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS); + final NetworkTemplate template4g = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE); + final NetworkTemplate template5g = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR); + final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)}; + + // 3G network comes online. + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new VpnInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 12L, 18L, 14L, 1L, 0L))); + forcePollAndWaitForIdle(); + + // Verify 3g templates gets stats. + assertUidTotal(sTemplateImsi1, UID_RED, 12L, 18L, 14L, 1L, 0); + assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); + assertUidTotal(template4g, UID_RED, 0L, 0L, 0L, 0L, 0); + assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0); + + // 4G network comes online. + incrementCurrentTime(MINUTE_IN_MILLIS); + setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + // Append more traffic on existing 3g stats entry. + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 16L, 22L, 17L, 2L, 0L)) + // Add entry that is new on 4g. + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, + 33L, 27L, 8L, 10L, 1L))); + forcePollAndWaitForIdle(); + + // Verify ALL_MOBILE template gets all. 3g template counters do not increase. + assertUidTotal(sTemplateImsi1, UID_RED, 49L, 49L, 25L, 12L, 1); + assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); + // Verify 4g template counts appended stats on existing entry and newly created entry. + assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1); + // Verify 5g template doesn't get anything since no traffic is generated on 5g. + assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0); + + // 5g network comes online. + incrementCurrentTime(MINUTE_IN_MILLIS); + setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_NR); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + // Existing stats remains. + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 16L, 22L, 17L, 2L, 0L)) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, + 33L, 27L, 8L, 10L, 1L)) + // Add some traffic on 5g. + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 5L, 13L, 31L, 9L, 2L))); + forcePollAndWaitForIdle(); + + // Verify ALL_MOBILE template gets all. + assertUidTotal(sTemplateImsi1, UID_RED, 54L, 62L, 56L, 21L, 3); + // 3g/4g template counters do not increase. + assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); + assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1); + // Verify 5g template gets the 5g count. + assertUidTotal(template5g, UID_RED, 5L, 13L, 31L, 9L, 2); + } + + // TODO: support per IMSI state + private void setMobileRatTypeAndWaitForIdle(int ratType) { + final ServiceState mockSs = mock(ServiceState.class); + when(mockSs.getDataNetworkType()).thenReturn(ratType); + mPhoneStateListener.onServiceStateChanged(mockSs); + + HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + } + + @Test public void testSummaryForAllUid() throws Exception { // pretend that network comes online expectDefaultSettings(); @@ -1197,6 +1294,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS); when(mSettings.getPollDelay()).thenReturn(0L); when(mSettings.getSampleEnabled()).thenReturn(true); + when(mSettings.getCombineSubtypeEnabled()).thenReturn(false); final Config config = new Config(bucketDuration, deleteAge, deleteAge); when(mSettings.getDevConfig()).thenReturn(config); @@ -1242,6 +1340,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { final NetworkCapabilities capabilities = new NetworkCapabilities(); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID); } @@ -1259,10 +1358,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { final NetworkCapabilities capabilities = new NetworkCapabilities(); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null); } - private static NetworkState buildMobile4gState(String iface) { + private static NetworkState buildWimaxState(@NonNull String iface) { final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null); info.setDetailedState(DetailedState.CONNECTED, null, null); final LinkProperties prop = new LinkProperties(); |