diff options
119 files changed, 2389 insertions, 808 deletions
diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS index f1903a5a54a7..69dfcc98340d 100644 --- a/cmds/idmap2/OWNERS +++ b/cmds/idmap2/OWNERS @@ -1,3 +1,4 @@ set noparent toddke@google.com -rtmitchell@google.com
\ No newline at end of file +rtmitchell@google.com +patb@google.com
\ No newline at end of file diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 848f480c79a1..5d23eb20a81a 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -89,9 +89,9 @@ package android.net { public final class UnderlyingNetworkInfo implements android.os.Parcelable { ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>); method public int describeContents(); - method @NonNull public String getIface(); + method @NonNull public String getInterface(); method public int getOwnerUid(); - method @NonNull public java.util.List<java.lang.String> getUnderlyingIfaces(); + method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR; } diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS index 21c39a8828ad..558280396348 100644 --- a/core/java/android/app/RESOURCES_OWNERS +++ b/core/java/android/app/RESOURCES_OWNERS @@ -1,2 +1,3 @@ rtmitchell@google.com toddke@google.com +patb@google.com diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index 9f1132b605ef..fe99f8532aaa 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -644,7 +644,10 @@ public class NetworkStatsManager { : NetworkTemplate.buildTemplateMobileAll(subscriberId); break; case ConnectivityManager.TYPE_WIFI: - template = NetworkTemplate.buildTemplateWifiWildcard(); + template = subscriberId == null + ? NetworkTemplate.buildTemplateWifiWildcard() + : NetworkTemplate.buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL, + subscriberId); break; default: throw new IllegalArgumentException("Cannot create template for network type " @@ -655,14 +658,14 @@ public class NetworkStatsManager { } /** - * Notify {@code NetworkStatsService} about network status changed. + * Notify {@code NetworkStatsService} about network status changed. * - * Notifies NetworkStatsService of network state changes for data usage accounting purposes. + * Notifies NetworkStatsService of network state changes for data usage accounting purposes. * - * To avoid races that attribute data usage to wrong network, such as new network with - * the same interface after SIM hot-swap, this function will not return until - * {@code NetworkStatsService} finishes its work of retrieving traffic statistics from - * all data sources. + * To avoid races that attribute data usage to wrong network, such as new network with + * the same interface after SIM hot-swap, this function will not return until + * {@code NetworkStatsService} finishes its work of retrieving traffic statistics from + * all data sources. * * @param defaultNetworks the list of all networks that could be used by network traffic that * does not explicitly select a network. @@ -689,8 +692,7 @@ public class NetworkStatsManager { Objects.requireNonNull(defaultNetworks); Objects.requireNonNull(networkStateSnapshots); Objects.requireNonNull(underlyingNetworkInfos); - // TODO: Change internal namings after the name is decided. - mService.forceUpdateIfaces(defaultNetworks.toArray(new Network[0]), + mService.notifyNetworkStatus(defaultNetworks.toArray(new Network[0]), networkStateSnapshots.toArray(new NetworkStateSnapshot[0]), activeIface, underlyingNetworkInfos.toArray(new UnderlyingNetworkInfo[0])); } catch (RemoteException e) { diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java index 14992fb2a4d1..3740ef7d5a92 100644 --- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java +++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java @@ -119,6 +119,7 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> { // how many APKs they're going through. mDeferredErrors.erase(); } + mTargetSdkVersion = null; return this; } diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index dc3b88a7c3be..12937b5cb2c7 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -65,8 +65,8 @@ interface INetworkStatsService { /** Increment data layer count of operations performed for UID and tag. */ void incrementOperationCount(int uid, int tag, int operationCount); - /** Force update of ifaces. */ - void forceUpdateIfaces( + /** Notify {@code NetworkStatsService} about network status changed. */ + void notifyNetworkStatus( in Network[] defaultNetworks, in NetworkStateSnapshot[] snapshots, in String activeIface, diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 3bde6fa6913d..1d07a0330bc5 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -26,8 +26,10 @@ import android.service.NetworkIdentityProto; import android.telephony.Annotation.NetworkType; import android.util.proto.ProtoOutputStream; +import com.android.net.module.util.NetworkCapabilitiesUtils; import com.android.net.module.util.NetworkIdentityUtils; +import java.util.ArrayList; import java.util.Objects; /** @@ -121,11 +123,37 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } builder.append(", metered=").append(mMetered); builder.append(", defaultNetwork=").append(mDefaultNetwork); - // TODO(180557699): Print a human readable string for OEM managed state. - builder.append(", oemManaged=").append(mOemManaged); + builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged)); return builder.append("}").toString(); } + /** + * Get the human readable representation of a bitfield representing the OEM managed state of a + * network. + */ + static String getOemManagedNames(int oemManaged) { + if (oemManaged == OEM_NONE) { + return "OEM_NONE"; + } + final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged); + final ArrayList<String> oemManagedNames = new ArrayList<String>(); + for (int position : bitPositions) { + oemManagedNames.add(nameOfOemManaged(1 << position)); + } + return String.join(",", oemManagedNames); + } + + private static String nameOfOemManaged(int oemManagedBit) { + switch (oemManagedBit) { + case OEM_PAID: + return "OEM_PAID"; + case OEM_PRIVATE: + return "OEM_PRIVATE"; + default: + return "Invalid(" + oemManagedBit + ")"; + } + } + public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index d3c89574944f..352f2e99aa5e 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -274,11 +274,14 @@ public class NetworkTemplate implements Parcelable { } /** - * Template to match all carrier networks with the given IMSI. + * Template to match all metered carrier networks with the given IMSI. */ - public static NetworkTemplate buildTemplateCarrier(@NonNull String subscriberId) { + public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) { Objects.requireNonNull(subscriberId); - return new NetworkTemplate(MATCH_CARRIER, subscriberId, null); + return new NetworkTemplate(MATCH_CARRIER, subscriberId, + new String[] { subscriberId }, null /* networkId */, METERED_YES, ROAMING_ALL, + DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL, + SUBSCRIBER_ID_MATCH_RULE_EXACT); } private final int mMatchRule; @@ -424,7 +427,7 @@ public class NetworkTemplate implements Parcelable { builder.append(", subType=").append(mSubType); } if (mOemManaged != OEM_MANAGED_ALL) { - builder.append(", oemManaged=").append(mOemManaged); + builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged)); } builder.append(", subscriberIdMatchRule=") .append(subscriberIdMatchRuleToString(mSubscriberIdMatchRule)); @@ -774,6 +777,19 @@ public class NetworkTemplate implements Parcelable { } } + private static String getOemManagedNames(int oemManaged) { + switch (oemManaged) { + case OEM_MANAGED_ALL: + return "OEM_MANAGED_ALL"; + case OEM_MANAGED_NO: + return "OEM_MANAGED_NO"; + case OEM_MANAGED_YES: + return "OEM_MANAGED_YES"; + default: + return NetworkIdentity.getOemManagedNames(oemManaged); + } + } + /** * Examine the given template and normalize if it refers to a "merged" * mobile subscriber. We pick the "lowest" merged subscriber as the primary diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java index 459fdacef816..33f9375c03bf 100644 --- a/core/java/android/net/UnderlyingNetworkInfo.java +++ b/core/java/android/net/UnderlyingNetworkInfo.java @@ -71,13 +71,13 @@ public final class UnderlyingNetworkInfo implements Parcelable { /** Get the interface name of this network. */ @NonNull - public String getIface() { + public String getInterface() { return mIface; } /** Get the names of the interfaces underlying this network. */ @NonNull - public List<String> getUnderlyingIfaces() { + public List<String> getUnderlyingInterfaces() { return mUnderlyingIfaces; } @@ -124,8 +124,8 @@ public final class UnderlyingNetworkInfo implements Parcelable { if (!(o instanceof UnderlyingNetworkInfo)) return false; final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o; return mOwnerUid == that.getOwnerUid() - && Objects.equals(mIface, that.getIface()) - && Objects.equals(mUnderlyingIfaces, that.getUnderlyingIfaces()); + && Objects.equals(mIface, that.getInterface()) + && Objects.equals(mUnderlyingIfaces, that.getUnderlyingInterfaces()); } @Override diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS index 33b9f0f75f81..2441e772468c 100644 --- a/core/java/android/net/vcn/OWNERS +++ b/core/java/android/net/vcn/OWNERS @@ -3,5 +3,5 @@ set noparent benedictwong@google.com ckesting@google.com evitayan@google.com +junyin@google.com nharold@google.com -jchalard@google.com
\ No newline at end of file diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 2df3e6c7ecd6..d59ad6f88864 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -161,9 +161,6 @@ public final class VcnGatewayConnectionConfig { private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities"; @NonNull private final SortedSet<Integer> mExposedCapabilities; - private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities"; - @NonNull private final SortedSet<Integer> mUnderlyingCapabilities; - private static final String MAX_MTU_KEY = "mMaxMtu"; private final int mMaxMtu; @@ -175,13 +172,11 @@ public final class VcnGatewayConnectionConfig { @NonNull String gatewayConnectionName, @NonNull IkeTunnelConnectionParams tunnelConnectionParams, @NonNull Set<Integer> exposedCapabilities, - @NonNull Set<Integer> underlyingCapabilities, @NonNull long[] retryIntervalsMs, @IntRange(from = MIN_MTU_V6) int maxMtu) { mGatewayConnectionName = gatewayConnectionName; mTunnelConnectionParams = tunnelConnectionParams; mExposedCapabilities = new TreeSet(exposedCapabilities); - mUnderlyingCapabilities = new TreeSet(underlyingCapabilities); mRetryIntervalsMs = retryIntervalsMs; mMaxMtu = maxMtu; @@ -198,16 +193,12 @@ public final class VcnGatewayConnectionConfig { final PersistableBundle exposedCapsBundle = in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY); - final PersistableBundle underlyingCapsBundle = - in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY); mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY); mTunnelConnectionParams = TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle); mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList( exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); - mUnderlyingCapabilities = new TreeSet<>(PersistableBundleUtils.toList( - underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY); mMaxMtu = in.getInt(MAX_MTU_KEY); @@ -307,36 +298,6 @@ public final class VcnGatewayConnectionConfig { } /** - * Returns all capabilities required of underlying networks. - * - * <p>The returned integer-value capabilities will be sorted in ascending numerical order. - * - * @see Builder#addRequiredUnderlyingCapability(int) - * @see Builder#removeRequiredUnderlyingCapability(int) - * @hide - */ - // TODO(b/182219992): Remove, and add when per-transport capabilities are supported - @NonNull - public int[] getRequiredUnderlyingCapabilities() { - // Sorted set guarantees ordering - return ArrayUtils.convertToIntArray(new ArrayList<>(mUnderlyingCapabilities)); - } - - /** - * Returns all capabilities required of underlying networks. - * - * <p>Left to prevent the need to make major changes while changes are actively in flight. - * - * @deprecated use getRequiredUnderlyingCapabilities() instead - * @hide - */ - @Deprecated - @NonNull - public Set<Integer> getAllUnderlyingCapabilities() { - return Collections.unmodifiableSet(mUnderlyingCapabilities); - } - - /** * Retrieves the configured retry intervals. * * @see Builder#setRetryIntervalsMillis(long[]) @@ -372,15 +333,10 @@ public final class VcnGatewayConnectionConfig { PersistableBundleUtils.fromList( new ArrayList<>(mExposedCapabilities), PersistableBundleUtils.INTEGER_SERIALIZER); - final PersistableBundle underlyingCapsBundle = - PersistableBundleUtils.fromList( - new ArrayList<>(mUnderlyingCapabilities), - PersistableBundleUtils.INTEGER_SERIALIZER); result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName); result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle); result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle); - result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle); result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs); result.putInt(MAX_MTU_KEY, mMaxMtu); @@ -392,7 +348,6 @@ public final class VcnGatewayConnectionConfig { return Objects.hash( mGatewayConnectionName, mExposedCapabilities, - mUnderlyingCapabilities, Arrays.hashCode(mRetryIntervalsMs), mMaxMtu); } @@ -406,7 +361,6 @@ public final class VcnGatewayConnectionConfig { final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other; return mGatewayConnectionName.equals(rhs.mGatewayConnectionName) && mExposedCapabilities.equals(rhs.mExposedCapabilities) - && mUnderlyingCapabilities.equals(rhs.mUnderlyingCapabilities) && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs) && mMaxMtu == rhs.mMaxMtu; } @@ -418,7 +372,6 @@ public final class VcnGatewayConnectionConfig { @NonNull private final String mGatewayConnectionName; @NonNull private final IkeTunnelConnectionParams mTunnelConnectionParams; @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); - @NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet(); @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS; private int mMaxMtu = DEFAULT_MAX_MTU; @@ -490,51 +443,6 @@ public final class VcnGatewayConnectionConfig { } /** - * Require a capability for Networks underlying this VCN Gateway Connection. - * - * @param underlyingCapability the capability that a network MUST have in order to be an - * underlying network for this VCN Gateway Connection. - * @return this {@link Builder} instance, for chaining - * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying - * networks - * @hide - */ - // TODO(b/182219992): Remove, and add when per-transport capabilities are supported - @NonNull - public Builder addRequiredUnderlyingCapability( - @VcnSupportedCapability int underlyingCapability) { - checkValidCapability(underlyingCapability); - - mUnderlyingCapabilities.add(underlyingCapability); - return this; - } - - /** - * Remove a requirement of a capability for Networks underlying this VCN Gateway Connection. - * - * <p>Calling this method will allow Networks that do NOT have this capability to be - * selected as an underlying network for this VCN Gateway Connection. However, underlying - * networks MAY still have the removed capability. - * - * @param underlyingCapability the capability that a network DOES NOT need to have in order - * to be an underlying network for this VCN Gateway Connection. - * @return this {@link Builder} instance, for chaining - * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying - * networks - * @hide - */ - // TODO(b/182219992): Remove, and add when per-transport capabilities are supported - @NonNull - @SuppressLint("BuilderSetStyle") // For consistency with NetCaps.Builder add/removeCap - public Builder removeRequiredUnderlyingCapability( - @VcnSupportedCapability int underlyingCapability) { - checkValidCapability(underlyingCapability); - - mUnderlyingCapabilities.remove(underlyingCapability); - return this; - } - - /** * Set the retry interval between VCN establishment attempts upon successive failures. * * <p>The last retry interval will be repeated until safe mode is entered, or a connection @@ -598,7 +506,6 @@ public final class VcnGatewayConnectionConfig { mGatewayConnectionName, mTunnelConnectionParams, mExposedCapabilities, - mUnderlyingCapabilities, mRetryIntervalsMs, mMaxMtu); } diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 9d1c1ff898e7..390c3b9453c2 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -74,6 +74,36 @@ import java.util.concurrent.Executor; public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); + /** + * Key for WiFi entry RSSI thresholds + * + * <p>The VCN will only migrate to a Carrier WiFi network that has a signal strength greater + * than, or equal to this threshold. + * + * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup. + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY = + "vcn_network_selection_wifi_entry_rssi_threshold"; + + /** + * Key for WiFi entry RSSI thresholds + * + * <p>If the VCN's selected Carrier WiFi network has a signal strength less than this threshold, + * the VCN will attempt to migrate away from the Carrier WiFi network. + * + * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup. + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY = + "vcn_network_selection_wifi_exit_rssi_threshold"; + + // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz + private static final Map< VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java index 0e9ccf144c2e..1f1818420b56 100644 --- a/core/java/android/net/vcn/VcnTransportInfo.java +++ b/core/java/android/net/vcn/VcnTransportInfo.java @@ -16,23 +16,17 @@ package android.net.vcn; -import static android.net.NetworkCapabilities.REDACT_ALL; -import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.NetworkCapabilities.REDACT_NONE; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.NetworkCapabilities; import android.net.TransportInfo; import android.net.wifi.WifiInfo; import android.os.Parcel; import android.os.Parcelable; import android.telephony.SubscriptionManager; -import com.android.internal.annotations.VisibleForTesting; - import java.util.Objects; /** @@ -55,32 +49,17 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { @Nullable private final WifiInfo mWifiInfo; private final int mSubId; - /** - * The redaction scheme to use when parcelling. - * - * <p>The TransportInfo/NetworkCapabilities redaction mechanisms rely on redaction being - * performed at parcelling time. This means that the redaction scheme must be stored for later - * use. - * - * <p>Since the redaction scheme itself is not parcelled, this field is listed as a transient. - * - * <p>Defaults to REDACT_ALL when constructed using public constructors, or creating from - * parcels. - */ - private final transient long mRedactions; - public VcnTransportInfo(@NonNull WifiInfo wifiInfo) { - this(wifiInfo, INVALID_SUBSCRIPTION_ID, REDACT_ALL); + this(wifiInfo, INVALID_SUBSCRIPTION_ID); } public VcnTransportInfo(int subId) { - this(null /* wifiInfo */, subId, REDACT_ALL); + this(null /* wifiInfo */, subId); } - private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId, long redactions) { + private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) { mWifiInfo = wifiInfo; mSubId = subId; - mRedactions = redactions; } /** @@ -102,25 +81,14 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. * * @return the Subscription ID if a cellular underlying Network is present, else {@link - * android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}. + * android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID}. */ public int getSubId() { return mSubId; } - /** - * Gets the redaction scheme - * - * @hide - */ - @VisibleForTesting(visibility = PRIVATE) - public long getRedaction() { - return mRedactions; - } - @Override public int hashCode() { - // mRedactions not hashed, as it is a transient, for control of parcelling return Objects.hash(mWifiInfo, mSubId); } @@ -128,8 +96,6 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { public boolean equals(Object o) { if (!(o instanceof VcnTransportInfo)) return false; final VcnTransportInfo that = (VcnTransportInfo) o; - - // mRedactions not compared, as it is a transient, for control of parcelling return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId; } @@ -143,31 +109,19 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { @NonNull public TransportInfo makeCopy(long redactions) { return new VcnTransportInfo( - mWifiInfo == null ? null : mWifiInfo.makeCopy(redactions), mSubId, redactions); + (mWifiInfo == null) ? null : mWifiInfo.makeCopy(redactions), mSubId); } @Override public long getApplicableRedactions() { - long redactions = REDACT_FOR_NETWORK_SETTINGS; - - // Add additional wifi redactions if necessary - if (mWifiInfo != null) { - redactions |= mWifiInfo.getApplicableRedactions(); - } - - return redactions; - } - - private boolean shouldParcelNetworkSettingsFields() { - return (mRedactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) == 0; + return (mWifiInfo == null) ? REDACT_NONE : mWifiInfo.getApplicableRedactions(); } /** {@inheritDoc} */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(shouldParcelNetworkSettingsFields() ? mSubId : INVALID_SUBSCRIPTION_ID); - dest.writeParcelable( - shouldParcelNetworkSettingsFields() ? (Parcelable) mWifiInfo : null, flags); + dest.writeInt(mSubId); + dest.writeParcelable(mWifiInfo, flags); } @Override @@ -181,17 +135,7 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { public VcnTransportInfo createFromParcel(Parcel in) { final int subId = in.readInt(); final WifiInfo wifiInfo = in.readParcelable(null); - - // If all fields are their null values, return null TransportInfo to avoid - // leaking information about this being a VCN Network (instead of macro - // cellular, etc) - if (wifiInfo == null && subId == INVALID_SUBSCRIPTION_ID) { - return null; - } - - // Prevent further forwarding by redacting everything in future parcels from - // this VcnTransportInfo - return new VcnTransportInfo(wifiInfo, subId, REDACT_ALL); + return new VcnTransportInfo(wifiInfo, subId); } public VcnTransportInfo[] newArray(int size) { diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index d9665953dceb..cd02d297270a 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -63,3 +63,6 @@ per-file *Zygote* = file:/ZYGOTE_OWNERS # RecoverySystem per-file *Recovery* = file:/services/core/java/com/android/server/recoverysystem/OWNERS + +# Bugreporting +per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS diff --git a/core/java/android/os/incremental/OWNERS b/core/java/android/os/incremental/OWNERS index 3795493b861f..47eee6406206 100644 --- a/core/java/android/os/incremental/OWNERS +++ b/core/java/android/os/incremental/OWNERS @@ -3,3 +3,4 @@ alexbuy@google.com schfan@google.com toddke@google.com zyy@google.com +patb@google.com diff --git a/core/java/com/android/internal/app/procstats/OWNERS b/core/java/com/android/internal/app/procstats/OWNERS new file mode 100644 index 000000000000..72c0a9e6e90c --- /dev/null +++ b/core/java/com/android/internal/app/procstats/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/am/OWNERS diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index c34b9f09ecaa..98082cb40f44 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -6126,6 +6126,17 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Records timing data related to an incoming Binder call in order to attribute + * the power consumption to the calling app. + */ + public void noteBinderCallStats(int workSourceUid, + Collection<BinderCallsStats.CallStat> callStats) { + synchronized (this) { + getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(callStats); + } + } + public String[] getWifiIfaces() { synchronized (mWifiNetworkLock) { return mWifiIfaces; @@ -8693,6 +8704,17 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * Notes incoming binder call stats associated with this work source UID. + */ + public void noteBinderCallStatsLocked(Collection<BinderCallsStats.CallStat> callStats) { + if (DEBUG) { + Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], callStats = [" + + new ArrayList<>(callStats) + "]"); + } + // TODO(dplotnikov): finish the implementation by actually remembering the stats + } + + /** * The statistics associated with a particular wake lock. */ public static class Wakelock extends BatteryStats.Uid.Wakelock { diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index dbba469dda1a..4cb5b020915b 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -19,10 +19,14 @@ package com.android.internal.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Binder; +import android.os.Handler; +import android.os.Looper; import android.os.Process; import android.os.SystemClock; +import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -95,7 +99,33 @@ public class BinderCallsStats implements BinderInternal.Observer { private CachedDeviceState.Readonly mDeviceState; private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; + private static final int CALL_STATS_OBSERVER_DEBOUNCE_MILLIS = 5000; private BinderLatencyObserver mLatencyObserver; + private BinderInternal.CallStatsObserver mCallStatsObserver; + private ArraySet<Integer> mSendUidsToObserver = new ArraySet<>(32); + private final Handler mCallStatsObserverHandler; + private Runnable mCallStatsObserverRunnable = new Runnable() { + @Override + public void run() { + if (mCallStatsObserver == null) { + return; + } + + noteCallsStatsDelayed(); + + synchronized (mLock) { + int size = mSendUidsToObserver.size(); + for (int i = 0; i < size; i++) { + UidEntry uidEntry = mUidEntries.get(mSendUidsToObserver.valueAt(i)); + if (uidEntry != null) { + mCallStatsObserver.noteCallStats(uidEntry.workSourceUid, + uidEntry.getCallStatsList()); + } + } + mSendUidsToObserver.clear(); + } + } + }; /** Injector for {@link BinderCallsStats}. */ public static class Injector { @@ -103,6 +133,10 @@ public class BinderCallsStats implements BinderInternal.Observer { return new Random(); } + public Handler getHandler() { + return new Handler(Looper.getMainLooper()); + } + public BinderLatencyObserver getLatencyObserver() { return new BinderLatencyObserver(new BinderLatencyObserver.Injector()); } @@ -110,6 +144,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public BinderCallsStats(Injector injector) { this.mRandom = injector.getRandomGenerator(); + this.mCallStatsObserverHandler = injector.getHandler(); this.mLatencyObserver = injector.getLatencyObserver(); } @@ -121,6 +156,24 @@ public class BinderCallsStats implements BinderInternal.Observer { mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch(); } + /** + * Registers an observer for call stats, which is invoked periodically with accumulated + * binder call stats. + */ + public void setCallStatsObserver( + BinderInternal.CallStatsObserver callStatsObserver) { + mCallStatsObserver = callStatsObserver; + noteCallsStatsDelayed(); + } + + private void noteCallsStatsDelayed() { + mCallStatsObserverHandler.removeCallbacks(mCallStatsObserverRunnable); + if (mCallStatsObserver != null) { + mCallStatsObserverHandler.postDelayed(mCallStatsObserverRunnable, + CALL_STATS_OBSERVER_DEBOUNCE_MILLIS); + } + } + @Override @Nullable public CallSession callStarted(Binder binder, int code, int workSourceUid) { @@ -233,6 +286,9 @@ public class BinderCallsStats implements BinderInternal.Observer { callStat.callCount++; } } + if (mCallStatsObserver != null && !UserHandle.isCore(workSourceUid)) { + mSendUidsToObserver.add(workSourceUid); + } } } @@ -267,7 +323,7 @@ public class BinderCallsStats implements BinderInternal.Observer { } @Nullable - private Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) { + private static Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) { try { return binder.getMethod("getDefaultTransactionName", int.class); } catch (NoSuchMethodException e) { @@ -277,16 +333,17 @@ public class BinderCallsStats implements BinderInternal.Observer { } @Nullable - private String resolveTransactionCode(Method getDefaultTransactionName, int transactionCode) { - if (getDefaultTransactionName == null) { - return null; - } - - try { - return (String) getDefaultTransactionName.invoke(null, transactionCode); - } catch (IllegalAccessException | InvocationTargetException | ClassCastException e) { - throw new RuntimeException(e); + private static String resolveTransactionCode(Method getDefaultTransactionName, + int transactionCode) { + String resolvedCode = null; + if (getDefaultTransactionName != null) { + try { + resolvedCode = (String) getDefaultTransactionName.invoke(null, transactionCode); + } catch (IllegalAccessException | InvocationTargetException | ClassCastException e) { + throw new RuntimeException(e); + } } + return resolvedCode == null ? String.valueOf(transactionCode) : resolvedCode; } /** @@ -342,11 +399,8 @@ public class BinderCallsStats implements BinderInternal.Observer { || previous.transactionCode != exported.transactionCode; final String methodName; if (isClassDifferent || isCodeDifferent) { - String resolvedCode = resolveTransactionCode( + methodName = resolveTransactionCode( getDefaultTransactionName, exported.transactionCode); - methodName = resolvedCode == null - ? String.valueOf(exported.transactionCode) - : resolvedCode; } else { methodName = previousMethodName; } @@ -657,6 +711,20 @@ public class BinderCallsStats implements BinderInternal.Observer { this.transactionCode = transactionCode; this.screenInteractive = screenInteractive; } + + @Override + public String toString() { + return "CallStat{" + + "callingUid=" + callingUid + + ", transaction=" + binderClass.getSimpleName() + + '.' + resolveTransactionCode( + getDefaultTransactionNameMethod(binderClass), transactionCode) + + ", callCount=" + callCount + + ", recordedCallCount=" + recordedCallCount + + ", cpuTimeMicros=" + cpuTimeMicros + + ", latencyMicros=" + latencyMicros + + '}'; + } } /** Key used to store CallStat object in a Map. */ diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index 95c36ca8429e..7253307feb63 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -31,6 +31,7 @@ import dalvik.system.VMRuntime; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; /** * Private and debugging Binder APIs. @@ -134,6 +135,16 @@ public class BinderInternal { } /** + * Allows to track observe incoming binder call stats. + */ + public interface CallStatsObserver { + /** + * Notes incoming binder call stats associated with this work source UID. + */ + void noteCallStats(int workSourceUid, Collection<BinderCallsStats.CallStat> callStats); + } + + /** * Add the calling thread to the IPC thread pool. This function does * not return until the current process is exiting. */ diff --git a/core/java/com/android/internal/os/BinderLatencyBuckets.java b/core/java/com/android/internal/os/BinderLatencyBuckets.java index bdee4ca8a6b8..d7d2d6a8f9dd 100644 --- a/core/java/com/android/internal/os/BinderLatencyBuckets.java +++ b/core/java/com/android/internal/os/BinderLatencyBuckets.java @@ -20,8 +20,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; /** * Generates the bucket thresholds (with a custom logarithmic scale) for a histogram to store @@ -29,7 +28,7 @@ import java.util.Collections; */ public class BinderLatencyBuckets { private static final String TAG = "BinderLatencyBuckets"; - private ArrayList<Integer> mBuckets; + private final int[] mBuckets; /** * @param bucketCount the number of buckets the histogram should have @@ -37,12 +36,11 @@ public class BinderLatencyBuckets { * @param scaleFactor the rate in which each consecutive bucket increases (before rounding) */ public BinderLatencyBuckets(int bucketCount, int firstBucketSize, float scaleFactor) { - mBuckets = new ArrayList<>(bucketCount - 1); - mBuckets.add(firstBucketSize); + int[] buffer = new int[bucketCount - 1]; + buffer[0] = firstBucketSize; // Last value and the target are disjoint as we never want to create buckets smaller than 1. double lastTarget = firstBucketSize; - int lastValue = firstBucketSize; // First bucket is already created and the last bucket is anything greater than the final // bucket in the list, so create 'bucketCount' - 2 buckets. @@ -50,29 +48,29 @@ public class BinderLatencyBuckets { // Increase the target bucket limit value by the scale factor. double nextTarget = lastTarget * scaleFactor; - if (nextTarget > Integer.MAX_VALUE || lastValue == Integer.MAX_VALUE) { + if (nextTarget > Integer.MAX_VALUE) { // Do not throw an exception here as this should not affect binder calls. Slog.w(TAG, "Attempted to create a bucket larger than maxint"); + mBuckets = Arrays.copyOfRange(buffer, 0, i); return; } - if ((int) nextTarget > lastValue) { + if ((int) nextTarget > buffer[i - 1]) { // Convert the target bucket limit value to an integer. - mBuckets.add((int) nextTarget); - lastValue = (int) nextTarget; + buffer[i] = (int) nextTarget; } else { // Avoid creating redundant buckets, so bucket size should be 1 at a minimum. - mBuckets.add(lastValue + 1); - lastValue = lastValue + 1; + buffer[i] = buffer[i - 1] + 1; } lastTarget = nextTarget; } + mBuckets = buffer; } /** Gets the bucket index to insert the provided sample in. */ public int sampleToBucket(int sample) { - if (sample > mBuckets.get(mBuckets.size() - 1)) { - return mBuckets.size(); + if (sample >= mBuckets[mBuckets.length - 1]) { + return mBuckets.length; } // Binary search returns the element index if it is contained in the list - in this case the @@ -80,12 +78,12 @@ public class BinderLatencyBuckets { // Otherwise, it returns (-(insertion point) - 1), where insertion point is the point where // to insert the element so that the array remains sorted - in this case the bucket index // is the insertion point. - int searchResult = Collections.binarySearch(mBuckets, sample); + int searchResult = Arrays.binarySearch(mBuckets, sample); return searchResult < 0 ? -(1 + searchResult) : searchResult + 1; } @VisibleForTesting - public ArrayList<Integer> getBuckets() { + public int[] getBuckets() { return mBuckets; } } diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 6860759eea8a..508782b9f3dc 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -21,8 +21,8 @@ import android.os.Trace; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; -import android.system.StructCapUserData; -import android.system.StructCapUserHeader; +import android.system.StructUserCapData; +import android.system.StructUserCapHeader; import android.util.Slog; import android.util.TimingsTraceLog; @@ -187,9 +187,9 @@ public class WrapperInit { * capabilities, which may make it crash, but not exceed its allowances. */ private static void preserveCapabilities() { - StructCapUserHeader header = new StructCapUserHeader( + StructUserCapHeader header = new StructUserCapHeader( OsConstants._LINUX_CAPABILITY_VERSION_3, 0); - StructCapUserData[] data; + StructUserCapData[] data; try { data = Os.capget(header); } catch (ErrnoException e) { @@ -199,9 +199,9 @@ public class WrapperInit { if (data[0].permitted != data[0].inheritable || data[1].permitted != data[1].inheritable) { - data[0] = new StructCapUserData(data[0].effective, data[0].permitted, + data[0] = new StructUserCapData(data[0].effective, data[0].permitted, data[0].permitted); - data[1] = new StructCapUserData(data[1].effective, data[1].permitted, + data[1] = new StructUserCapData(data[1].effective, data[1].permitted, data[1].permitted); try { Os.capset(header, data); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index a54108957775..0121b789f39a 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -45,8 +45,8 @@ import android.security.keystore2.AndroidKeyStoreProvider; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; -import android.system.StructCapUserData; -import android.system.StructCapUserHeader; +import android.system.StructUserCapData; +import android.system.StructUserCapHeader; import android.text.Hyphenator; import android.util.EventLog; import android.util.Log; @@ -742,9 +742,9 @@ public class ZygoteInit { OsConstants.CAP_BLOCK_SUSPEND ); /* Containers run without some capabilities, so drop any caps that are not available. */ - StructCapUserHeader header = new StructCapUserHeader( + StructUserCapHeader header = new StructUserCapHeader( OsConstants._LINUX_CAPABILITY_VERSION_3, 0); - StructCapUserData[] data; + StructUserCapData[] data; try { data = Os.capget(header); } catch (ErrnoException ex) { diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS index 1262925447b9..554e27890476 100644 --- a/core/java/com/android/server/OWNERS +++ b/core/java/com/android/server/OWNERS @@ -1 +1 @@ -per-file SystemConfig.java = toddke@google.com +per-file SystemConfig.java = toddke@google.com,patb@google.com diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp index 24fef4881e4c..5fe96ede202e 100644 --- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp +++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp @@ -402,7 +402,7 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( socklen_t cred_size = sizeof credentials; if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1 || cred_size != sizeof credentials) { - fail_fn_1("ForkMany failed to get initial credentials, %s", strerror(errno)); + fail_fn_1(CREATE_ERROR("ForkMany failed to get initial credentials, %s", strerror(errno))); } bool first_time = true; @@ -453,7 +453,7 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( close(session_socket); int new_fd = accept(zygote_socket_fd, nullptr, nullptr); if (new_fd == -1) { - fail_fn_z("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)); + fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno))); } if (new_fd != session_socket) { // Move new_fd back to the old value, so that we don't have to change Java-level data diff --git a/core/proto/OWNERS b/core/proto/OWNERS index e62b5c102a59..44ea23fdf685 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -14,9 +14,10 @@ per-file settings_enums.proto=tmfang@google.com # Frameworks ogunwale@google.com jjaggi@google.com +kwekua@google.com roosa@google.com -per-file package_item_info.proto = toddke@google.com -per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com +per-file package_item_info.proto = toddke@google.com,patb@google.com +per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS # Biometrics diff --git a/core/res/OWNERS b/core/res/OWNERS index 9d739b90bcc5..7a8da36d8a7d 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -1,6 +1,7 @@ adamp@google.com alanv@google.com asc@google.com +cinek@google.com dsandler@android.com dsandler@google.com dupin@google.com @@ -8,6 +9,7 @@ hackbod@android.com hackbod@google.com ilyamaty@google.com jaggies@google.com +jdemeulenaere@google.com jsharkey@android.com jsharkey@google.com juliacr@google.com @@ -17,7 +19,9 @@ nandana@google.com narayan@google.com ogunwale@google.com patb@google.com +shanh@google.com svetoslavganov@android.com svetoslavganov@google.com toddke@google.com +tsuji@google.com yamasani@google.com diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index cec62164e57a..ee6e6c00a4dc 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -16,10 +16,16 @@ package com.android.internal.os; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; @@ -46,11 +52,12 @@ import java.util.Random; @RunWith(AndroidJUnit4.class) @Presubmit public class BinderCallsStatsTest { - private static final int WORKSOURCE_UID = 1; + private static final int WORKSOURCE_UID = Process.FIRST_APPLICATION_UID; private static final int CALLING_UID = 2; private static final int REQUEST_SIZE = 2; private static final int REPLY_SIZE = 3; private final CachedDeviceState mDeviceState = new CachedDeviceState(false, true); + private final TestHandler mHandler = new TestHandler(); @Test public void testDetailedOff() { @@ -754,6 +761,51 @@ public class BinderCallsStatsTest { } @Test + public void testCallStatsObserver() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setSamplingInterval(1); + bcs.setTrackScreenInteractive(false); + + final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>(); + bcs.setCallStatsObserver((workSourceUid, callStats) -> callStatsList.addAll(callStats)); + + Binder binder = new Binder(); + + CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.time += 20; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID); + bcs.time += 30; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + for (Runnable runnable: mHandler.mRunnables) { + // Execute all pending runnables. Ignore the delay. + runnable.run(); + } + + assertThat(callStatsList).hasSize(2); + for (int i = 0; i < 2; i++) { + BinderCallsStats.CallStat callStats = callStatsList.get(i); + if (callStats.transactionCode == 1) { + assertEquals(2, callStats.callCount); + assertEquals(2, callStats.recordedCallCount); + assertEquals(30, callStats.cpuTimeMicros); + assertEquals(20, callStats.maxCpuTimeMicros); + } else { + assertEquals(1, callStats.callCount); + assertEquals(1, callStats.recordedCallCount); + assertEquals(30, callStats.cpuTimeMicros); + assertEquals(30, callStats.maxCpuTimeMicros); + } + } + } + + @Test public void testLatencyCollectionEnabled() { TestBinderCallsStats bcs = new TestBinderCallsStats(); bcs.setCollectLatencyData(true); @@ -781,6 +833,20 @@ public class BinderCallsStatsTest { assertEquals(0, bcs.getLatencyObserver().getLatencyHistograms().size()); } + private static class TestHandler extends Handler { + ArrayList<Runnable> mRunnables = new ArrayList<>(); + + TestHandler() { + super(Looper.getMainLooper()); + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + mRunnables.add(msg.getCallback()); + return true; + } + } + class TestBinderCallsStats extends BinderCallsStats { public int callingUid = CALLING_UID; public long time = 1234; @@ -803,6 +869,10 @@ public class BinderCallsStatsTest { }; } + public Handler getHandler() { + return mHandler; + } + public BinderLatencyObserver getLatencyObserver() { return new BinderLatencyObserverTest.TestBinderLatencyObserver(); } diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java index 00443a967c79..b2054f1ee9ad 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java @@ -36,6 +36,7 @@ public class BinderLatencyBucketsTest { public void testBucketThresholds() { BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(10, 2, 1.45f); assertThat(latencyBuckets.getBuckets()) + .asList() .containsExactly(2, 3, 4, 6, 8, 12, 18, 26, 39) .inOrder(); } @@ -58,6 +59,7 @@ public class BinderLatencyBucketsTest { public void testMaxIntBuckets() { BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(5, Integer.MAX_VALUE / 2, 2); assertThat(latencyBuckets.getBuckets()) + .asList() .containsExactly(Integer.MAX_VALUE / 2, Integer.MAX_VALUE - 1) .inOrder(); diff --git a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java index 3f9e62e7b180..952721320c90 100644 --- a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java @@ -29,6 +29,8 @@ import android.util.Log; import com.android.internal.util.FileRotator.Reader; import com.android.internal.util.FileRotator.Writer; +import com.android.internal.util.test.FsUtil; + import com.google.android.collect.Lists; import java.io.DataInputStream; @@ -38,15 +40,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.ProtocolException; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; -import junit.framework.Assert; - -import libcore.io.IoUtils; - /** * Tests for {@link FileRotator}. */ @@ -67,7 +64,7 @@ public class FileRotatorTest extends AndroidTestCase { super.setUp(); mBasePath = getContext().getFilesDir(); - IoUtils.deleteContents(mBasePath); + FsUtil.deleteContents(mBasePath); } public void testEmpty() throws Exception { diff --git a/data/etc/OWNERS b/data/etc/OWNERS index 5aacfddab28c..ea23aba16d12 100644 --- a/data/etc/OWNERS +++ b/data/etc/OWNERS @@ -10,6 +10,7 @@ svetoslavganov@android.com svetoslavganov@google.com toddke@android.com toddke@google.com +patb@google.com yamasani@google.com per-file preinstalled-packages* = file:/MULTIUSER_OWNERS diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java index fe05989c3846..97592b44ba2e 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java @@ -252,7 +252,9 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { blockModes, userAuthenticationRequired, (int) userAuthenticationValidityDurationSeconds, - keymasterHwEnforcedUserAuthenticators, + userAuthenticationRequirementEnforcedBySecureHardware + ? keymasterHwEnforcedUserAuthenticators + : keymasterSwEnforcedUserAuthenticators, userAuthenticationRequirementEnforcedBySecureHardware, userAuthenticationValidWhileOnBody, trustedUserPresenceRequired, diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 3e2fb94f0387..f3cfcf18dec1 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -41,6 +41,8 @@ import android.system.keystore2.KeyMetadata; import android.system.keystore2.ResponseCode; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -974,7 +976,6 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { } private Set<String> getUniqueAliases() { - try { final KeyDescriptor[] keys = mKeyStore.list( getTargetDomain(), @@ -987,7 +988,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { return aliases; } catch (android.security.KeyStoreException e) { Log.e(TAG, "Failed to list keystore entries.", e); - return null; + return new HashSet<>(); } } @@ -1099,6 +1100,17 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { return caAlias; } + /** + * Used by Tests to initialize with a fake KeyStore2. + * @hide + * @param keystore + */ + @VisibleForTesting + public void initForTesting(KeyStore2 keystore) { + mKeyStore = keystore; + mNamespace = KeyProperties.NAMESPACE_APPLICATION; + } + @Override public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp index 2315a8568c64..7de45233494b 100644 --- a/keystore/tests/Android.bp +++ b/keystore/tests/Android.bp @@ -28,6 +28,7 @@ android_test { static_libs: [ "androidx.test.rules", "hamcrest-library", + "mockito-target-minus-junit4", ], platform_apis: true, libs: ["android.test.runner"], diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java index b7d72fce6eba..2ae61ab3b38d 100644 --- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java @@ -43,7 +43,6 @@ public final class ParcelableKeyGenParameterSpecTest { static final String ALIAS = "keystore-alias"; static final String ANOTHER_ALIAS = "another-keystore-alias"; static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY; - static final int UID = 1230; static final int KEYSIZE = 2048; static final X500Principal SUBJECT = new X500Principal("CN=subject"); static final BigInteger SERIAL = new BigInteger("1234567890"); @@ -61,7 +60,7 @@ public final class ParcelableKeyGenParameterSpecTest { public static KeyGenParameterSpec configureDefaultSpec() { return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) - .setUid(UID) + .setNamespace(KeyProperties.NAMESPACE_WIFI) .setKeySize(KEYSIZE) .setCertificateSubject(SUBJECT) .setCertificateSerialNumber(SERIAL) @@ -88,10 +87,11 @@ public final class ParcelableKeyGenParameterSpecTest { .build(); } - public static void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) { + public static void validateSpecValues(KeyGenParameterSpec spec, + @KeyProperties.Namespace int namespace, String alias) { assertThat(spec.getKeystoreAlias(), is(alias)); assertThat(spec.getPurposes(), is(KEY_PURPOSES)); - assertThat(spec.getUid(), is(uid)); + assertThat(spec.getNamespace(), is(namespace)); assertThat(spec.getKeySize(), is(KEYSIZE)); assertThat(spec.getCertificateSubject(), is(SUBJECT)); assertThat(spec.getCertificateSerialNumber(), is(SERIAL)); @@ -134,7 +134,7 @@ public final class ParcelableKeyGenParameterSpecTest { Parcel parcel = parcelForReading(spec); ParcelableKeyGenParameterSpec fromParcel = ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel); - validateSpecValues(fromParcel.getSpec(), UID, ALIAS); + validateSpecValues(fromParcel.getSpec(), KeyProperties.NAMESPACE_WIFI, ALIAS); assertThat(parcel.dataAvail(), is(0)); } diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java index b2edfd05d13f..ddbb1d8c097c 100644 --- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java @@ -21,8 +21,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import android.security.ParcelableKeyGenParameterSpecTest; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyProperties; import androidx.test.runner.AndroidJUnit4; @@ -41,7 +39,7 @@ public final class KeyGenParameterSpecTest { KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build(); ParcelableKeyGenParameterSpecTest.validateSpecValues( - copiedSpec, spec.getUid(), spec.getKeystoreAlias()); + copiedSpec, spec.getNamespace(), spec.getKeystoreAlias()); } @Test diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java new file mode 100644 index 000000000000..1bd3069f483a --- /dev/null +++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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.security.keystore2; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.security.KeyStore2; +import android.security.KeyStoreException; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class AndroidKeyStoreSpiTest { + + @Mock + private KeyStore2 mKeystore2; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testEngineAliasesReturnsEmptySetOnKeyStoreError() throws Exception { + when(mKeystore2.list(anyInt(), anyLong())) + .thenThrow(new KeyStoreException(6, "Some Error")); + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + assertThat("Empty collection expected", !spi.engineAliases().hasMoreElements()); + + verify(mKeystore2).list(anyInt(), anyLong()); + } + +} diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS index bc056df23a36..610fd80fe73c 100644 --- a/libs/androidfw/OWNERS +++ b/libs/androidfw/OWNERS @@ -1,6 +1,7 @@ set noparent toddke@google.com rtmitchell@google.com +patb@google.com per-file CursorWindow.cpp=omakoto@google.com per-file LocaleDataTables.cpp=vichang@google.com,ngeoffray@google.com,nikitai@google.com diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index b219375aed70..6c454bcd4cd7 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -48,6 +48,7 @@ package android.net { public class ConnectivitySettingsManager { method public static void clearGlobalProxy(@NonNull android.content.Context); + method @NonNull public static java.util.Set<java.lang.String> getAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context); method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context); method public static int getCaptivePortalMode(@NonNull android.content.Context, int); method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration); @@ -65,9 +66,9 @@ package android.net { method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context); method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context); method public static int getPrivateDnsMode(@NonNull android.content.Context); - method @NonNull public static java.util.Set<java.lang.String> getRestrictedAllowedApps(@NonNull android.content.Context); method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean); method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setAppsAllowedOnRestrictedNetworks(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.String>); method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String); method public static void setCaptivePortalMode(@NonNull android.content.Context, int); method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration); @@ -85,7 +86,6 @@ package android.net { method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull int); method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String); method public static void setPrivateDnsMode(@NonNull android.content.Context, int); - method public static void setRestrictedAllowedApps(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.String>); method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean); method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2 diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt index 5750845c5863..27bf114b5229 100644 --- a/packages/Connectivity/framework/api/system-current.txt +++ b/packages/Connectivity/framework/api/system-current.txt @@ -294,7 +294,6 @@ package android.net { method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int); method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int); method @NonNull public android.net.NetworkCapabilities build(); - method @NonNull public android.net.NetworkCapabilities.Builder clearAll(); method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int); method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int); method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]); @@ -308,6 +307,7 @@ package android.net { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String); method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>); method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo); + method @NonNull public static android.net.NetworkCapabilities.Builder withoutDefaultCapabilities(); } public class NetworkProvider { @@ -381,6 +381,7 @@ package android.net { public abstract class QosFilter { method @NonNull public abstract android.net.Network getNetwork(); method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int); + method public abstract boolean matchesRemoteAddress(@NonNull java.net.InetAddress, int, int); } public final class QosSession implements android.os.Parcelable { @@ -403,6 +404,7 @@ package android.net { method public int describeContents(); method @NonNull public java.net.InetSocketAddress getLocalSocketAddress(); method @NonNull public android.net.Network getNetwork(); + method @Nullable public java.net.InetSocketAddress getRemoteSocketAddress(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR; } diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 0a3e23123702..1a6b37bfdb42 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -3337,7 +3337,60 @@ public class ConnectivityManager { provider.setProviderId(NetworkProvider.ID_NONE); } + /** + * Register or update a network offer with ConnectivityService. + * + * ConnectivityService keeps track of offers made by the various providers and matches + * them to networking requests made by apps or the system. The provider supplies a score + * and the capabilities of the network it might be able to bring up ; these act as filters + * used by ConnectivityService to only send those requests that can be fulfilled by the + * provider. + * + * The provider is under no obligation to be able to bring up the network it offers at any + * given time. Instead, this mechanism is meant to limit requests received by providers + * to those they actually have a chance to fulfill, as providers don't have a way to compare + * the quality of the network satisfying a given request to their own offer. + * + * An offer can be updated by calling this again with the same callback object. This is + * similar to calling unofferNetwork and offerNetwork again, but will only update the + * provider with the changes caused by the changes in the offer. + * + * @param provider The provider making this offer. + * @param score The prospective score of the network. + * @param caps The prospective capabilities of the network. + * @param callback The callback to call when this offer is needed or unneeded. + * @hide + */ + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_FACTORY}) + public void offerNetwork(@NonNull final NetworkProvider provider, + @NonNull final NetworkScore score, @NonNull final NetworkCapabilities caps, + @NonNull final INetworkOfferCallback callback) { + try { + mService.offerNetwork(Objects.requireNonNull(provider.getMessenger(), "null messenger"), + Objects.requireNonNull(score, "null score"), + Objects.requireNonNull(caps, "null caps"), + Objects.requireNonNull(callback, "null callback")); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** + * Withdraw a network offer made with {@link #offerNetwork}. + * + * @param callback The callback passed at registration time. This must be the same object + * that was passed to {@link #offerNetwork} + * @hide + */ + public void unofferNetwork(@NonNull final INetworkOfferCallback callback) { + try { + mService.unofferNetwork(Objects.requireNonNull(callback)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } /** @hide exposed via the NetworkProvider class. */ @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java index 07754e4af2ff..762f24f7e79a 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java @@ -43,7 +43,6 @@ import java.time.Duration; import java.util.List; import java.util.Set; import java.util.StringJoiner; -import java.util.regex.Pattern; /** * A manager class for connectivity module settings. @@ -375,11 +374,12 @@ public class ConnectivitySettingsManager { private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME_STRING = "hostname"; /** - * A list of apps that should be granted netd system permission for using restricted networks. + * A list of apps that is allowed on restricted networks. * * @hide */ - public static final String RESTRICTED_ALLOWED_APPS = "restricted_allowed_apps"; + public static final String APPS_ALLOWED_ON_RESTRICTED_NETWORKS = + "apps_allowed_on_restricted_networks"; /** * Get mobile data activity timeout from {@link Settings}. @@ -1047,17 +1047,16 @@ public class ConnectivitySettingsManager { } /** - * Get the list of apps(from {@link Settings}) that should be granted netd system permission for - * using restricted networks. + * Get the list of apps(from {@link Settings}) that is allowed on restricted networks. * * @param context The {@link Context} to query the setting. - * @return A list of apps that should be granted netd system permission for using restricted - * networks or null if no setting value. + * @return A list of apps that is allowed on restricted networks or null if no setting + * value. */ @NonNull - public static Set<String> getRestrictedAllowedApps(@NonNull Context context) { + public static Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) { final String appList = Settings.Secure.getString( - context.getContentResolver(), RESTRICTED_ALLOWED_APPS); + context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS); if (TextUtils.isEmpty(appList)) { return new ArraySet<>(); } @@ -1065,27 +1064,24 @@ public class ConnectivitySettingsManager { } /** - * Set the list of apps(from {@link Settings}) that should be granted netd system permission for - * using restricted networks. + * Set the list of apps(from {@link Settings}) that is allowed on restricted networks. * * Note: Please refer to android developer guidelines for valid app(package name). * https://developer.android.com/guide/topics/manifest/manifest-element.html#package * * @param context The {@link Context} to set the setting. - * @param list A list of apps that should be granted netd system permission for using - * restricted networks. + * @param list A list of apps that is allowed on restricted networks. */ - public static void setRestrictedAllowedApps(@NonNull Context context, + public static void setAppsAllowedOnRestrictedNetworks(@NonNull Context context, @NonNull Set<String> list) { - final Pattern appPattern = Pattern.compile("[a-zA-Z_0-9]+([.][a-zA-Z_0-9]+)*"); final StringJoiner joiner = new StringJoiner(";"); for (String app : list) { - if (!appPattern.matcher(app).matches()) { + if (app == null || app.contains(";")) { throw new IllegalArgumentException("Invalid app(package name)"); } joiner.add(app); } - Settings.Secure.putString( - context.getContentResolver(), RESTRICTED_ALLOWED_APPS, joiner.toString()); + Settings.Secure.putString(context.getContentResolver(), APPS_ALLOWED_ON_RESTRICTED_NETWORKS, + joiner.toString()); } } diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index a7cb618f9790..d937c9cd78c0 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -23,6 +23,7 @@ import android.net.IConnectivityDiagnosticsCallback; import android.net.INetworkAgent; import android.net.IOnCompleteListener; import android.net.INetworkActivityListener; +import android.net.INetworkOfferCallback; import android.net.IQosCallback; import android.net.ISocketKeepaliveCallback; import android.net.LinkProperties; @@ -221,4 +222,8 @@ interface IConnectivityManager in IOnCompleteListener listener); int getRestrictBackgroundStatusByCaller(); + + void offerNetwork(in Messenger messenger, in NetworkScore score, + in NetworkCapabilities caps, in INetworkOfferCallback callback); + void unofferNetwork(in INetworkOfferCallback callback); } diff --git a/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl b/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl new file mode 100644 index 000000000000..67d2d405dbed --- /dev/null +++ b/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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.net.NetworkRequest; + +/** + * A callback registered with connectivity by network providers together with + * a NetworkOffer. + * + * When the offer is needed to satisfy some application or system component, + * connectivity will call onOfferNeeded on this callback. When this happens, + * the provider should try and bring up the network. + * + * When the offer is no longer needed, for example because the application has + * withdrawn the request or if the request is being satisfied by a network + * that this offer will never be able to beat, connectivity calls + * onOfferUnneeded. When this happens, the provider should stop trying to + * bring up the network, or tear it down if it has already been brought up. + * + * When NetworkProvider#offerNetwork is called, the provider can expect to + * immediately receive all requests that can be fulfilled by that offer and + * are not already satisfied by a better network. It is possible no such + * request is currently outstanding, because no requests have been made that + * can be satisfied by this offer, or because all such requests are already + * satisfied by a better network. + * onOfferNeeded can be called at any time after registration and until the + * offer is withdrawn with NetworkProvider#unofferNetwork is called. This + * typically happens when a new network request is filed by an application, + * or when the network satisfying a request disconnects and this offer now + * stands a chance to be the best network for it. + * + * @hide + */ +oneway interface INetworkOfferCallback { + /** + * Informs the registrant that the offer is needed to fulfill this request. + * @param networkRequest the request to satisfy + * @param providerId the ID of the provider currently satisfying + * this request, or NetworkProvider.ID_NONE if none. + */ + void onOfferNeeded(in NetworkRequest networkRequest, int providerId); + + /** + * Informs the registrant that the offer is no longer needed to fulfill this request. + */ + void onOfferUnneeded(in NetworkRequest networkRequest); +} diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index 4a99d290f38c..90d821bd3b1e 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -139,19 +139,13 @@ public final class NetworkCapabilities implements Parcelable { */ private String mRequestorPackageName; - /** - * Indicates what fields should be redacted from this instance. - */ - private final @RedactionType long mRedactions; - public NetworkCapabilities() { - mRedactions = REDACT_ALL; clearAll(); mNetworkCapabilities = DEFAULT_CAPABILITIES; } public NetworkCapabilities(NetworkCapabilities nc) { - this(nc, REDACT_ALL); + this(nc, REDACT_NONE); } /** @@ -163,10 +157,12 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public NetworkCapabilities(@Nullable NetworkCapabilities nc, @RedactionType long redactions) { - mRedactions = redactions; if (nc != null) { set(nc); } + if (mTransportInfo != null) { + mTransportInfo = nc.mTransportInfo.makeCopy(redactions); + } } /** @@ -175,14 +171,6 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public void clearAll() { - // Ensures that the internal copies maintained by the connectivity stack does not set it to - // anything other than |REDACT_ALL|. - if (mRedactions != REDACT_ALL) { - // This is needed because the current redaction mechanism relies on redaction while - // parceling. - throw new UnsupportedOperationException( - "Cannot clear NetworkCapabilities when mRedactions is set"); - } mNetworkCapabilities = mTransportTypes = mForbiddenNetworkCapabilities = 0; mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; mNetworkSpecifier = null; @@ -211,7 +199,7 @@ public final class NetworkCapabilities implements Parcelable { mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; mNetworkSpecifier = nc.mNetworkSpecifier; if (nc.getTransportInfo() != null) { - setTransportInfo(nc.getTransportInfo().makeCopy(mRedactions)); + setTransportInfo(nc.getTransportInfo()); } else { setTransportInfo(null); } @@ -2411,6 +2399,11 @@ public final class NetworkCapabilities implements Parcelable { return mTransportInfo.getApplicableRedactions(); } + private NetworkCapabilities removeDefaultCapabilites() { + mNetworkCapabilities &= ~DEFAULT_CAPABILITIES; + return this; + } + /** * Builder class for NetworkCapabilities. * @@ -2447,6 +2440,16 @@ public final class NetworkCapabilities implements Parcelable { } /** + * Creates a new Builder without the default capabilities. + */ + @NonNull + public static Builder withoutDefaultCapabilities() { + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.removeDefaultCapabilites(); + return new Builder(nc); + } + + /** * Adds the given transport type. * * Multiple transports may be added. Note that when searching for a network to satisfy a @@ -2507,17 +2510,6 @@ public final class NetworkCapabilities implements Parcelable { } /** - * Completely clears the contents of this object, removing even the capabilities that are - * set by default when the object is constructed. - * @return this builder - */ - @NonNull - public Builder clearAll() { - mCaps.clearAll(); - return this; - } - - /** * Sets the owner UID. * * The default value is {@link Process#INVALID_UID}. Pass this value to reset. diff --git a/packages/Connectivity/framework/src/android/net/NetworkProvider.java b/packages/Connectivity/framework/src/android/net/NetworkProvider.java index 14cb51c85d06..8f93047cf850 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkProvider.java +++ b/packages/Connectivity/framework/src/android/net/NetworkProvider.java @@ -28,6 +28,11 @@ import android.os.Message; import android.os.Messenger; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.concurrent.Executor; + /** * Base class for network providers such as telephony or Wi-Fi. NetworkProviders connect the device * to networks and makes them available to the core network stack by creating @@ -78,7 +83,9 @@ public class NetworkProvider { */ @SystemApi public NetworkProvider(@NonNull Context context, @NonNull Looper looper, @NonNull String name) { - Handler handler = new Handler(looper) { + // TODO (b/174636568) : this class should be able to cache an instance of + // ConnectivityManager so it doesn't have to fetch it again every time. + final Handler handler = new Handler(looper) { @Override public void handleMessage(Message m) { switch (m.what) { @@ -159,4 +166,148 @@ public class NetworkProvider { public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) { ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request); } + + /** @hide */ + // TODO : make @SystemApi when the impl is complete + public interface NetworkOfferCallback { + /** Called by the system when this offer is needed to satisfy some networking request. */ + void onOfferNeeded(@NonNull NetworkRequest request, int providerId); + /** Called by the system when this offer is no longer needed. */ + void onOfferUnneeded(@NonNull NetworkRequest request); + } + + private class NetworkOfferCallbackProxy extends INetworkOfferCallback.Stub { + @NonNull public final NetworkOfferCallback callback; + @NonNull private final Executor mExecutor; + + NetworkOfferCallbackProxy(@NonNull final NetworkOfferCallback callback, + @NonNull final Executor executor) { + this.callback = callback; + this.mExecutor = executor; + } + + @Override + public void onOfferNeeded(final @NonNull NetworkRequest request, + final int providerId) { + mExecutor.execute(() -> callback.onOfferNeeded(request, providerId)); + } + + @Override + public void onOfferUnneeded(final @NonNull NetworkRequest request) { + mExecutor.execute(() -> callback.onOfferUnneeded(request)); + } + } + + @GuardedBy("mProxies") + @NonNull private final ArrayList<NetworkOfferCallbackProxy> mProxies = new ArrayList<>(); + + // Returns the proxy associated with this callback, or null if none. + @Nullable + private NetworkOfferCallbackProxy findProxyForCallback(@NonNull final NetworkOfferCallback cb) { + synchronized (mProxies) { + for (final NetworkOfferCallbackProxy p : mProxies) { + if (p.callback == cb) return p; + } + } + return null; + } + + /** + * Register or update an offer for network with the passed caps and score. + * + * A NetworkProvider's job is to provide networks. This function is how a provider tells the + * connectivity stack what kind of network it may provide. The score and caps arguments act + * as filters that the connectivity stack uses to tell when the offer is necessary. When an + * offer might be advantageous over existing networks, the provider will receive a call to + * the associated callback's {@link NetworkOfferCallback#onOfferNeeded} method. The provider + * should then try to bring up this network. When an offer is no longer needed, the stack + * will inform the provider by calling {@link NetworkOfferCallback#onOfferUnneeded}. The + * provider should stop trying to bring up such a network, or disconnect it if it already has + * one. + * + * The stack determines what offers are needed according to what networks are currently + * available to the system, and what networking requests are made by applications. If an + * offer looks like it could be a better choice than any existing network for any particular + * request, that's when the stack decides the offer is needed. If the current networking + * requests are all satisfied by networks that this offer can't possibly be a better match + * for, that's when the offer is unneeded. An offer starts off as unneeded ; the provider + * should not try to bring up the network until {@link NetworkOfferCallback#onOfferNeeded} + * is called. + * + * Note that the offers are non-binding to the providers, in particular because providers + * often don't know if they will be able to bring up such a network at any given time. For + * example, no wireless network may be in range when the offer is needed. This is fine and + * expected ; the provider should simply continue to try to bring up the network and do so + * if/when it becomes possible. In the mean time, the stack will continue to satisfy requests + * with the best network currently available, or if none, keep the apps informed that no + * network can currently satisfy this request. When/if the provider can bring up the network, + * the connectivity stack will match it against requests, and inform interested apps of the + * availability of this network. This may, in turn, render the offer of some other provider + * unneeded if all requests it used to satisfy are now better served by this network. + * + * A network can become unneeded for a reason like the above : whether the provider managed + * to bring up the offered network after it became needed or not, some other provider may + * bring up a better network than this one, making this offer unneeded. A network may also + * become unneeded if the application making the request withdrew it (for example, after it + * is done transferring data, or if the user canceled an operation). + * + * The capabilities and score act as filters as to what requests the provider will see. + * They are not promises, but for best performance, the providers should strive to put + * as much known information as possible in the offer. For capabilities in particular, it + * should put all NetworkAgent-managed capabilities a network may have, even if it doesn't + * have them at first. This applies to INTERNET, for example ; if a provider thinks the + * network it can bring up for this offer may offer Internet access it should include the + * INTERNET bit. It's fine if the brought up network ends up not actually having INTERNET. + * + * TODO : in the future, to avoid possible infinite loops, there should be constraints on + * what can be put in capabilities of networks brought up for an offer. If a provider might + * bring up a network with or without INTERNET, then it should file two offers : this will + * let it know precisely what networks are needed, so it can avoid bringing up networks that + * won't actually satisfy requests and remove the risk for bring-up-bring-down loops. + * + * @hide + */ + // TODO : make @SystemApi when the impl is complete + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void offerNetwork(@NonNull final NetworkScore score, + @NonNull final NetworkCapabilities caps, @NonNull final Executor executor, + @NonNull final NetworkOfferCallback callback) { + NetworkOfferCallbackProxy proxy = null; + synchronized (mProxies) { + for (final NetworkOfferCallbackProxy existingProxy : mProxies) { + if (existingProxy.callback == callback) { + proxy = existingProxy; + break; + } + } + if (null == proxy) { + proxy = new NetworkOfferCallbackProxy(callback, executor); + mProxies.add(proxy); + } + } + mContext.getSystemService(ConnectivityManager.class).offerNetwork(this, score, caps, proxy); + } + + /** + * Withdraw a network offer previously made to the networking stack. + * + * If a provider can no longer provide a network they offered, it should call this method. + * An example of usage could be if the hardware necessary to bring up the network was turned + * off in UI by the user. Note that because offers are never binding, the provider might + * alternatively decide not to withdraw this offer and simply refuse to bring up the network + * even when it's needed. However, withdrawing the request is slightly more resource-efficient + * because the networking stack won't have to compare this offer to exiting networks to see + * if it could beat any of them, and may be advantageous to the provider's implementation that + * can rely on no longer receiving callbacks for a network that they can't bring up anyways. + * + * @hide + */ + // TODO : make @SystemApi when the impl is complete + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void unofferNetwork(final @NonNull NetworkOfferCallback callback) { + final NetworkOfferCallbackProxy proxy = findProxyForCallback(callback); + if (null == proxy) return; + mProxies.remove(proxy); + mContext.getSystemService(ConnectivityManager.class).unofferNetwork(proxy); + } } diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index dd88c5a5c94e..e6a96ef74869 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -200,8 +200,9 @@ public class NetworkRequest implements Parcelable { private final NetworkCapabilities mNetworkCapabilities; - // A boolean that represents the user modified NOT_VCN_MANAGED capability. - private boolean mModifiedNotVcnManaged = false; + // A boolean that represents whether the NOT_VCN_MANAGED capability should be deduced when + // the NetworkRequest object is built. + private boolean mShouldDeduceNotVcnManaged = true; /** * Default constructor for Builder. @@ -223,7 +224,7 @@ public class NetworkRequest implements Parcelable { // If the caller constructed the builder from a request, it means the user // might explicitly want the capabilities from the request. Thus, the NOT_VCN_MANAGED // capabilities should not be touched later. - mModifiedNotVcnManaged = true; + mShouldDeduceNotVcnManaged = false; } /** @@ -254,7 +255,7 @@ public class NetworkRequest implements Parcelable { public Builder addCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.addCapability(capability); if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { - mModifiedNotVcnManaged = true; + mShouldDeduceNotVcnManaged = false; } return this; } @@ -268,7 +269,7 @@ public class NetworkRequest implements Parcelable { public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.removeCapability(capability); if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { - mModifiedNotVcnManaged = true; + mShouldDeduceNotVcnManaged = false; } return this; } @@ -352,7 +353,7 @@ public class NetworkRequest implements Parcelable { mNetworkCapabilities.clearAll(); // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities // should not be add back later. - mModifiedNotVcnManaged = true; + mShouldDeduceNotVcnManaged = false; return this; } @@ -453,6 +454,9 @@ public class NetworkRequest implements Parcelable { throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted"); } mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); + // Do not touch NOT_VCN_MANAGED if the caller needs to access to a very specific + // Network. + mShouldDeduceNotVcnManaged = false; return this; } @@ -486,12 +490,13 @@ public class NetworkRequest implements Parcelable { * {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to * allow the callers automatically utilize VCN networks if available. * 2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED, + * or has clear intention of tracking specific network, * do not alter them to allow user fire request that suits their need. * * @hide */ private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) { - if (mModifiedNotVcnManaged) return; + if (!mShouldDeduceNotVcnManaged) return; for (final int cap : nc.getCapabilities()) { if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return; } diff --git a/packages/Connectivity/framework/src/android/net/QosFilter.java b/packages/Connectivity/framework/src/android/net/QosFilter.java index ab55002e02b3..957c867f206d 100644 --- a/packages/Connectivity/framework/src/android/net/QosFilter.java +++ b/packages/Connectivity/framework/src/android/net/QosFilter.java @@ -71,5 +71,16 @@ public abstract class QosFilter { */ public abstract boolean matchesLocalAddress(@NonNull InetAddress address, int startPort, int endPort); + + /** + * Determines whether or not the parameters is a match for the filter. + * + * @param address the remote address + * @param startPort the start of the port range + * @param endPort the end of the port range + * @return whether the parameters match the remote address of the filter + */ + public abstract boolean matchesRemoteAddress(@NonNull InetAddress address, + int startPort, int endPort); } diff --git a/packages/Connectivity/framework/src/android/net/QosSocketFilter.java b/packages/Connectivity/framework/src/android/net/QosSocketFilter.java index 2080e68f5fba..69da7f440185 100644 --- a/packages/Connectivity/framework/src/android/net/QosSocketFilter.java +++ b/packages/Connectivity/framework/src/android/net/QosSocketFilter.java @@ -138,13 +138,26 @@ public class QosSocketFilter extends QosFilter { if (mQosSocketInfo.getLocalSocketAddress() == null) { return false; } + return matchesAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort, + endPort); + } - return matchesLocalAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort, + /** + * @inheritDoc + */ + @Override + public boolean matchesRemoteAddress(@NonNull final InetAddress address, final int startPort, + final int endPort) { + if (mQosSocketInfo.getRemoteSocketAddress() == null) { + return false; + } + return matchesAddress(mQosSocketInfo.getRemoteSocketAddress(), address, startPort, endPort); } /** - * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} with the + * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} + * and {@link QosSocketFilter#matchesRemoteAddress(InetAddress, int, int)} with the * filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}. * <p> * This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked @@ -156,7 +169,7 @@ public class QosSocketFilter extends QosFilter { * @param endPort the end of the port range to check */ @VisibleForTesting - public static boolean matchesLocalAddress(@NonNull final InetSocketAddress filterSocketAddress, + public static boolean matchesAddress(@NonNull final InetSocketAddress filterSocketAddress, @NonNull final InetAddress address, final int startPort, final int endPort) { return startPort <= filterSocketAddress.getPort() diff --git a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java index 53d966937a70..a45d5075d6c7 100644 --- a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java +++ b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.ParcelFileDescriptor; @@ -32,7 +33,8 @@ import java.util.Objects; /** * Used in conjunction with * {@link ConnectivityManager#registerQosCallback} - * in order to receive Qos Sessions related to the local address and port of a bound {@link Socket}. + * in order to receive Qos Sessions related to the local address and port of a bound {@link Socket} + * and/or remote address and port of a connected {@link Socket}. * * @hide */ @@ -48,6 +50,9 @@ public final class QosSocketInfo implements Parcelable { @NonNull private final InetSocketAddress mLocalSocketAddress; + @Nullable + private final InetSocketAddress mRemoteSocketAddress; + /** * The {@link Network} the socket is on. * @@ -81,6 +86,18 @@ public final class QosSocketInfo implements Parcelable { } /** + * The remote address of the socket passed into {@link QosSocketInfo(Network, Socket)}. + * The value does not reflect any changes that occur to the socket after it is first set + * in the constructor. + * + * @return the remote address of the socket if socket is connected, null otherwise + */ + @Nullable + public InetSocketAddress getRemoteSocketAddress() { + return mRemoteSocketAddress; + } + + /** * Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link Socket}. The * {@link Socket} must remain bound in order to receive {@link QosSession}s. * @@ -95,6 +112,12 @@ public final class QosSocketInfo implements Parcelable { mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket); mLocalSocketAddress = new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort()); + + if (socket.isConnected()) { + mRemoteSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress(); + } else { + mRemoteSocketAddress = null; + } } /* Parcelable methods */ @@ -102,11 +125,15 @@ public final class QosSocketInfo implements Parcelable { mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in)); mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in); - final int addressLength = in.readInt(); - mLocalSocketAddress = readSocketAddress(in, addressLength); + final int localAddressLength = in.readInt(); + mLocalSocketAddress = readSocketAddress(in, localAddressLength); + + final int remoteAddressLength = in.readInt(); + mRemoteSocketAddress = remoteAddressLength == 0 ? null + : readSocketAddress(in, remoteAddressLength); } - private InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) { + private @NonNull InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) { final byte[] address = new byte[addressLength]; in.readByteArray(address); final int port = in.readInt(); @@ -130,10 +157,19 @@ public final class QosSocketInfo implements Parcelable { mNetwork.writeToParcel(dest, 0); mParcelFileDescriptor.writeToParcel(dest, 0); - final byte[] address = mLocalSocketAddress.getAddress().getAddress(); - dest.writeInt(address.length); - dest.writeByteArray(address); + final byte[] localAddress = mLocalSocketAddress.getAddress().getAddress(); + dest.writeInt(localAddress.length); + dest.writeByteArray(localAddress); dest.writeInt(mLocalSocketAddress.getPort()); + + if (mRemoteSocketAddress == null) { + dest.writeInt(0); + } else { + final byte[] remoteAddress = mRemoteSocketAddress.getAddress().getAddress(); + dest.writeInt(remoteAddress.length); + dest.writeByteArray(remoteAddress); + dest.writeInt(mRemoteSocketAddress.getPort()); + } } @NonNull diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp index 1330e719e774..513de1956952 100644 --- a/packages/Connectivity/service/Android.bp +++ b/packages/Connectivity/service/Android.bp @@ -52,8 +52,8 @@ cc_library_shared { java_library { name: "service-connectivity-pre-jarjar", srcs: [ + "src/**/*.java", ":framework-connectivity-shared-srcs", - ":connectivity-service-srcs", ], libs: [ "android.net.ipsec.ike", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java index 39a990cf5d73..e192c8fae266 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java @@ -15,7 +15,6 @@ */ package com.android.server; - import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.content.pm.PackageManager.FEATURE_BLUETOOTH; import static android.content.pm.PackageManager.FEATURE_WATCH; @@ -125,6 +124,7 @@ import android.net.INetworkActivityListener; import android.net.INetworkAgent; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; +import android.net.INetworkOfferCallback; import android.net.IOnCompleteListener; import android.net.IQosCallback; import android.net.ISocketKeepaliveCallback; @@ -234,6 +234,7 @@ import com.android.net.module.util.PermissionUtils; import com.android.server.connectivity.AutodestructReference; import com.android.server.connectivity.DnsManager; import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate; +import com.android.server.connectivity.FullScore; import com.android.server.connectivity.KeepaliveTracker; import com.android.server.connectivity.LingerMonitor; import com.android.server.connectivity.MockableSystemProperties; @@ -241,6 +242,7 @@ import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkDiagnostics; import com.android.server.connectivity.NetworkNotificationManager; import com.android.server.connectivity.NetworkNotificationManager.NotificationType; +import com.android.server.connectivity.NetworkOffer; import com.android.server.connectivity.NetworkRanker; import com.android.server.connectivity.PermissionMonitor; import com.android.server.connectivity.ProfileNetworkPreferences; @@ -604,6 +606,18 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_UID_BLOCKED_REASON_CHANGED = 51; /** + * Event to register a new network offer + * obj = NetworkOffer + */ + private static final int EVENT_REGISTER_NETWORK_OFFER = 52; + + /** + * Event to unregister an existing network offer + * obj = INetworkOfferCallback + */ + private static final int EVENT_UNREGISTER_NETWORK_OFFER = 53; + + /** * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification * should be shown. */ @@ -1031,14 +1045,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { // ConnectivityService publishes binder service using publishBinderService() with // no priority assigned will be treated as NORMAL priority. Dumpsys does not send - // "--dump-priority" arguments to the service. Thus, dump both NORMAL and HIGH to - // align the legacy design. + // "--dump-priority" arguments to the service. Thus, dump NORMAL only to align the + // legacy output for dumpsys connectivity. // TODO: Integrate into signal dump. dumpNormal(fd, pw, args); - pw.println(); - pw.println("DUMP OF SERVICE HIGH connectivity"); - pw.println(); - dumpHigh(fd, pw); } } } @@ -1380,7 +1390,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // arguments like the handler or the DnsResolver. // TODO : remove this ; it is probably better handled with a sentinel request. mNoServiceNetwork = new NetworkAgentInfo(null, - new Network(NO_SERVICE_NET_ID), + new Network(INetd.UNREACHABLE_NET_ID), new NetworkInfo(TYPE_NONE, 0, "", ""), new LinkProperties(), new NetworkCapabilities(), new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null, @@ -4684,6 +4694,18 @@ public class ConnectivityService extends IConnectivityManager.Stub handleUnregisterNetworkProvider((Messenger) msg.obj); break; } + case EVENT_REGISTER_NETWORK_OFFER: { + handleRegisterNetworkOffer((NetworkOffer) msg.obj); + break; + } + case EVENT_UNREGISTER_NETWORK_OFFER: { + final NetworkOfferInfo offer = + findNetworkOfferInfoByCallback((INetworkOfferCallback) msg.obj); + if (null != offer) { + handleUnregisterNetworkOffer(offer); + } + break; + } case EVENT_REGISTER_NETWORK_AGENT: { final Pair<NetworkAgentInfo, INetworkMonitor> arg = (Pair<NetworkAgentInfo, INetworkMonitor>) msg.obj; @@ -6214,12 +6236,37 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger)); } + @Override + public void offerNetwork(@NonNull final Messenger providerMessenger, + @NonNull final NetworkScore score, @NonNull final NetworkCapabilities caps, + @NonNull final INetworkOfferCallback callback) { + final NetworkOffer offer = new NetworkOffer( + FullScore.makeProspectiveScore(score, caps), caps, callback, providerMessenger); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_OFFER, offer)); + } + + @Override + public void unofferNetwork(@NonNull final INetworkOfferCallback callback) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_OFFER, callback)); + } + private void handleUnregisterNetworkProvider(Messenger messenger) { NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger); if (npi == null) { loge("Failed to find Messenger in unregisterNetworkProvider"); return; } + // Unregister all the offers from this provider + final ArrayList<NetworkOfferInfo> toRemove = new ArrayList<>(); + for (final NetworkOfferInfo noi : mNetworkOffers) { + if (noi.offer.provider == messenger) { + // Can't call handleUnregisterNetworkOffer here because iteration is in progress + toRemove.add(noi); + } + } + for (NetworkOfferInfo noi : toRemove) { + handleUnregisterNetworkOffer(noi); + } if (DBG) log("unregisterNetworkProvider for " + npi.name); } @@ -6258,6 +6305,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // (on the handler thread). private volatile List<UidRange> mVpnBlockedUidRanges = new ArrayList<>(); + // Must only be accessed on the handler thread + @NonNull + private final ArrayList<NetworkOfferInfo> mNetworkOffers = new ArrayList<>(); + @GuardedBy("mBlockedAppUids") private final HashSet<Integer> mBlockedAppUids = new HashSet<>(); @@ -6433,8 +6484,6 @@ public class ConnectivityService extends IConnectivityManager.Stub // Request used to optionally keep vehicle internal network always active private final NetworkRequest mDefaultVehicleRequest; - // TODO replace with INetd.UNREACHABLE_NET_ID when available. - private static final int NO_SERVICE_NET_ID = 52; // Sentinel NAI used to direct apps with default networks that should have no connectivity to a // network with no service. This NAI should never be matched against, nor should any public API // ever return the associated network. For this reason, this NAI is not in the list of available @@ -6582,6 +6631,65 @@ public class ConnectivityService extends IConnectivityManager.Stub updateUids(nai, null, nai.networkCapabilities); } + private class NetworkOfferInfo implements IBinder.DeathRecipient { + @NonNull public final NetworkOffer offer; + + NetworkOfferInfo(@NonNull final NetworkOffer offer) { + this.offer = offer; + } + + @Override + public void binderDied() { + mHandler.post(() -> handleUnregisterNetworkOffer(this)); + } + } + + /** + * Register or update a network offer. + * @param newOffer The new offer. If the callback member is the same as an existing + * offer, it is an update of that offer. + */ + private void handleRegisterNetworkOffer(@NonNull final NetworkOffer newOffer) { + ensureRunningOnConnectivityServiceThread(); + if (null == mNetworkProviderInfos.get(newOffer.provider)) { + // This may actually happen if a provider updates its score or registers and then + // immediately unregisters. The offer would still be in the handler queue, but the + // provider would have been removed. + if (DBG) log("Received offer from an unregistered provider"); + return; + } + + final NetworkOfferInfo existingOffer = findNetworkOfferInfoByCallback(newOffer.callback); + if (null != existingOffer) { + handleUnregisterNetworkOffer(existingOffer); + newOffer.migrateFrom(existingOffer.offer); + } + final NetworkOfferInfo noi = new NetworkOfferInfo(newOffer); + try { + noi.offer.provider.getBinder().linkToDeath(noi, 0 /* flags */); + } catch (RemoteException e) { + noi.binderDied(); + return; + } + mNetworkOffers.add(noi); + // TODO : send requests to the provider. + } + + private void handleUnregisterNetworkOffer(@NonNull final NetworkOfferInfo noi) { + ensureRunningOnConnectivityServiceThread(); + mNetworkOffers.remove(noi); + noi.offer.provider.getBinder().unlinkToDeath(noi, 0 /* flags */); + } + + @Nullable private NetworkOfferInfo findNetworkOfferInfoByCallback( + @NonNull final INetworkOfferCallback callback) { + ensureRunningOnConnectivityServiceThread(); + for (final NetworkOfferInfo noi : mNetworkOffers) { + if (noi.offer.callback.equals(callback)) return noi; + } + return null; + } + /** * Called when receiving LinkProperties directly from a NetworkAgent. * Stores into |nai| any data coming from the agent that might also be written to the network's @@ -8476,11 +8584,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final UnderlyingNetworkInfo[] underlyingNetworkInfos = getAllVpnInfo(); try { final ArrayList<NetworkStateSnapshot> snapshots = new ArrayList<>(); - // TODO: Directly use NetworkStateSnapshot when feasible. - for (final NetworkState state : getAllNetworkState()) { - final NetworkStateSnapshot snapshot = new NetworkStateSnapshot(state.network, - state.networkCapabilities, state.linkProperties, state.subscriberId, - state.legacyNetworkType); + for (final NetworkStateSnapshot snapshot : getAllNetworkStateSnapshots()) { snapshots.add(snapshot); } mStatsManager.notifyNetworkStatus(getDefaultNetworks(), @@ -9041,7 +9145,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) { - final NetworkCapabilities sanitized = new NetworkCapabilities(nc); + final NetworkCapabilities sanitized = new NetworkCapabilities(nc, + NetworkCapabilities.REDACT_ALL); sanitized.setUids(null); sanitized.setAdministratorUids(new int[0]); sanitized.setOwnerUid(Process.INVALID_UID); diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/packages/Connectivity/service/src/com/android/server/ConnectivityServiceInitializer.java index 2465479aadd8..2465479aadd8 100644 --- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java +++ b/packages/Connectivity/service/src/com/android/server/ConnectivityServiceInitializer.java diff --git a/services/core/java/com/android/server/NetIdManager.java b/packages/Connectivity/service/src/com/android/server/NetIdManager.java index 61925c80a22b..61925c80a22b 100644 --- a/services/core/java/com/android/server/NetIdManager.java +++ b/packages/Connectivity/service/src/com/android/server/NetIdManager.java diff --git a/services/core/java/com/android/server/TestNetworkService.java b/packages/Connectivity/service/src/com/android/server/TestNetworkService.java index f5662772f59f..f5662772f59f 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/packages/Connectivity/service/src/com/android/server/TestNetworkService.java diff --git a/services/core/java/com/android/server/connectivity/AutodestructReference.java b/packages/Connectivity/service/src/com/android/server/connectivity/AutodestructReference.java index 009a43e58285..009a43e58285 100644 --- a/services/core/java/com/android/server/connectivity/AutodestructReference.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/AutodestructReference.java diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/packages/Connectivity/service/src/com/android/server/connectivity/ConnectivityConstants.java index 325a2cd7bd69..325a2cd7bd69 100644 --- a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/ConnectivityConstants.java diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java index 05b12bad5589..05b12bad5589 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java index 028cfee36593..9326d692f6e4 100644 --- a/services/core/java/com/android/server/connectivity/FullScore.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java @@ -16,6 +16,7 @@ package com.android.server.connectivity; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_VPN; @@ -116,6 +117,33 @@ public class FullScore { } /** + * Given a score supplied by the NetworkAgent, produce a prospective score for an offer. + * + * NetworkOffers have score filters that are compared to the scores of actual networks + * to see if they could possibly beat the current satisfier. Some things the agent can't + * know in advance ; a good example is the validation bit – some networks will validate, + * others won't. For comparison purposes, assume the best, so all possibly beneficial + * networks will be brought up. + * + * @param score the score supplied by the agent for this offer + * @param caps the capabilities supplied by the agent for this offer + * @return a FullScore appropriate for comparing to actual network's scores. + */ + public static FullScore makeProspectiveScore(@NonNull final NetworkScore score, + @NonNull final NetworkCapabilities caps) { + // If the network offers Internet access, it may validate. + final boolean mayValidate = caps.hasCapability(NET_CAPABILITY_INTERNET); + // VPN transports are known in advance. + final boolean vpn = caps.hasTransport(TRANSPORT_VPN); + // The network hasn't been chosen by the user (yet, at least). + final boolean everUserSelected = false; + // Don't assume the user will accept unvalidated connectivity. + final boolean acceptUnvalidated = false; + return withPolicies(score.getLegacyInt(), mayValidate, vpn, everUserSelected, + acceptUnvalidated); + } + + /** * Return a new score given updated caps and config. * * @param caps the NetworkCapabilities of the network diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/KeepaliveTracker.java index acf39f05a541..acf39f05a541 100644 --- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/KeepaliveTracker.java diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/packages/Connectivity/service/src/com/android/server/connectivity/LingerMonitor.java index 032612c6f093..032612c6f093 100644 --- a/services/core/java/com/android/server/connectivity/LingerMonitor.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/LingerMonitor.java diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/packages/Connectivity/service/src/com/android/server/connectivity/MockableSystemProperties.java index a25b89ac039a..a25b89ac039a 100644 --- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/MockableSystemProperties.java diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/packages/Connectivity/service/src/com/android/server/connectivity/Nat464Xlat.java index c66a280f2b02..c66a280f2b02 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/Nat464Xlat.java diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java index ee32fbf00dfe..ee32fbf00dfe 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkDiagnostics.java index 2e51be39bfae..2e51be39bfae 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkDiagnostics.java diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkNotificationManager.java index 0c0d45995a2b..0c0d45995a2b 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkNotificationManager.java diff --git a/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java new file mode 100644 index 000000000000..fa2d465fff1d --- /dev/null +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021 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.server.connectivity; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.INetworkOfferCallback; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.os.Messenger; + +import java.util.Objects; + + +/** + * Represents an offer made by a NetworkProvider to create a network if a need arises. + * + * This class contains the prospective score and capabilities of the network. The provider + * is not obligated to caps able to create a network satisfying this, nor to build a network + * with the exact score and/or capabilities passed ; after all, not all providers know in + * advance what a network will look like after it's connected. Instead, this is meant as a + * filter to limit requests sent to the provider by connectivity to those that this offer stands + * a chance to fulfill. + * + * @see NetworkProvider#offerNetwork. + * + * @hide + */ +public class NetworkOffer { + @NonNull public final FullScore score; + @NonNull public final NetworkCapabilities caps; + @NonNull public final INetworkOfferCallback callback; + @NonNull public final Messenger provider; + + private static NetworkCapabilities emptyCaps() { + final NetworkCapabilities nc = new NetworkCapabilities(); + return nc; + } + + // Ideally the caps argument would be non-null, but null has historically meant no filter + // and telephony passes null. Keep backward compatibility. + public NetworkOffer(@NonNull final FullScore score, + @Nullable final NetworkCapabilities caps, + @NonNull final INetworkOfferCallback callback, + @NonNull final Messenger provider) { + this.score = Objects.requireNonNull(score); + this.caps = null != caps ? caps : emptyCaps(); + this.callback = Objects.requireNonNull(callback); + this.provider = Objects.requireNonNull(provider); + } + + /** + * Migrate from, and take over, a previous offer. + * + * When an updated offer is sent from a provider, call this method on the new offer, passing + * the old one, to take over the state. + * + * @param previousOffer + */ + public void migrateFrom(@NonNull final NetworkOffer previousOffer) { + if (!callback.equals(previousOffer.callback)) { + throw new IllegalArgumentException("Can only migrate from a previous version of" + + " the same offer"); + } + } + + /** + * Returns whether an offer can satisfy a NetworkRequest, according to its capabilities. + * @param request The request to test against. + * @return Whether this offer can satisfy the request. + */ + public final boolean canSatisfy(@NonNull final NetworkRequest request) { + return request.networkCapabilities.satisfiedByNetworkCapabilities(caps); + } + + @Override + public String toString() { + return "NetworkOffer [ Score " + score + " ]"; + } +} diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java index d0aabf95d572..d0aabf95d572 100644 --- a/services/core/java/com/android/server/connectivity/NetworkRanker.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java index 506cadb2b1d2..673c80417b8d 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java @@ -24,6 +24,7 @@ import static android.Manifest.permission.UPDATE_DEVICE_STATS; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.net.ConnectivitySettingsManager.APPS_ALLOWED_ON_RESTRICTED_NETWORKS; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; @@ -39,6 +40,8 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.database.ContentObserver; +import android.net.ConnectivitySettingsManager; import android.net.INetd; import android.net.UidRange; import android.net.Uri; @@ -48,7 +51,9 @@ import android.os.ServiceSpecificException; import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.system.OsConstants; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; @@ -66,7 +71,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; - /** * A utility class to inform Netd of UID permisisons. * Does a mass update at boot and then monitors for app install/remove. @@ -105,6 +109,14 @@ public class PermissionMonitor { @GuardedBy("this") private final Set<Integer> mAllApps = new HashSet<>(); + // A set of apps which are allowed to use restricted networks. These apps can't hold the + // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission because they can't be signature|privileged + // apps. However, these apps should still be able to use restricted networks under certain + // conditions (e.g. government app using emergency services). So grant netd system permission + // to uids whose package name is listed in APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting. + @GuardedBy("this") + private final Set<String> mAppsAllowedOnRestrictedNetworks = new ArraySet<>(); + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -135,6 +147,22 @@ public class PermissionMonitor { public int getDeviceFirstSdkInt() { return Build.VERSION.FIRST_SDK_INT; } + + /** + * Get apps allowed to use restricted networks via ConnectivitySettingsManager. + */ + public Set<String> getAppsAllowedOnRestrictedNetworks(@NonNull Context context) { + return ConnectivitySettingsManager.getAppsAllowedOnRestrictedNetworks(context); + } + + /** + * Register ContentObserver for given Uri. + */ + public void registerContentObserver(@NonNull Context context, @NonNull Uri uri, + boolean notifyForDescendants, @NonNull ContentObserver observer) { + context.getContentResolver().registerContentObserver( + uri, notifyForDescendants, observer); + } } public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) { @@ -157,14 +185,31 @@ public class PermissionMonitor { public synchronized void startMonitoring() { log("Monitoring"); + final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); - mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */).registerReceiver( + userAllContext.registerReceiver( mIntentReceiver, intentFilter, null /* broadcastPermission */, null /* scheduler */); + // Register APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer + mDeps.registerContentObserver( + userAllContext, + Settings.Secure.getUriFor(APPS_ALLOWED_ON_RESTRICTED_NETWORKS), + false /* notifyForDescendants */, + new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + onSettingChanged(); + } + }); + + // Read APPS_ALLOWED_ON_RESTRICTED_NETWORKS setting and update + // mAppsAllowedOnRestrictedNetworks. + updateAppsAllowedOnRestrictedNetworks(mDeps.getAppsAllowedOnRestrictedNetworks(mContext)); + List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS | MATCH_ANY_USER); if (apps == null) { @@ -220,11 +265,33 @@ public class PermissionMonitor { } @VisibleForTesting + void updateAppsAllowedOnRestrictedNetworks(final Set<String> apps) { + mAppsAllowedOnRestrictedNetworks.clear(); + mAppsAllowedOnRestrictedNetworks.addAll(apps); + } + + @VisibleForTesting static boolean isVendorApp(@NonNull ApplicationInfo appInfo) { return appInfo.isVendor() || appInfo.isOem() || appInfo.isProduct(); } @VisibleForTesting + boolean isCarryoverPackage(final ApplicationInfo appInfo) { + if (appInfo == null) return false; + return (appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo)) + // Backward compatibility for b/114245686, on devices that launched before Q daemons + // and apps running as the system UID are exempted from this check. + || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q); + } + + @VisibleForTesting + boolean isAppAllowedOnRestrictedNetworks(@NonNull final PackageInfo app) { + // Check whether package name is in allowed on restricted networks app list. If so, this app + // can have netd system permission. + return mAppsAllowedOnRestrictedNetworks.contains(app.packageName); + } + + @VisibleForTesting boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) { if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) { return false; @@ -241,22 +308,10 @@ public class PermissionMonitor { @VisibleForTesting boolean hasRestrictedNetworkPermission(@NonNull final PackageInfo app) { - // TODO : remove this check in the future(b/31479477). All apps should just - // request the appropriate permission for their use case since android Q. - if (app.applicationInfo != null) { - // Backward compatibility for b/114245686, on devices that launched before Q daemons - // and apps running as the system UID are exempted from this check. - if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) { - return true; - } - - if (app.applicationInfo.targetSdkVersion < VERSION_Q - && isVendorApp(app.applicationInfo)) { - return true; - } - } - - return hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK) + // TODO : remove carryover package check in the future(b/31479477). All apps should just + // request the appropriate permission for their use case since android Q. + return isCarryoverPackage(app.applicationInfo) || isAppAllowedOnRestrictedNetworks(app) + || hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK) || hasPermission(app, NETWORK_STACK) || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS); } @@ -410,6 +465,20 @@ public class PermissionMonitor { mAllApps.add(UserHandle.getAppId(uid)); } + private Boolean highestUidNetworkPermission(int uid) { + Boolean permission = null; + final String[] packages = mPackageManager.getPackagesForUid(uid); + if (!CollectionUtils.isEmpty(packages)) { + for (String name : packages) { + permission = highestPermissionForUid(permission, name); + if (permission == SYSTEM) { + break; + } + } + } + return permission; + } + /** * Called when a package is removed. * @@ -440,19 +509,14 @@ public class PermissionMonitor { } Map<Integer, Boolean> apps = new HashMap<>(); - Boolean permission = null; - String[] packages = mPackageManager.getPackagesForUid(uid); - if (packages != null && packages.length > 0) { - for (String name : packages) { - permission = highestPermissionForUid(permission, name); - if (permission == SYSTEM) { - // An app with this UID still has the SYSTEM permission. - // Therefore, this UID must already have the SYSTEM permission. - // Nothing to do. - return; - } - } + final Boolean permission = highestUidNetworkPermission(uid); + if (permission == SYSTEM) { + // An app with this UID still has the SYSTEM permission. + // Therefore, this UID must already have the SYSTEM permission. + // Nothing to do. + return; } + if (permission == mApps.get(uid)) { // The permissions of this UID have not changed. Nothing to do. return; @@ -705,6 +769,38 @@ public class PermissionMonitor { return mVpnUidRanges.get(iface); } + private synchronized void onSettingChanged() { + // Step1. Update apps allowed to use restricted networks and compute the set of packages to + // update. + final Set<String> packagesToUpdate = new ArraySet<>(mAppsAllowedOnRestrictedNetworks); + updateAppsAllowedOnRestrictedNetworks(mDeps.getAppsAllowedOnRestrictedNetworks(mContext)); + packagesToUpdate.addAll(mAppsAllowedOnRestrictedNetworks); + + final Map<Integer, Boolean> updatedApps = new HashMap<>(); + final Map<Integer, Boolean> removedApps = new HashMap<>(); + + // Step2. For each package to update, find out its new permission. + for (String app : packagesToUpdate) { + final PackageInfo info = getPackageInfo(app); + if (info == null || info.applicationInfo == null) continue; + + final int uid = info.applicationInfo.uid; + final Boolean permission = highestUidNetworkPermission(uid); + + if (null == permission) { + removedApps.put(uid, NETWORK); // Doesn't matter which permission is set here. + mApps.remove(uid); + } else { + updatedApps.put(uid, permission); + mApps.put(uid, permission); + } + } + + // Step3. Update or revoke permission for uids with netd. + update(mUsers, updatedApps, true /* add */); + update(mUsers, removedApps, false /* add */); + } + /** Dump info to dumpsys */ public void dump(IndentingPrintWriter pw) { pw.println("Interface filtering rules:"); diff --git a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java b/packages/Connectivity/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java index dd2815d9e2e3..dd2815d9e2e3 100644 --- a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/ProxyTracker.java index f572b46a9b58..f572b46a9b58 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/ProxyTracker.java diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java index 534dbe7699a7..534dbe7699a7 100644 --- a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackTracker.java index b6ab47b276e3..b6ab47b276e3 100644 --- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackTracker.java diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/packages/Connectivity/service/src/com/android/server/connectivity/TcpKeepaliveController.java index c480594b8c60..c480594b8c60 100644 --- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/TcpKeepaliveController.java diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java index b178bad7123d..d74b802c8729 100644 --- a/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java +++ b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java @@ -340,7 +340,7 @@ public class NetworkCapabilitiesTest { private void testParcelSane(NetworkCapabilities cap) { if (isAtLeastS()) { - assertParcelSane(cap, 17); + assertParcelSane(cap, 16); } else if (isAtLeastR()) { assertParcelSane(cap, 15); } else { diff --git a/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt index 87cfb345e5e0..f23ba26d0039 100644 --- a/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt @@ -36,15 +36,15 @@ class UnderlyingNetworkInfoTest { @Test fun testParcelUnparcel() { val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST) - assertEquals(TEST_OWNER_UID, testInfo.ownerUid) - assertEquals(TEST_IFACE, testInfo.iface) - assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces) + assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid()) + assertEquals(TEST_IFACE, testInfo.getInterface()) + assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces()) assertParcelSane(testInfo, 3) val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf()) - assertEquals(0, emptyInfo.ownerUid) - assertEquals(String(), emptyInfo.iface) - assertEquals(listOf(), emptyInfo.underlyingIfaces) + assertEquals(0, emptyInfo.getOwnerUid()) + assertEquals(String(), emptyInfo.getInterface()) + assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces()) assertParcelSane(emptyInfo, 3) } }
\ No newline at end of file diff --git a/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt index ab6b2f409867..cb39a0c819f2 100644 --- a/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt @@ -40,7 +40,7 @@ import android.net.NetworkTemplate.OEM_MANAGED_YES import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT import android.net.NetworkTemplate.buildTemplateWifi import android.net.NetworkTemplate.buildTemplateWifiWildcard -import android.net.NetworkTemplate.buildTemplateCarrier +import android.net.NetworkTemplate.buildTemplateCarrierMetered import android.net.NetworkTemplate.buildTemplateMobileWithRatType import android.telephony.TelephonyManager import com.android.testutils.assertParcelSane @@ -73,11 +73,12 @@ class NetworkTemplateTest { type: Int, subscriberId: String? = null, ssid: String? = null, - oemManaged: Int = OEM_NONE + oemManaged: Int = OEM_NONE, + metered: Boolean = true ): NetworkStateSnapshot { val lp = LinkProperties() val caps = NetworkCapabilities().apply { - setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !metered) setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) setSSID(ssid) setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, @@ -167,25 +168,38 @@ class NetworkTemplateTest { } @Test - fun testCarrierMatches() { - val templateCarrierImsi1 = buildTemplateCarrier(TEST_IMSI1) - - val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identMobile2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identWifiSsid1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) - val identCarrierWifiImsi1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0) - val identCarrierWifiImsi2 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0) - - templateCarrierImsi1.assertMatches(identCarrierWifiImsi1) - templateCarrierImsi1.assertDoesNotMatch(identCarrierWifiImsi2) - templateCarrierImsi1.assertDoesNotMatch(identWifiSsid1) - templateCarrierImsi1.assertMatches(identMobile1) - templateCarrierImsi1.assertDoesNotMatch(identMobile2) + fun testCarrierMeteredMatches() { + val templateCarrierImsi1Metered = buildTemplateCarrierMetered(TEST_IMSI1) + + val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1) + val mobileImsi1Unmetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI1, null /* ssid */, + OEM_NONE, false /* metered */) + val mobileImsi2 = buildMobileNetworkState(TEST_IMSI2) + val wifiSsid1 = buildWifiNetworkState(null /* subscriberId */, TEST_SSID1) + val wifiImsi1Ssid1 = buildWifiNetworkState(TEST_IMSI1, TEST_SSID1) + val wifiImsi1Ssid1Unmetered = buildNetworkState(TYPE_WIFI, TEST_IMSI1, TEST_SSID1, + OEM_NONE, false /* metered */) + + val identMobileImsi1Metered = buildNetworkIdentity(mockContext, + mobileImsi1, false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS) + val identMobileImsi1Unmetered = buildNetworkIdentity(mockContext, + mobileImsi1Unmetered, false /* defaultNetwork */, + TelephonyManager.NETWORK_TYPE_UMTS) + val identMobileImsi2Metered = buildNetworkIdentity(mockContext, + mobileImsi2, false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS) + val identWifiSsid1Metered = buildNetworkIdentity( + mockContext, wifiSsid1, true /* defaultNetwork */, 0 /* subType */) + val identCarrierWifiImsi1Metered = buildNetworkIdentity( + mockContext, wifiImsi1Ssid1, true /* defaultNetwork */, 0 /* subType */) + val identCarrierWifiImsi1NonMetered = buildNetworkIdentity(mockContext, + wifiImsi1Ssid1Unmetered, true /* defaultNetwork */, 0 /* subType */) + + templateCarrierImsi1Metered.assertMatches(identMobileImsi1Metered) + templateCarrierImsi1Metered.assertDoesNotMatch(identMobileImsi1Unmetered) + templateCarrierImsi1Metered.assertDoesNotMatch(identMobileImsi2Metered) + templateCarrierImsi1Metered.assertDoesNotMatch(identWifiSsid1Metered) + templateCarrierImsi1Metered.assertMatches(identCarrierWifiImsi1Metered) + templateCarrierImsi1Metered.assertDoesNotMatch(identCarrierWifiImsi1NonMetered) } @Test diff --git a/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java index ad58960eaadd..40f8f1b8d09a 100644 --- a/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java @@ -35,7 +35,7 @@ public class QosSocketFilterTest { public void testPortExactMatch() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertTrue(QosSocketFilter.matchesLocalAddress( + assertTrue(QosSocketFilter.matchesAddress( new InetSocketAddress(addressA, 10), addressB, 10, 10)); } @@ -44,7 +44,7 @@ public class QosSocketFilterTest { public void testPortLessThanStart() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertFalse(QosSocketFilter.matchesLocalAddress( + assertFalse(QosSocketFilter.matchesAddress( new InetSocketAddress(addressA, 8), addressB, 10, 10)); } @@ -52,7 +52,7 @@ public class QosSocketFilterTest { public void testPortGreaterThanEnd() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertFalse(QosSocketFilter.matchesLocalAddress( + assertFalse(QosSocketFilter.matchesAddress( new InetSocketAddress(addressA, 18), addressB, 10, 10)); } @@ -60,7 +60,7 @@ public class QosSocketFilterTest { public void testPortBetweenStartAndEnd() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertTrue(QosSocketFilter.matchesLocalAddress( + assertTrue(QosSocketFilter.matchesAddress( new InetSocketAddress(addressA, 10), addressB, 8, 18)); } @@ -68,7 +68,7 @@ public class QosSocketFilterTest { public void testAddressesDontMatch() { final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5"); - assertFalse(QosSocketFilter.matchesLocalAddress( + assertFalse(QosSocketFilter.matchesAddress( new InetSocketAddress(addressA, 10), addressB, 10, 10)); } } diff --git a/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java index ab5079820611..41458f16baa4 100644 --- a/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -19,6 +19,7 @@ package com.android.server; import static android.Manifest.permission.CHANGE_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.DUMP; +import static android.Manifest.permission.LOCAL_MAC_ADDRESS; import static android.Manifest.permission.NETWORK_FACTORY; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.app.PendingIntent.FLAG_IMMUTABLE; @@ -126,6 +127,7 @@ import static com.android.testutils.MiscAsserts.assertContainsExactly; import static com.android.testutils.MiscAsserts.assertEmpty; import static com.android.testutils.MiscAsserts.assertLength; import static com.android.testutils.MiscAsserts.assertRunsInAtMost; +import static com.android.testutils.MiscAsserts.assertSameElements; import static com.android.testutils.MiscAsserts.assertThrows; import static org.junit.Assert.assertEquals; @@ -5805,20 +5807,8 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(networkCallback); } - private <T> void assertSameElementsNoDuplicates(T[] expected, T[] actual) { - // Easier to implement than a proper "assertSameElements" method that also correctly deals - // with duplicates. - final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual); - assertEquals(msg, expected.length, actual.length); - Set expectedSet = new ArraySet<>(Arrays.asList(expected)); - assertEquals("expected contains duplicates", expectedSet.size(), expected.length); - // actual cannot have duplicates because it's the same length and has the same elements. - Set actualSet = new ArraySet<>(Arrays.asList(actual)); - assertEquals(expectedSet, actualSet); - } - - private void expectNetworkStatus(Network[] networks, String defaultIface, - Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception { + private void expectNotifyNetworkStatus(List<Network> networks, String defaultIface, + Integer vpnUid, String vpnIfname, List<String> underlyingIfaces) throws Exception { ArgumentCaptor<List<Network>> networksCaptor = ArgumentCaptor.forClass(List.class); ArgumentCaptor<List<UnderlyingNetworkInfo>> vpnInfosCaptor = ArgumentCaptor.forClass(List.class); @@ -5826,26 +5816,24 @@ public class ConnectivityServiceTest { verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(), any(List.class), eq(defaultIface), vpnInfosCaptor.capture()); - assertSameElementsNoDuplicates(networksCaptor.getValue().toArray(), networks); + assertSameElements(networksCaptor.getValue(), networks); - UnderlyingNetworkInfo[] infos = - vpnInfosCaptor.getValue().toArray(new UnderlyingNetworkInfo[0]); + List<UnderlyingNetworkInfo> infos = vpnInfosCaptor.getValue(); if (vpnUid != null) { - assertEquals("Should have exactly one VPN:", 1, infos.length); - UnderlyingNetworkInfo info = infos[0]; + assertEquals("Should have exactly one VPN:", 1, infos.size()); + UnderlyingNetworkInfo info = infos.get(0); assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid()); - assertEquals("Unexpected VPN interface:", vpnIfname, info.getIface()); - assertSameElementsNoDuplicates(underlyingIfaces, - info.getUnderlyingIfaces().toArray(new String[0])); + assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface()); + assertSameElements(underlyingIfaces, info.getUnderlyingInterfaces()); } else { - assertEquals(0, infos.length); + assertEquals(0, infos.size()); return; } } - private void expectNetworkStatus( - Network[] networks, String defaultIface) throws Exception { - expectNetworkStatus(networks, defaultIface, null, null, new String[0]); + private void expectNotifyNetworkStatus( + List<Network> networks, String defaultIface) throws Exception { + expectNotifyNetworkStatus(networks, defaultIface, null, null, List.of()); } @Test @@ -5853,8 +5841,8 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()}; - final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()}; + final List<Network> onlyCell = List.of(mCellNetworkAgent.getNetwork()); + final List<Network> onlyWifi = List.of(mWiFiNetworkAgent.getNetwork()); LinkProperties cellLp = new LinkProperties(); cellLp.setInterfaceName(MOBILE_IFNAME); @@ -5865,7 +5853,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(false); mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); + expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME); reset(mStatsManager); // Default network switch should update ifaces. @@ -5873,37 +5861,37 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.sendLinkProperties(wifiLp); waitForIdle(); assertEquals(wifiLp, mService.getActiveLinkProperties()); - expectNetworkStatus(onlyWifi, WIFI_IFNAME); + expectNotifyNetworkStatus(onlyWifi, WIFI_IFNAME); reset(mStatsManager); // Disconnect should update ifaces. mWiFiNetworkAgent.disconnect(); waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); + expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME); reset(mStatsManager); // Metered change should update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); + expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME); reset(mStatsManager); mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); + expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME); reset(mStatsManager); // Temp metered change shouldn't update ifaces mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); waitForIdle(); - verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)), + verify(mStatsManager, never()).notifyNetworkStatus(eq(onlyCell), any(List.class), eq(MOBILE_IFNAME), any(List.class)); reset(mStatsManager); // Roaming change should update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); + expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME); reset(mStatsManager); // Test VPNs. @@ -5913,29 +5901,29 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(lp); assertUidRangesUpdatedForMyUid(true); - final Network[] cellAndVpn = new Network[] { - mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; + final List<Network> cellAndVpn = + List.of(mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()); // A VPN with default (null) underlying networks sets the underlying network's interfaces... - expectNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME}); + expectNotifyNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + List.of(MOBILE_IFNAME)); // ...and updates them as the default network switches. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); mWiFiNetworkAgent.sendLinkProperties(wifiLp); final Network[] onlyNull = new Network[]{null}; - final Network[] wifiAndVpn = new Network[] { - mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; - final Network[] cellAndWifi = new Network[] { - mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()}; - final Network[] cellNullAndWifi = new Network[] { - mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()}; + final List<Network> wifiAndVpn = + List.of(mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()); + final List<Network> cellAndWifi = + List.of(mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()); + final Network[] cellNullAndWifi = + new Network[]{mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()}; waitForIdle(); assertEquals(wifiLp, mService.getActiveLinkProperties()); - expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{WIFI_IFNAME}); + expectNotifyNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, + List.of(WIFI_IFNAME)); reset(mStatsManager); // A VPN that sets its underlying networks passes the underlying interfaces, and influences @@ -5944,23 +5932,23 @@ public class ConnectivityServiceTest { // MOBILE_IFNAME even though the default network is wifi. // TODO: fix this to pass in the actual default network interface. Whether or not the VPN // applies to the system server UID should not have any bearing on network stats. - mMockVpn.setUnderlyingNetworks(onlyCell); + mMockVpn.setUnderlyingNetworks(onlyCell.toArray(new Network[0])); waitForIdle(); - expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME}); + expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + List.of(MOBILE_IFNAME)); reset(mStatsManager); - mMockVpn.setUnderlyingNetworks(cellAndWifi); + mMockVpn.setUnderlyingNetworks(cellAndWifi.toArray(new Network[0])); waitForIdle(); - expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + List.of(MOBILE_IFNAME, WIFI_IFNAME)); reset(mStatsManager); // Null underlying networks are ignored. mMockVpn.setUnderlyingNetworks(cellNullAndWifi); waitForIdle(); - expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + List.of(MOBILE_IFNAME, WIFI_IFNAME)); reset(mStatsManager); // If an underlying network disconnects, that interface should no longer be underlying. @@ -5973,15 +5961,15 @@ public class ConnectivityServiceTest { mCellNetworkAgent.disconnect(); waitForIdle(); assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork())); - expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + List.of(MOBILE_IFNAME, WIFI_IFNAME)); // Confirm that we never tell NetworkStatsService that cell is no longer the underlying // network for the VPN... verify(mStatsManager, never()).notifyNetworkStatus(any(List.class), any(List.class), any() /* anyString() doesn't match null */, - argThat(infos -> infos.get(0).getUnderlyingIfaces().size() == 1 - && WIFI_IFNAME.equals(infos.get(0).getUnderlyingIfaces().get(0)))); + argThat(infos -> infos.get(0).getUnderlyingInterfaces().size() == 1 + && WIFI_IFNAME.equals(infos.get(0).getUnderlyingInterfaces().get(0)))); verifyNoMoreInteractions(mStatsManager); reset(mStatsManager); @@ -5994,8 +5982,8 @@ public class ConnectivityServiceTest { waitForIdle(); verify(mStatsManager).notifyNetworkStatus(any(List.class), any(List.class), any() /* anyString() doesn't match null */, - argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingIfaces().size() == 1 - && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingIfaces().get(0)))); + argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingInterfaces().size() == 1 + && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingInterfaces().get(0)))); mEthernetNetworkAgent.disconnect(); waitForIdle(); reset(mStatsManager); @@ -6008,26 +5996,26 @@ public class ConnectivityServiceTest { // Also, for the same reason as above, the active interface passed in is null. mMockVpn.setUnderlyingNetworks(new Network[0]); waitForIdle(); - expectNetworkStatus(wifiAndVpn, null); + expectNotifyNetworkStatus(wifiAndVpn, null); reset(mStatsManager); // Specifying only a null underlying network is the same as no networks. mMockVpn.setUnderlyingNetworks(onlyNull); waitForIdle(); - expectNetworkStatus(wifiAndVpn, null); + expectNotifyNetworkStatus(wifiAndVpn, null); reset(mStatsManager); // Specifying networks that are all disconnected is the same as specifying no networks. - mMockVpn.setUnderlyingNetworks(onlyCell); + mMockVpn.setUnderlyingNetworks(onlyCell.toArray(new Network[0])); waitForIdle(); - expectNetworkStatus(wifiAndVpn, null); + expectNotifyNetworkStatus(wifiAndVpn, null); reset(mStatsManager); // Passing in null again means follow the default network again. mMockVpn.setUnderlyingNetworks(null); waitForIdle(); - expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{WIFI_IFNAME}); + expectNotifyNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, + List.of(WIFI_IFNAME)); reset(mStatsManager); } @@ -9407,9 +9395,9 @@ public class ConnectivityServiceTest { @Override public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) { return new TestTransportInfo( - (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0, - (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0, - (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0 + locationRedacted | (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0, + localMacAddressRedacted | (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0, + settingsRedacted | (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0 ); } @@ -9432,8 +9420,26 @@ public class ConnectivityServiceTest { public int hashCode() { return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted); } + + @Override + public String toString() { + return String.format( + "TestTransportInfo{locationRedacted=%s macRedacted=%s settingsRedacted=%s}", + locationRedacted, localMacAddressRedacted, settingsRedacted); + } + } + + private TestTransportInfo getTestTransportInfo(NetworkCapabilities nc) { + return (TestTransportInfo) nc.getTransportInfo(); + } + + private TestTransportInfo getTestTransportInfo(TestNetworkAgentWrapper n) { + final NetworkCapabilities nc = mCm.getNetworkCapabilities(n.getNetwork()); + assertNotNull(nc); + return getTestTransportInfo(nc); } + private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps( @NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid, @NonNull TransportInfo actualTransportInfo, int expectedOwnerUid, @@ -9462,7 +9468,6 @@ public class ConnectivityServiceTest { wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent, nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid()) && Objects.equals(expectedTransportInfo, nc.getTransportInfo())); - } @Test @@ -9483,6 +9488,40 @@ public class ConnectivityServiceTest { wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo); } + @Test + public void testTransportInfoRedactionInSynchronousCalls() throws Exception { + final NetworkCapabilities ncTemplate = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .setTransportInfo(new TestTransportInfo()); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), + ncTemplate); + mWiFiNetworkAgent.connect(true /* validated; waits for callback */); + + // NETWORK_SETTINGS redaction is controlled by the NETWORK_SETTINGS permission + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + withPermission(NETWORK_SETTINGS, () -> { + assertFalse(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + }); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + + // LOCAL_MAC_ADDRESS redaction is controlled by the LOCAL_MAC_ADDRESS permission + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + withPermission(LOCAL_MAC_ADDRESS, () -> { + assertFalse(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + }); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + + // Synchronous getNetworkCapabilities calls never return unredacted location-sensitive + // information. + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + denyAllLocationPrivilegedPermissions(); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + } + private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE); @@ -9840,12 +9879,27 @@ public class ConnectivityServiceTest { // Connect the cell agent verify that it notifies TestNetworkCallback that it is available final TestNetworkCallback callback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(callback); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + + final NetworkCapabilities ncTemplate = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .setTransportInfo(new TestTransportInfo()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), + ncTemplate); mCellNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); callback.assertNoCallback(); } + private boolean areConnDiagCapsRedacted(NetworkCapabilities nc) { + TestTransportInfo ti = (TestTransportInfo) nc.getTransportInfo(); + return nc.getUids() == null + && nc.getAdministratorUids().length == 0 + && nc.getOwnerUid() == Process.INVALID_UID + && getTestTransportInfo(nc).locationRedacted + && getTestTransportInfo(nc).localMacAddressRedacted + && getTestTransportInfo(nc).settingsRedacted; + } + @Test public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() throws Exception { @@ -9856,12 +9910,7 @@ public class ConnectivityServiceTest { // Verify onConnectivityReport fired verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable( - argThat(report -> { - final NetworkCapabilities nc = report.getNetworkCapabilities(); - return nc.getUids() == null - && nc.getAdministratorUids().length == 0 - && nc.getOwnerUid() == Process.INVALID_UID; - })); + argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities()))); } @Test @@ -9877,12 +9926,7 @@ public class ConnectivityServiceTest { // Verify onDataStallSuspected fired verify(mConnectivityDiagnosticsCallback).onDataStallSuspected( - argThat(report -> { - final NetworkCapabilities nc = report.getNetworkCapabilities(); - return nc.getUids() == null - && nc.getAdministratorUids().length == 0 - && nc.getOwnerUid() == Process.INVALID_UID; - })); + argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities()))); } @Test diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java index 02a58080fefd..c75618f43cde 100644 --- a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -30,6 +30,8 @@ import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.net.ConnectivitySettingsManager.APPS_ALLOWED_ON_RESTRICTED_NETWORKS; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.os.Process.SYSTEM_UID; import static com.android.server.connectivity.PermissionMonitor.NETWORK; @@ -43,8 +45,10 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalMatchers.aryEq; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; @@ -61,6 +65,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.database.ContentObserver; import android.net.INetd; import android.net.UidRange; import android.net.Uri; @@ -68,6 +73,7 @@ import android.os.Build; import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; +import android.util.ArraySet; import android.util.SparseIntArray; import androidx.test.InstrumentationRegistry; @@ -136,6 +142,7 @@ public class PermissionMonitorTest { final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext)); doReturn(UserHandle.ALL).when(asUserCtx).getUser(); when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); + when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>()); mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); @@ -145,8 +152,15 @@ public class PermissionMonitorTest { private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid, String... permissions) { + return hasRestrictedNetworkPermission( + partition, targetSdkVersion, "" /* packageName */, uid, permissions); + } + + private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, + String packageName, int uid, String... permissions) { final PackageInfo packageInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, permissions, partition); + packageInfo.packageName = packageName; packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; packageInfo.applicationInfo.uid = uid; return mPermissionMonitor.hasRestrictedNetworkPermission(packageInfo); @@ -280,6 +294,8 @@ public class PermissionMonitorTest { PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); assertFalse(hasRestrictedNetworkPermission( PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, MOCK_UID1, PERMISSION_MAINLINE_NETWORK_STACK)); assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); assertFalse(hasRestrictedNetworkPermission( @@ -324,6 +340,90 @@ public class PermissionMonitorTest { PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE)); } + @Test + public void testHasRestrictedNetworkPermissionAppAllowedOnRestrictedNetworks() { + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks( + new ArraySet<>(new String[] { MOCK_PACKAGE1 })); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL)); + + assertFalse(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID1, CONNECTIVITY_INTERNAL)); + + } + + private boolean wouldBeCarryoverPackage(String partition, int targetSdkVersion, int uid) { + final PackageInfo packageInfo = packageInfoWithPermissions( + REQUESTED_PERMISSION_GRANTED, new String[] {}, partition); + packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; + packageInfo.applicationInfo.uid = uid; + return mPermissionMonitor.isCarryoverPackage(packageInfo.applicationInfo); + } + + @Test + public void testIsCarryoverPackage() { + doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt(); + assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); + + doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); + + assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, SYSTEM_UID)); + assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID1)); + assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1)); + } + + private boolean wouldBeAppAllowedOnRestrictedNetworks(String packageName) { + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = packageName; + return mPermissionMonitor.isAppAllowedOnRestrictedNetworks(packageInfo); + } + + @Test + public void testIsAppAllowedOnRestrictedNetworks() { + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks(new ArraySet<>()); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1)); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2)); + + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks( + new ArraySet<>(new String[] { MOCK_PACKAGE1 })); + assertTrue(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1)); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2)); + + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks( + new ArraySet<>(new String[] { MOCK_PACKAGE2 })); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1)); + assertTrue(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2)); + + mPermissionMonitor.updateAppsAllowedOnRestrictedNetworks( + new ArraySet<>(new String[] { "com.android.test" })); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE1)); + assertFalse(wouldBeAppAllowedOnRestrictedNetworks(MOCK_PACKAGE2)); + } + private void assertBackgroundPermission(boolean hasPermission, String name, int uid, String... permissions) throws Exception { when(mPackageManager.getPackageInfo(eq(name), anyInt())) @@ -800,4 +900,102 @@ public class PermissionMonitorTest { mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 }); } -} + @Test + public void testAppsAllowedOnRestrictedNetworksChanged() throws Exception { + final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService); + final ArgumentCaptor<ContentObserver> captor = + ArgumentCaptor.forClass(ContentObserver.class); + verify(mDeps, times(1)).registerContentObserver(any(), + argThat(uri -> uri.getEncodedPath().contains(APPS_ALLOWED_ON_RESTRICTED_NETWORKS)), + anyBoolean(), captor.capture()); + final ContentObserver contentObserver = captor.getValue(); + + mPermissionMonitor.onUserAdded(MOCK_USER1); + // Prepare PackageInfo for MOCK_PACKAGE1 + final PackageInfo packageInfo = buildPackageInfo( + false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1); + packageInfo.packageName = MOCK_PACKAGE1; + when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), anyInt())).thenReturn(packageInfo); + when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{MOCK_PACKAGE1}); + // Prepare PackageInfo for MOCK_PACKAGE2 + final PackageInfo packageInfo2 = buildPackageInfo( + false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1); + packageInfo2.packageName = MOCK_PACKAGE2; + when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2); + when(mPackageManager.getPackagesForUid(MOCK_UID2)).thenReturn(new String[]{MOCK_PACKAGE2}); + + // MOCK_PACKAGE1 is listed in setting that allow to use restricted networks, MOCK_UID1 + // should have SYSTEM permission. + when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn( + new ArraySet<>(new String[] { MOCK_PACKAGE1 })); + contentObserver.onChange(true /* selfChange */); + mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2}); + + // MOCK_PACKAGE2 is listed in setting that allow to use restricted networks, MOCK_UID2 + // should have SYSTEM permission but MOCK_UID1 should revoke permission. + when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn( + new ArraySet<>(new String[] { MOCK_PACKAGE2 })); + contentObserver.onChange(true /* selfChange */); + mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2}); + mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + + // No app lists in setting, should revoke permission from all uids. + when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>()); + contentObserver.onChange(true /* selfChange */); + mNetdMonitor.expectNoPermission( + new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1, MOCK_UID2}); + } + + @Test + public void testAppsAllowedOnRestrictedNetworksChangedWithSharedUid() throws Exception { + final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService); + final ArgumentCaptor<ContentObserver> captor = + ArgumentCaptor.forClass(ContentObserver.class); + verify(mDeps, times(1)).registerContentObserver(any(), + argThat(uri -> uri.getEncodedPath().contains(APPS_ALLOWED_ON_RESTRICTED_NETWORKS)), + anyBoolean(), captor.capture()); + final ContentObserver contentObserver = captor.getValue(); + + mPermissionMonitor.onUserAdded(MOCK_USER1); + // Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2 with shared uid MOCK_UID1. + final PackageInfo packageInfo = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE); + packageInfo.applicationInfo.uid = MOCK_USER1.getUid(MOCK_UID1); + packageInfo.packageName = MOCK_PACKAGE1; + final PackageInfo packageInfo2 = buildPackageInfo( + false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1); + packageInfo2.packageName = MOCK_PACKAGE2; + when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), anyInt())).thenReturn(packageInfo); + when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2); + when(mPackageManager.getPackagesForUid(MOCK_UID1)) + .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2}); + + // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID1 should have NETWORK permission. + addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1); + mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + + // MOCK_PACKAGE2 is listed in setting that allow to use restricted networks, MOCK_UID1 + // should upgrade to SYSTEM permission. + when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn( + new ArraySet<>(new String[] { MOCK_PACKAGE2 })); + contentObserver.onChange(true /* selfChange */); + mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + + // MOCK_PACKAGE1 is listed in setting that allow to use restricted networks, MOCK_UID1 + // should still have SYSTEM permission. + when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn( + new ArraySet<>(new String[] { MOCK_PACKAGE1 })); + contentObserver.onChange(true /* selfChange */); + mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + + // No app lists in setting, MOCK_UID1 should downgrade to NETWORK permission. + when(mDeps.getAppsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>()); + contentObserver.onChange(true /* selfChange */); + mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + + // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID1. + when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{MOCK_PACKAGE2}); + removePackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1); + mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1}); + } +}
\ No newline at end of file diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java index f3ae9b051e7c..93599f3c376d 100644 --- a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java @@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.tests.net.R; +import com.android.internal.util.test.FsUtil; import libcore.io.IoUtils; import libcore.io.Streams; @@ -71,7 +72,7 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { public void setUp() throws Exception { mTestProc = new File(InstrumentationRegistry.getContext().getFilesDir(), "proc"); if (mTestProc.exists()) { - IoUtils.deleteContents(mTestProc); + FsUtil.deleteContents(mTestProc); } // The libandroid_servers which have the native method is not available to @@ -87,7 +88,7 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { mFactory = null; if (mTestProc.exists()) { - IoUtils.deleteContents(mTestProc); + FsUtil.deleteContents(mTestProc); } } diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java index fd374bc9e68f..0ba5f7d8241e 100644 --- a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java @@ -112,13 +112,12 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ArrayUtils; import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.internal.util.test.FsUtil; import com.android.server.net.NetworkStatsService.NetworkStatsSettings; import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config; import com.android.testutils.HandlerUtils; import com.android.testutils.TestableNetworkStatsProviderBinder; -import libcore.io.IoUtils; - import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -152,6 +151,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private static final String TEST_SSID = "AndroidAP"; private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID); + private static NetworkTemplate sTemplateCarrierWifi1 = + buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL, IMSI_1); private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); @@ -213,7 +214,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mServiceContext = new MockContext(context); mStatsDir = context.getFilesDir(); if (mStatsDir.exists()) { - IoUtils.deleteContents(mStatsDir); + FsUtil.deleteContents(mStatsDir); } PowerManager powerManager = (PowerManager) mServiceContext.getSystemService( @@ -283,7 +284,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { @After public void tearDown() throws Exception { - IoUtils.deleteContents(mStatsDir); + FsUtil.deleteContents(mStatsDir); mServiceContext = null; mStatsDir = null; @@ -297,45 +298,82 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mHandlerThread.quitSafely(); } - @Test - public void testNetworkStatsWifi() throws Exception { + private void initWifiStats(NetworkStateSnapshot snapshot) throws Exception { // pretend that wifi network comes online; service should ask about full // network state, and poll any existing interfaces before updating. expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {snapshot}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); + } - // verify service has empty history for wifi - assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); - - // modify some number on wifi, and trigger poll event - incrementCurrentTime(HOUR_IN_MILLIS); + private void incrementWifiStats(long durationMillis, String iface, + long rxb, long rxp, long txb, long txp) throws Exception { + incrementCurrentTime(durationMillis); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L)); + .insertEntry(iface, rxb, rxp, txb, txp)); expectNetworkStatsUidDetail(buildEmptyStats()); forcePollAndWaitForIdle(); + } + + @Test + public void testNetworkStatsCarrierWifi() throws Exception { + initWifiStats(buildWifiState(true, TEST_IFACE, IMSI_1)); + // verify service has empty history for carrier merged wifi and non-carrier wifi + assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0); + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + + // modify some number on wifi, and trigger poll event + incrementWifiStats(HOUR_IN_MILLIS, TEST_IFACE, 1024L, 1L, 2048L, 2L); // verify service recorded history - assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); + assertNetworkTotal(sTemplateCarrierWifi1, 1024L, 1L, 2048L, 2L, 0); + + // verify service recorded history for wifi with SSID filter + assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); // and bump forward again, with counters going higher. this is // important, since polling should correctly subtract last snapshot. - incrementCurrentTime(DAY_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L)); - expectNetworkStatsUidDetail(buildEmptyStats()); - forcePollAndWaitForIdle(); + incrementWifiStats(DAY_IN_MILLIS, TEST_IFACE, 4096L, 4L, 8192L, 8L); // verify service recorded history + assertNetworkTotal(sTemplateCarrierWifi1, 4096L, 4L, 8192L, 8L, 0); + // verify service recorded history for wifi with SSID filter assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0); + } + + @Test + public void testNetworkStatsNonCarrierWifi() throws Exception { + initWifiStats(buildWifiState()); + + // verify service has empty history for wifi + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + // verify service has empty history for carrier merged wifi + assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0); + + // modify some number on wifi, and trigger poll event + incrementWifiStats(HOUR_IN_MILLIS, TEST_IFACE, 1024L, 1L, 2048L, 2L); + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); + // verify service has empty history for carrier wifi since current network is non carrier + // wifi + assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0); + + // and bump forward again, with counters going higher. this is + // important, since polling should correctly subtract last snapshot. + incrementWifiStats(DAY_IN_MILLIS, TEST_IFACE, 4096L, 4L, 8192L, 8L); + + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0); + // verify service has empty history for carrier wifi since current network is non carrier + // wifi + assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0); } @Test @@ -349,7 +387,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // verify service has empty history for wifi @@ -423,7 +461,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // modify some number on wifi, and trigger poll event @@ -464,7 +502,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // create some traffic on first network @@ -499,7 +537,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); forcePollAndWaitForIdle(); @@ -539,7 +577,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // create some traffic @@ -607,7 +645,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsUidDetail(buildEmptyStats()); setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Create some traffic. @@ -699,7 +737,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PAID})}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Create some traffic. @@ -714,7 +752,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE})}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Create some traffic. @@ -730,7 +768,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { NetworkCapabilities.NET_CAPABILITY_OEM_PAID})}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Create some traffic. @@ -744,7 +782,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false, new int[]{})}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Create some traffic. @@ -797,7 +835,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // create some traffic for two apps @@ -856,7 +894,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); NetworkStats.Entry entry1 = new NetworkStats.Entry( @@ -900,7 +938,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); NetworkStats.Entry uidStats = new NetworkStats.Entry( @@ -931,7 +969,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // mStatsFactory#readNetworkStatsDetail() has the following invocations: // 1) NetworkStatsService#systemReady from #setUp. - // 2) mService#forceUpdateIfaces in the test above. + // 2) mService#notifyNetworkStatus in the test above. // // Additionally, we should have one call from the above call to mService#getDetailedUidStats // with the augmented ifaceFilter. @@ -955,7 +993,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // create some initial traffic @@ -1013,7 +1051,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // create some initial traffic @@ -1053,7 +1091,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Create some traffic @@ -1092,7 +1130,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // create some tethering traffic @@ -1149,7 +1187,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // verify service has empty history for wifi @@ -1255,7 +1293,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mService.registerNetworkStatsProvider("TEST", provider); assertNotNull(cb); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Verifies that one requestStatsUpdate will be called during iface update. @@ -1320,7 +1358,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mService.registerNetworkStatsProvider("TEST", provider); assertNotNull(cb); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Verifies that one requestStatsUpdate will be called during iface update. @@ -1378,7 +1416,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{buildWifiState(true /* isMetered */, TEST_IFACE)}; - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Register custom provider and retrieve callback. @@ -1428,7 +1466,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // 3G network comes online. setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Create some traffic. @@ -1450,7 +1488,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { setCombineSubtypeEnabled(true); // Call handleOnCollapsedRatTypeChanged manually to simulate the callback fired - // when stopping monitor, this is needed by NetworkStatsService to trigger updateIfaces. + // when stopping monitor, this is needed by NetworkStatsService to trigger + // handleNotifyNetworkStatus. mService.handleOnCollapsedRatTypeChanged(); HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); // Create some traffic. @@ -1499,7 +1538,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)}; expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); // Create some traffic on mobile network. @@ -1661,10 +1700,15 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } private static NetworkStateSnapshot buildWifiState() { - return buildWifiState(false, TEST_IFACE); + return buildWifiState(false, TEST_IFACE, null); } private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface) { + return buildWifiState(isMetered, iface, null); + } + + private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface, + String subscriberId) { final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(iface); final NetworkCapabilities capabilities = new NetworkCapabilities(); @@ -1672,7 +1716,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); capabilities.setSSID(TEST_SSID); - return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, null, TYPE_WIFI); + return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, subscriberId, TYPE_WIFI); } private static NetworkStateSnapshot buildMobile3gState(String subscriberId) { diff --git a/packages/CtsShim/OWNERS b/packages/CtsShim/OWNERS index ba9f2b97678a..94197715150d 100644 --- a/packages/CtsShim/OWNERS +++ b/packages/CtsShim/OWNERS @@ -1,2 +1,3 @@ ioffe@google.com -toddke@google.com
\ No newline at end of file +toddke@google.com +patb@google.com
\ No newline at end of file diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS index 8e1774b0baa2..c6331133367a 100644 --- a/packages/PackageInstaller/OWNERS +++ b/packages/PackageInstaller/OWNERS @@ -1,5 +1,6 @@ svetoslavganov@google.com toddke@google.com +patb@google.com suprabh@google.com # For automotive related changes diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS index cf9799c6a026..6c61d4b91d36 100644 --- a/packages/SettingsProvider/OWNERS +++ b/packages/SettingsProvider/OWNERS @@ -4,3 +4,4 @@ narayan@google.com svetoslavganov@google.com schfan@google.com toddke@google.com +patb@google.com diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 27df92f0832b..2a12ce2854b6 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -467,6 +467,9 @@ <!-- Permission required for CTS test - CtsAlarmManagerTestCases --> <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> + <!-- Permission required for CTS test - GlobalSearchSessionPlatformCtsTests --> + <uses-permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS index 6d738f8d4d43..177f86b08864 100644 --- a/packages/Shell/OWNERS +++ b/packages/Shell/OWNERS @@ -7,6 +7,7 @@ svetoslavganov@google.com hackbod@google.com yamasani@google.com toddke@google.com +patb@google.com cbrubaker@google.com omakoto@google.com michaelwr@google.com diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 5a525974f3cb..9e603561acf1 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -114,7 +114,7 @@ class ControlsUiControllerImpl @Inject constructor ( private val onSeedingComplete = Consumer<Boolean> { accepted -> if (accepted) { - selectedStructure = controlsController.get().getFavorites().maxBy { + selectedStructure = controlsController.get().getFavorites().maxByOrNull { it.controls.size } ?: EMPTY_STRUCTURE updatePreferences(selectedStructure) diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt index 1d2e74703b42..eec69f98b9be 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt @@ -28,7 +28,7 @@ class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyIt appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType }) .toList() .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps - { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest) + { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest) types = itemsList.map { it.privacyType }.distinct().sorted() } diff --git a/services/OWNERS b/services/OWNERS index 03e0807eea62..3b972e922e95 100644 --- a/services/OWNERS +++ b/services/OWNERS @@ -3,4 +3,4 @@ per-file Android.bp = file:platform/build/soong:/OWNERS # art-team@ manages the system server profile per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com -per-file java/com/android/server/* = toddke@google.com +per-file java/com/android/server/* = toddke@google.com,patb@google.com diff --git a/services/core/Android.bp b/services/core/Android.bp index 0ac8f74ff831..706f738d754d 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -10,7 +10,6 @@ package { filegroup { name: "services.core-sources", srcs: ["java/**/*.java"], - exclude_srcs: [":connectivity-service-srcs"], path: "java", visibility: ["//frameworks/base/services"], } @@ -200,29 +199,3 @@ prebuilt_etc { src: ":services.core.json.gz", } -// TODO: Move connectivity service sources to independent directory. -filegroup { - name: "connectivity-service-srcs", - srcs: [ - "java/com/android/server/ConnectivityService.java", - "java/com/android/server/ConnectivityServiceInitializer.java", - "java/com/android/server/TestNetworkService.java", - "java/com/android/server/connectivity/AutodestructReference.java", - "java/com/android/server/connectivity/ConnectivityConstants.java", - "java/com/android/server/connectivity/DnsManager.java", - "java/com/android/server/connectivity/FullScore.java", - "java/com/android/server/connectivity/KeepaliveTracker.java", - "java/com/android/server/connectivity/LingerMonitor.java", - "java/com/android/server/connectivity/MockableSystemProperties.java", - "java/com/android/server/connectivity/Nat464Xlat.java", - "java/com/android/server/connectivity/NetworkAgentInfo.java", - "java/com/android/server/connectivity/NetworkDiagnostics.java", - "java/com/android/server/connectivity/NetworkNotificationManager.java", - "java/com/android/server/connectivity/NetworkRanker.java", - "java/com/android/server/connectivity/PermissionMonitor.java", - "java/com/android/server/connectivity/ProxyTracker.java", - "java/com/android/server/connectivity/QosCallbackAgentConnection.java", - "java/com/android/server/connectivity/QosCallbackTracker.java", - "java/com/android/server/connectivity/TcpKeepaliveController.java", - ], -} diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index 679f18e1e860..5c5edb2bcd49 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -16,6 +16,10 @@ package android.os; +import com.android.internal.os.BinderCallsStats; + +import java.util.Collection; + /** * Battery stats local system service interface. This is used to pass internal data out of * BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl. @@ -41,4 +45,10 @@ public abstract class BatteryStatsInternal { * @param sinceLast how long in millis has it been since a job was run */ public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast); + + /** + * Informs battery stats of binder stats for the given work source UID. + */ + public abstract void noteBinderCallStats(int workSourceUid, + Collection<BinderCallsStats.CallStat> callStats); } diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index f4a8f372bc36..339ca84c93cf 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; +import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Process; import android.os.SystemProperties; @@ -173,10 +174,10 @@ public class BinderCallsStatsService extends Binder { } try { - mParser.setString(Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.BINDER_CALLS_STATS)); + mParser.setString(Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.BINDER_CALLS_STATS)); } catch (IllegalArgumentException e) { - Slog.e(TAG, "Bad binder call stats settings", e); + Slog.e(TAG, "Bad binder call stats settings", e); } mBinderCallsStats.setDetailedTracking(mParser.getBoolean( SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT)); @@ -298,6 +299,11 @@ public class BinderCallsStatsService extends Binder { CachedDeviceState.Readonly deviceState = getLocalService( CachedDeviceState.Readonly.class); mBinderCallsStats.setDeviceState(deviceState); + + BatteryStatsInternal batteryStatsInternal = getLocalService( + BatteryStatsInternal.class); + mBinderCallsStats.setCallStatsObserver(batteryStatsInternal::noteBinderCallStats); + // It needs to be called before mService.systemReady to make sure the observer is // initialized before installing it. mWorkSourceProvider.systemReady(getContext()); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2744f11f1c4e..5122be2b10c4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10272,11 +10272,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (lines > 0) { sb.append("\n"); - // Merge several logcat streams, and take the last N lines InputStreamReader input = null; try { java.lang.Process logcat = new ProcessBuilder( - "/system/bin/timeout", "-k", "15s", "10s", + // Time out after 10s, but kill logcat with SEGV + // so we can investigate why it didn't finish. + "/system/bin/timeout", "-s", "SEGV", "10s", + // Merge several logcat streams, and take the last N lines. "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system", "-b", "main", "-b", "crash", "-t", String.valueOf(lines)) .redirectErrorStream(true).start(); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 226802c74f25..0260bebbbeac 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -66,6 +66,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; @@ -87,6 +88,7 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -304,6 +306,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub if (DBG) Slog.d(TAG, "Jobs deferred " + uid + ": " + numDeferred + " " + sinceLast); BatteryStatsService.this.noteJobsDeferred(uid, numDeferred, sinceLast); } + + @Override + public void noteBinderCallStats(int workSourceUid, + Collection<BinderCallsStats.CallStat> callStats) { + mStats.noteBinderCallStats(workSourceUid, callStats); + } } private static void awaitUninterruptibly(Future<?> future) { @@ -1771,5 +1779,4 @@ public final class BatteryStatsService extends IBatteryStats.Stub Binder.restoreCallingIdentity(ident); } } - } diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS index 90d940939be8..0c8114cdf2e2 100644 --- a/services/core/java/com/android/server/am/OWNERS +++ b/services/core/java/com/android/server/am/OWNERS @@ -20,6 +20,7 @@ ogunwale@google.com # Permissions & Packages svetoslavganov@google.com toddke@google.com +patb@google.com # Battery Stats joeo@google.com diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index e7c0a50163da..431b00914f02 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -382,8 +382,8 @@ public class NetworkStatsFactory { // Migrate data usage over a VPN to the TUN network. for (UnderlyingNetworkInfo info : vpnArray) { - delta.migrateTun(info.getOwnerUid(), info.getIface(), - info.getUnderlyingIfaces()); + delta.migrateTun(info.getOwnerUid(), info.getInterface(), + info.getUnderlyingInterfaces()); // Filter out debug entries as that may lead to over counting. delta.filterDebugEntries(); } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 3c14440c6467..4ee867b7d051 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -181,7 +181,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; + private static final int MSG_NOTIFY_NETWORK_STATUS = 3; // A message for broadcasting ACTION_NETWORK_STATS_UPDATED in handler thread to prevent // deadlock. private static final int MSG_BROADCAST_NETWORK_STATS_UPDATED = 4; @@ -379,11 +379,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { performPoll(FLAG_PERSIST_ALL); break; } - case MSG_UPDATE_IFACES: { + case MSG_NOTIFY_NETWORK_STATUS: { // If no cached states, ignore. if (mLastNetworkStateSnapshots == null) break; // TODO (b/181642673): Protect mDefaultNetworks from concurrent accessing. - updateIfaces(mDefaultNetworks, mLastNetworkStateSnapshots, mActiveIface); + handleNotifyNetworkStatus( + mDefaultNetworks, mLastNetworkStateSnapshots, mActiveIface); break; } case MSG_PERFORM_POLL_REGISTER_ALERT: { @@ -474,7 +475,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @NonNull Looper looper, @NonNull Executor executor, @NonNull NetworkStatsService service) { // TODO: Update RatType passively in NSS, instead of querying into the monitor - // when forceUpdateIface. + // when notifyNetworkStatus. return new NetworkStatsSubscriptionsMonitor(context, looper, executor, (subscriberId, type) -> service.handleOnCollapsedRatTypeChanged()); } @@ -971,16 +972,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - public void forceUpdateIfaces( - Network[] defaultNetworks, - NetworkStateSnapshot[] networkStates, - String activeIface, - UnderlyingNetworkInfo[] underlyingNetworkInfos) { + /** + * Notify {@code NetworkStatsService} about network status changed. + */ + public void notifyNetworkStatus( + @NonNull Network[] defaultNetworks, + @NonNull NetworkStateSnapshot[] networkStates, + @Nullable String activeIface, + @NonNull UnderlyingNetworkInfo[] underlyingNetworkInfos) { checkNetworkStackPermission(mContext); final long token = Binder.clearCallingIdentity(); try { - updateIfaces(defaultNetworks, networkStates, activeIface); + handleNotifyNetworkStatus(defaultNetworks, networkStates, activeIface); } finally { Binder.restoreCallingIdentity(token); } @@ -1244,12 +1248,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @VisibleForTesting public void handleOnCollapsedRatTypeChanged() { // Protect service from frequently updating. Remove pending messages if any. - mHandler.removeMessages(MSG_UPDATE_IFACES); + mHandler.removeMessages(MSG_NOTIFY_NETWORK_STATUS); mHandler.sendMessageDelayed( - mHandler.obtainMessage(MSG_UPDATE_IFACES), mSettings.getPollDelay()); + mHandler.obtainMessage(MSG_NOTIFY_NETWORK_STATUS), mSettings.getPollDelay()); } - private void updateIfaces( + private void handleNotifyNetworkStatus( Network[] defaultNetworks, NetworkStateSnapshot[] snapshots, String activeIface) { @@ -1257,7 +1261,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mWakeLock.acquire(); try { mActiveIface = activeIface; - updateIfacesLocked(defaultNetworks, snapshots); + handleNotifyNetworkStatusLocked(defaultNetworks, snapshots); } finally { mWakeLock.release(); } @@ -1270,10 +1274,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * they are combined under a single {@link NetworkIdentitySet}. */ @GuardedBy("mStatsLock") - private void updateIfacesLocked(@NonNull Network[] defaultNetworks, + private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks, @NonNull NetworkStateSnapshot[] snapshots) { if (!mSystemReady) return; - if (LOGV) Slog.v(TAG, "updateIfacesLocked()"); + if (LOGV) Slog.v(TAG, "handleNotifyNetworkStatusLocked()"); // take one last stats snapshot before updating iface mapping. this // isn't perfect, since the kernel may already be counting traffic from diff --git a/services/core/java/com/android/server/os/OWNERS b/services/core/java/com/android/server/os/OWNERS new file mode 100644 index 000000000000..19573323e5ad --- /dev/null +++ b/services/core/java/com/android/server/os/OWNERS @@ -0,0 +1,2 @@ +# Bugreporting +per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 77c1c1db2257..49a0a8827699 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -31,6 +31,9 @@ import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.os.BatteryManagerInternal; import android.os.Environment; +import android.os.IThermalService; +import android.os.PowerManager; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; @@ -82,10 +85,15 @@ public class BackgroundDexOptService extends JobService { private static final int OPTIMIZE_ABORT_BY_JOB_SCHEDULER = 2; // Optimizations should be aborted. No space left on device. private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3; + // Optimizations should be aborted. Thermal throttling level too high. + private static final int OPTIMIZE_ABORT_THERMAL = 4; // Used for calculating space threshold for downgrading unused apps. private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2; + // Thermal cutoff value used if one isn't defined by a system property. + private static final int THERMAL_CUTOFF_DEFAULT = PowerManager.THERMAL_STATUS_MODERATE; + /** * Set of failed packages remembered across job runs. */ @@ -107,8 +115,14 @@ public class BackgroundDexOptService extends JobService { private static final long mDowngradeUnusedAppsThresholdInMillis = getDowngradeUnusedAppsThresholdInMillis(); + private final IThermalService mThermalService = + IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + private static List<PackagesUpdatedListener> sPackagesUpdatedListeners = new ArrayList<>(); + private int mThermalStatusCutoff = THERMAL_CUTOFF_DEFAULT; + public static void schedule(Context context) { if (isBackgroundDexoptDisabled()) { return; @@ -251,12 +265,18 @@ public class BackgroundDexOptService extends JobService { Slog.w(TAG, "Idle optimizations aborted because of space constraints."); } else if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { Slog.w(TAG, "Idle optimizations aborted by job scheduler."); + } else if (result == OPTIMIZE_ABORT_THERMAL) { + Slog.w(TAG, "Idle optimizations aborted by thermal throttling."); } else { Slog.w(TAG, "Idle optimizations ended with unexpected code: " + result); } - if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { + + if (result == OPTIMIZE_ABORT_THERMAL) { + // Abandon our timeslice and reschedule + jobFinished(jobParams, /* wantsReschedule */ true); + } else if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { // Abandon our timeslice and do not reschedule. - jobFinished(jobParams, /* reschedule */ false); + jobFinished(jobParams, /* wantsReschedule */ false); } } }.start(); @@ -542,6 +562,24 @@ public class BackgroundDexOptService extends JobService { // JobScheduler requested an early abort. return OPTIMIZE_ABORT_BY_JOB_SCHEDULER; } + + // Abort background dexopt if the device is in a moderate or stronger thermal throttling + // state. + try { + final int thermalStatus = mThermalService.getCurrentThermalStatus(); + + if (DEBUG) { + Log.i(TAG, "Thermal throttling status during bgdexopt: " + thermalStatus); + } + + if (thermalStatus >= mThermalStatusCutoff) { + return OPTIMIZE_ABORT_THERMAL; + } + } catch (RemoteException ex) { + // Because this is a intra-process Binder call it is impossible for a RemoteException + // to be raised. + } + long usableSpace = mDataDir.getUsableSpace(); if (usableSpace < lowStorageThreshold) { // Rather bail than completely fill up the disk. @@ -603,6 +641,9 @@ public class BackgroundDexOptService extends JobService { return false; } + mThermalStatusCutoff = + SystemProperties.getInt("dalvik.vm.dexopt.thermal-cutoff", THERMAL_CUTOFF_DEFAULT); + boolean result; if (params.getJobId() == JOB_POST_BOOT_UPDATE) { result = runPostBootUpdate(params, pm, pkgs); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index fa64df5b1670..836e6150414c 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -5257,6 +5257,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { long[] pattern; switch (effectId) { case HapticFeedbackConstants.CONTEXT_CLICK: + case HapticFeedbackConstants.GESTURE_END: return VibrationEffect.get(VibrationEffect.EFFECT_TICK); case HapticFeedbackConstants.TEXT_HANDLE_MOVE: if (!mHapticTextHandleEnabled) { @@ -5269,7 +5270,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: case HapticFeedbackConstants.ENTRY_BUMP: case HapticFeedbackConstants.DRAG_CROSSING: - case HapticFeedbackConstants.GESTURE_END: return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false); case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS case HapticFeedbackConstants.VIRTUAL_KEY: diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS index 33b9f0f75f81..2441e772468c 100644 --- a/services/core/java/com/android/server/vcn/OWNERS +++ b/services/core/java/com/android/server/vcn/OWNERS @@ -3,5 +3,5 @@ set noparent benedictwong@google.com ckesting@google.com evitayan@google.com +junyin@google.com nharold@google.com -jchalard@google.com
\ No newline at end of file diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java index 19fbdbd86099..5565ccb6cf7c 100644 --- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -145,7 +145,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { */ public void handleSubscriptionsChanged() { final Map<ParcelUuid, Set<String>> privilegedPackages = new HashMap<>(); - final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>(); + final Map<Integer, SubscriptionInfo> newSubIdToInfoMap = new HashMap<>(); final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList(); if (allSubs == null) { @@ -160,7 +160,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { } // Build subId -> subGrp cache - newSubIdToGroupMap.put(subInfo.getSubscriptionId(), subInfo.getGroupUuid()); + newSubIdToInfoMap.put(subInfo.getSubscriptionId(), subInfo); // Update subscription groups that are both ready, and active. For a group to be // considered active, both of the following must be true: @@ -186,7 +186,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { } final TelephonySubscriptionSnapshot newSnapshot = - new TelephonySubscriptionSnapshot(newSubIdToGroupMap, privilegedPackages); + new TelephonySubscriptionSnapshot(newSubIdToInfoMap, privilegedPackages); // If snapshot was meaningfully updated, fire the callback if (!newSnapshot.equals(mCurrentSnapshot)) { @@ -245,7 +245,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */ public static class TelephonySubscriptionSnapshot { - private final Map<Integer, ParcelUuid> mSubIdToGroupMap; + private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap; private final Map<ParcelUuid, Set<String>> mPrivilegedPackages; public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT = @@ -253,12 +253,12 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { @VisibleForTesting(visibility = Visibility.PRIVATE) TelephonySubscriptionSnapshot( - @NonNull Map<Integer, ParcelUuid> subIdToGroupMap, + @NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap, @NonNull Map<ParcelUuid, Set<String>> privilegedPackages) { - Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null"); + Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null"); Objects.requireNonNull(privilegedPackages, "privilegedPackages was null"); - mSubIdToGroupMap = Collections.unmodifiableMap(subIdToGroupMap); + mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap); final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>(); for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) { @@ -285,7 +285,9 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { /** Returns the Subscription Group for a given subId. */ @Nullable public ParcelUuid getGroupForSubId(int subId) { - return mSubIdToGroupMap.get(subId); + return mSubIdToInfoMap.containsKey(subId) + ? mSubIdToInfoMap.get(subId).getGroupUuid() + : null; } /** @@ -295,8 +297,8 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { public Set<Integer> getAllSubIdsInGroup(ParcelUuid subGrp) { final Set<Integer> subIds = new ArraySet<>(); - for (Entry<Integer, ParcelUuid> entry : mSubIdToGroupMap.entrySet()) { - if (subGrp.equals(entry.getValue())) { + for (Entry<Integer, SubscriptionInfo> entry : mSubIdToInfoMap.entrySet()) { + if (subGrp.equals(entry.getValue().getGroupUuid())) { subIds.add(entry.getKey()); } } @@ -304,9 +306,17 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { return subIds; } + /** Checks if the requested subscription is opportunistic */ + @NonNull + public boolean isOpportunistic(int subId) { + return mSubIdToInfoMap.containsKey(subId) + ? mSubIdToInfoMap.get(subId).isOpportunistic() + : false; + } + @Override public int hashCode() { - return Objects.hash(mSubIdToGroupMap, mPrivilegedPackages); + return Objects.hash(mSubIdToInfoMap, mPrivilegedPackages); } @Override @@ -317,7 +327,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj; - return mSubIdToGroupMap.equals(other.mSubIdToGroupMap) + return mSubIdToInfoMap.equals(other.mSubIdToInfoMap) && mPrivilegedPackages.equals(other.mPrivilegedPackages); } @@ -326,7 +336,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { pw.println("TelephonySubscriptionSnapshot:"); pw.increaseIndent(); - pw.println("mSubIdToGroupMap: " + mSubIdToGroupMap); + pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap); pw.println("mPrivilegedPackages: " + mPrivilegedPackages); pw.decreaseIndent(); @@ -335,7 +345,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { @Override public String toString() { return "TelephonySubscriptionSnapshot{ " - + "mSubIdToGroupMap=" + mSubIdToGroupMap + + "mSubIdToInfoMap=" + mSubIdToInfoMap + ", mPrivilegedPackages=" + mPrivilegedPackages + " }"; } diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java index 3bdeec0c1d8e..b05662e1678e 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -16,6 +16,10 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; + import android.annotation.NonNull; import android.annotation.Nullable; import android.net.ConnectivityManager; @@ -25,8 +29,16 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; +import android.net.vcn.VcnManager; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.ParcelUuid; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyManager; +import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -35,9 +47,13 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.TreeSet; /** * Tracks a set of Networks underpinning a VcnGatewayConnection. @@ -51,19 +67,62 @@ import java.util.Set; public class UnderlyingNetworkTracker { @NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName(); + /** + * Minimum signal strength for a WiFi network to be eligible for switching to + * + * <p>A network that satisfies this is eligible to become the selected underlying network with + * no additional conditions + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70; + + /** + * Minimum signal strength to continue using a WiFi network + * + * <p>A network that satisfies the conditions may ONLY continue to be used if it is already + * selected as the underlying network. A WiFi network satisfying this condition, but NOT the + * prospective-network RSSI threshold CANNOT be switched to. + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74; + + /** Priority for any cellular network for which the subscription is listed as opportunistic */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0; + + /** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_WIFI_IN_USE = 1; + + /** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_WIFI_PROSPECTIVE = 2; + + /** Priority for any standard macro cellular network */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_MACRO_CELLULAR = 3; + + /** Priority for any other networks (including unvalidated, etc) */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_ANY = Integer.MAX_VALUE; + @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; - @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities; @NonNull private final UnderlyingNetworkTrackerCallback mCb; @NonNull private final Dependencies mDeps; @NonNull private final Handler mHandler; @NonNull private final ConnectivityManager mConnectivityManager; + @NonNull private final TelephonyCallback mActiveDataSubIdListener = + new VcnActiveDataSubscriptionIdListener(); @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>(); @Nullable private NetworkCallback mWifiBringupCallback; - @Nullable private NetworkCallback mRouteSelectionCallback; + @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback; + @Nullable private NetworkCallback mWifiExitRssiThresholdCallback; + @Nullable private UnderlyingNetworkListener mRouteSelectionCallback; @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; + @Nullable private PersistableBundle mCarrierConfig; private boolean mIsQuitting = false; @Nullable private UnderlyingNetworkRecord mCurrentRecord; @@ -73,13 +132,11 @@ public class UnderlyingNetworkTracker { @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, - @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities, @NonNull UnderlyingNetworkTrackerCallback cb) { this( vcnContext, subscriptionGroup, snapshot, - requiredUnderlyingNetworkCapabilities, cb, new Dependencies()); } @@ -88,22 +145,41 @@ public class UnderlyingNetworkTracker { @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, - @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities, @NonNull UnderlyingNetworkTrackerCallback cb, @NonNull Dependencies deps) { mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); - mRequiredUnderlyingNetworkCapabilities = - Objects.requireNonNull( - requiredUnderlyingNetworkCapabilities, - "Missing requiredUnderlyingNetworkCapabilities"); mCb = Objects.requireNonNull(cb, "Missing cb"); mDeps = Objects.requireNonNull(deps, "Missing deps"); mHandler = new Handler(mVcnContext.getLooper()); mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); + mVcnContext + .getContext() + .getSystemService(TelephonyManager.class) + .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener); + + // TODO: Listen for changes in carrier config that affect this. + for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) { + PersistableBundle config = + mVcnContext + .getContext() + .getSystemService(CarrierConfigManager.class) + .getConfigForSubId(subId); + + if (config != null) { + mCarrierConfig = config; + + // Attempt to use (any) non-opportunistic subscription. If this subscription is + // opportunistic, continue and try to find a non-opportunistic subscription, using + // the opportunistic ones as a last resort. + if (!isOpportunistic(mLastSnapshot, Collections.singleton(subId))) { + break; + } + } + } registerOrUpdateNetworkRequests(); } @@ -111,16 +187,30 @@ public class UnderlyingNetworkTracker { private void registerOrUpdateNetworkRequests() { NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback; NetworkCallback oldWifiCallback = mWifiBringupCallback; + NetworkCallback oldWifiEntryRssiThresholdCallback = mWifiEntryRssiThresholdCallback; + NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback; List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks); mCellBringupCallbacks.clear(); // Register new callbacks. Make-before-break; always register new callbacks before removal // of old callbacks if (!mIsQuitting) { - mRouteSelectionCallback = new RouteSelectionCallback(); - mConnectivityManager.requestBackgroundNetwork( + mRouteSelectionCallback = new UnderlyingNetworkListener(); + mConnectivityManager.registerNetworkCallback( getRouteSelectionRequest(), mRouteSelectionCallback, mHandler); + mWifiEntryRssiThresholdCallback = new NetworkBringupCallback(); + mConnectivityManager.registerNetworkCallback( + getWifiEntryRssiThresholdNetworkRequest(), + mWifiEntryRssiThresholdCallback, + mHandler); + + mWifiExitRssiThresholdCallback = new NetworkBringupCallback(); + mConnectivityManager.registerNetworkCallback( + getWifiExitRssiThresholdNetworkRequest(), + mWifiExitRssiThresholdCallback, + mHandler); + mWifiBringupCallback = new NetworkBringupCallback(); mConnectivityManager.requestBackgroundNetwork( getWifiNetworkRequest(), mWifiBringupCallback, mHandler); @@ -135,6 +225,8 @@ public class UnderlyingNetworkTracker { } else { mRouteSelectionCallback = null; mWifiBringupCallback = null; + mWifiEntryRssiThresholdCallback = null; + mWifiExitRssiThresholdCallback = null; // mCellBringupCallbacks already cleared above. } @@ -145,6 +237,12 @@ public class UnderlyingNetworkTracker { if (oldWifiCallback != null) { mConnectivityManager.unregisterNetworkCallback(oldWifiCallback); } + if (oldWifiEntryRssiThresholdCallback != null) { + mConnectivityManager.unregisterNetworkCallback(oldWifiEntryRssiThresholdCallback); + } + if (oldWifiExitRssiThresholdCallback != null) { + mConnectivityManager.unregisterNetworkCallback(oldWifiExitRssiThresholdCallback); + } for (NetworkCallback cellBringupCallback : oldCellCallbacks) { mConnectivityManager.unregisterNetworkCallback(cellBringupCallback); } @@ -168,6 +266,8 @@ public class UnderlyingNetworkTracker { } return getBaseNetworkRequestBuilder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) .build(); } @@ -189,6 +289,38 @@ public class UnderlyingNetworkTracker { } /** + * Builds the WiFi entry threshold signal strength request + * + * <p>This request ensures that WiFi reports the crossing of the wifi entry RSSI threshold. + * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a + * pace to effectively select a short-lived WiFi offload network. + */ + private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() { + return getBaseNetworkRequestBuilder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) + // Ensure wifi updates signal strengths when crossing this threshold. + .setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig)) + .build(); + } + + /** + * Builds the WiFi exit threshold signal strength request + * + * <p>This request ensures that WiFi reports the crossing of the wifi exit RSSI threshold. + * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a + * pace to effectively select away from a failing WiFi network. + */ + private NetworkRequest getWifiExitRssiThresholdNetworkRequest() { + return getBaseNetworkRequestBuilder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) + // Ensure wifi updates signal strengths when crossing this threshold. + .setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig)) + .build(); + } + + /** * Builds a Cellular bringup request for a given subId * * <p>This request is filed in order to ensure that the Telephony stack always has a @@ -233,10 +365,18 @@ public class UnderlyingNetworkTracker { * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change. */ - public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) { - Objects.requireNonNull(snapshot, "Missing snapshot"); + public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot newSnapshot) { + Objects.requireNonNull(newSnapshot, "Missing newSnapshot"); + + final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; + mLastSnapshot = newSnapshot; - mLastSnapshot = snapshot; + // Only trigger re-registration if subIds in this group have changed + if (oldSnapshot + .getAllSubIdsInGroup(mSubscriptionGroup) + .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) { + return; + } registerOrUpdateNetworkRequests(); } @@ -247,88 +387,43 @@ public class UnderlyingNetworkTracker { // Will unregister all existing callbacks, but not register new ones due to quitting flag. registerOrUpdateNetworkRequests(); - } - /** Returns whether the currently selected Network matches the given network. */ - private static boolean isSameNetwork( - @Nullable UnderlyingNetworkRecord.Builder recordInProgress, @NonNull Network network) { - return recordInProgress != null && recordInProgress.getNetwork().equals(network); + mVcnContext + .getContext() + .getSystemService(TelephonyManager.class) + .unregisterTelephonyCallback(mActiveDataSubIdListener); } - /** Notify the Callback if a full UnderlyingNetworkRecord exists. */ - private void maybeNotifyCallback() { - // Only forward this update if a complete record has been received - if (!mRecordInProgress.isValid()) { - return; - } + private void reevaluateNetworks() { + TreeSet<UnderlyingNetworkRecord> sorted = + new TreeSet<>( + UnderlyingNetworkRecord.getComparator( + mSubscriptionGroup, mLastSnapshot, mCurrentRecord, mCarrierConfig)); + sorted.addAll(mRouteSelectionCallback.getUnderlyingNetworks()); - // Only forward this update if the updated record differs form the current record - UnderlyingNetworkRecord updatedRecord = mRecordInProgress.build(); - if (!updatedRecord.equals(mCurrentRecord)) { - mCurrentRecord = updatedRecord; - - mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); - } - } - - private void handleNetworkAvailable(@NonNull Network network) { - mVcnContext.ensureRunningOnLooperThread(); - - mRecordInProgress = new UnderlyingNetworkRecord.Builder(network); - } - - private void handleNetworkLost(@NonNull Network network) { - mVcnContext.ensureRunningOnLooperThread(); - - if (!isSameNetwork(mRecordInProgress, network)) { - Slog.wtf(TAG, "Non-underlying Network lost"); + UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first(); + if (Objects.equals(mCurrentRecord, candidate)) { return; } - mRecordInProgress = null; - mCurrentRecord = null; - mCb.onSelectedUnderlyingNetworkChanged(null /* underlyingNetworkRecord */); + mCurrentRecord = candidate; + mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); } - private void handleCapabilitiesChanged( - @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { - mVcnContext.ensureRunningOnLooperThread(); - - if (!isSameNetwork(mRecordInProgress, network)) { - Slog.wtf(TAG, "Invalid update to NetworkCapabilities"); - return; + private static boolean isOpportunistic( + @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) { + if (snapshot == null) { + Slog.wtf(TAG, "Got null snapshot"); + return false; } - mRecordInProgress.setNetworkCapabilities(networkCapabilities); - - maybeNotifyCallback(); - } - - private void handlePropertiesChanged( - @NonNull Network network, @NonNull LinkProperties linkProperties) { - mVcnContext.ensureRunningOnLooperThread(); - - if (!isSameNetwork(mRecordInProgress, network)) { - Slog.wtf(TAG, "Invalid update to LinkProperties"); - return; - } - - mRecordInProgress.setLinkProperties(linkProperties); - - maybeNotifyCallback(); - } - - private void handleNetworkBlocked(@NonNull Network network, boolean isBlocked) { - mVcnContext.ensureRunningOnLooperThread(); - - if (!isSameNetwork(mRecordInProgress, network)) { - Slog.wtf(TAG, "Invalid update to isBlocked"); - return; + for (int subId : subIds) { + if (snapshot.isOpportunistic(subId)) { + return true; + } } - mRecordInProgress.setIsBlocked(isBlocked); - - maybeNotifyCallback(); + return false; } /** @@ -347,36 +442,104 @@ public class UnderlyingNetworkTracker { * truth. */ @VisibleForTesting - class RouteSelectionCallback extends NetworkCallback { + class UnderlyingNetworkListener extends NetworkCallback { + private final Map<Network, UnderlyingNetworkRecord.Builder> + mUnderlyingNetworkRecordBuilders = new ArrayMap<>(); + + private List<UnderlyingNetworkRecord> getUnderlyingNetworks() { + final List<UnderlyingNetworkRecord> records = new ArrayList<>(); + + for (UnderlyingNetworkRecord.Builder builder : + mUnderlyingNetworkRecordBuilders.values()) { + if (builder.isValid()) { + records.add(builder.build()); + } + } + + return records; + } + @Override public void onAvailable(@NonNull Network network) { - handleNetworkAvailable(network); + mUnderlyingNetworkRecordBuilders.put( + network, new UnderlyingNetworkRecord.Builder(network)); } @Override public void onLost(@NonNull Network network) { - handleNetworkLost(network); + mUnderlyingNetworkRecordBuilders.remove(network); + + reevaluateNetworks(); } @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { - if (networkCapabilities.equals(mRecordInProgress.getNetworkCapabilities())) return; - handleCapabilitiesChanged(network, networkCapabilities); + final UnderlyingNetworkRecord.Builder builder = + mUnderlyingNetworkRecordBuilders.get(network); + if (builder == null) { + Slog.wtf(TAG, "Got capabilities change for unknown key: " + network); + return; + } + + builder.setNetworkCapabilities(networkCapabilities); + if (builder.isValid()) { + reevaluateNetworks(); + } } @Override public void onLinkPropertiesChanged( @NonNull Network network, @NonNull LinkProperties linkProperties) { - handlePropertiesChanged(network, linkProperties); + final UnderlyingNetworkRecord.Builder builder = + mUnderlyingNetworkRecordBuilders.get(network); + if (builder == null) { + Slog.wtf(TAG, "Got link properties change for unknown key: " + network); + return; + } + + builder.setLinkProperties(linkProperties); + if (builder.isValid()) { + reevaluateNetworks(); + } } @Override public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) { - handleNetworkBlocked(network, isBlocked); + final UnderlyingNetworkRecord.Builder builder = + mUnderlyingNetworkRecordBuilders.get(network); + if (builder == null) { + Slog.wtf(TAG, "Got blocked status change for unknown key: " + network); + return; + } + + builder.setIsBlocked(isBlocked); + if (builder.isValid()) { + reevaluateNetworks(); + } } } + private static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) { + if (carrierConfig != null) { + return carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, + WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT); + } + + return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT; + } + + private static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) { + if (carrierConfig != null) { + return carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + WIFI_EXIT_RSSI_THRESHOLD_DEFAULT); + } + + return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT; + } + /** A record of a single underlying network, caching relevant fields. */ public static class UnderlyingNetworkRecord { @NonNull public final Network network; @@ -413,6 +576,89 @@ public class UnderlyingNetworkTracker { return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); } + /** + * Gives networks a priority class, based on the following priorities: + * + * <ol> + * <li>Opportunistic cellular + * <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT + * <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT + * <li>Macro cellular + * <li>Any others + * </ol> + */ + private int calculatePriorityClass( + ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, + UnderlyingNetworkRecord currentlySelected, + PersistableBundle carrierConfig) { + final NetworkCapabilities caps = networkCapabilities; + + // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED + + if (isBlocked) { + Slog.wtf(TAG, "Network blocked for System Server: " + network); + return PRIORITY_ANY; + } + + if (caps.hasTransport(TRANSPORT_CELLULAR) + && isOpportunistic(snapshot, caps.getSubscriptionIds())) { + // If this carrier is the active data provider, ensure that opportunistic is only + // ever prioritized if it is also the active data subscription. This ensures that + // if an opportunistic subscription is still in the process of being switched to, + // or switched away from, the VCN does not attempt to continue using it against the + // decision made at the telephony layer. Failure to do so may result in the modem + // switching back and forth. + // + // Allow the following two cases: + // 1. Active subId is NOT in the group that this VCN is supporting + // 2. This opportunistic subscription is for the active subId + if (!snapshot.getAllSubIdsInGroup(subscriptionGroup) + .contains(SubscriptionManager.getActiveDataSubscriptionId()) + || caps.getSubscriptionIds() + .contains(SubscriptionManager.getActiveDataSubscriptionId())) { + return PRIORITY_OPPORTUNISTIC_CELLULAR; + } + } + + if (caps.hasTransport(TRANSPORT_WIFI)) { + if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig) + && currentlySelected != null + && network.equals(currentlySelected.network)) { + return PRIORITY_WIFI_IN_USE; + } + + if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) { + return PRIORITY_WIFI_PROSPECTIVE; + } + } + + // Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might + // be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be + // the case if the Default Data SubId does not support certain services (eg voice + // calling) + if (caps.hasTransport(TRANSPORT_CELLULAR) + && !isOpportunistic(snapshot, caps.getSubscriptionIds())) { + return PRIORITY_MACRO_CELLULAR; + } + + return PRIORITY_ANY; + } + + private static Comparator<UnderlyingNetworkRecord> getComparator( + ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, + UnderlyingNetworkRecord currentlySelected, + PersistableBundle carrierConfig) { + return (left, right) -> { + return Integer.compare( + left.calculatePriorityClass( + subscriptionGroup, snapshot, currentlySelected, carrierConfig), + right.calculatePriorityClass( + subscriptionGroup, snapshot, currentlySelected, carrierConfig)); + }; + } + /** Dumps the state of this record for logging and debugging purposes. */ public void dump(IndentingPrintWriter pw) { pw.println("UnderlyingNetworkRecord:"); @@ -434,6 +680,8 @@ public class UnderlyingNetworkTracker { boolean mIsBlocked; boolean mWasIsBlockedSet; + @Nullable private UnderlyingNetworkRecord mCached; + private Builder(@NonNull Network network) { mNetwork = network; } @@ -445,6 +693,7 @@ public class UnderlyingNetworkTracker { private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) { mNetworkCapabilities = networkCapabilities; + mCached = null; } @Nullable @@ -454,11 +703,13 @@ public class UnderlyingNetworkTracker { private void setLinkProperties(@NonNull LinkProperties linkProperties) { mLinkProperties = linkProperties; + mCached = null; } private void setIsBlocked(boolean isBlocked) { mIsBlocked = isBlocked; mWasIsBlockedSet = true; + mCached = null; } private boolean isValid() { @@ -466,12 +717,30 @@ public class UnderlyingNetworkTracker { } private UnderlyingNetworkRecord build() { - return new UnderlyingNetworkRecord( - mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked); + if (!isValid()) { + throw new IllegalArgumentException( + "Called build before UnderlyingNetworkRecord was valid"); + } + + if (mCached == null) { + mCached = + new UnderlyingNetworkRecord( + mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked); + } + + return mCached; } } } + private class VcnActiveDataSubscriptionIdListener extends TelephonyCallback + implements ActiveDataSubscriptionIdListener { + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + reevaluateNetworks(); + } + } + /** Callbacks for being notified of the changes in, or to the selected underlying network. */ public interface UnderlyingNetworkTrackerCallback { /** diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 5cecff6f93c1..dff04bfc6d7c 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -673,7 +673,6 @@ public class VcnGatewayConnection extends StateMachine { mVcnContext, subscriptionGroup, mLastSnapshot, - mConnectionConfig.getAllUnderlyingCapabilities(), mUnderlyingNetworkTrackerCallback); mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class); @@ -2274,13 +2273,11 @@ public class VcnGatewayConnection extends StateMachine { VcnContext vcnContext, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - Set<Integer> requiredUnderlyingNetworkCapabilities, UnderlyingNetworkTrackerCallback callback) { return new UnderlyingNetworkTracker( vcnContext, subscriptionGroup, snapshot, - requiredUnderlyingNetworkCapabilities, callback); } diff --git a/services/incremental/OWNERS b/services/incremental/OWNERS index ad5eca7f6daf..7ebb962c8feb 100644 --- a/services/incremental/OWNERS +++ b/services/incremental/OWNERS @@ -5,3 +5,4 @@ alexbuy@google.com schfan@google.com toddke@google.com zyy@google.com +patb@google.com diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index a02a039c3beb..d041eecab17b 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -159,13 +159,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; +import com.android.internal.util.test.FsUtil; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.usage.AppStandbyInternal; import com.google.common.util.concurrent.AbstractFuture; -import libcore.io.IoUtils; import libcore.io.Streams; import org.junit.After; @@ -2347,7 +2347,7 @@ public class NetworkPolicyManagerServiceTest { private void setNetpolicyXml(Context context) throws Exception { mPolicyDir = context.getFilesDir(); if (mPolicyDir.exists()) { - IoUtils.deleteContents(mPolicyDir); + FsUtil.deleteContents(mPolicyDir); } if (!TextUtils.isEmpty(mNetpolicyXml)) { final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml; diff --git a/tests/BackgroundDexOptServiceIntegrationTests/OWNERS b/tests/BackgroundDexOptServiceIntegrationTests/OWNERS new file mode 100644 index 000000000000..3414a7469ac2 --- /dev/null +++ b/tests/BackgroundDexOptServiceIntegrationTests/OWNERS @@ -0,0 +1 @@ +include platform/art:/OWNERS diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java index e05816eb391f..90ddb6ffb34a 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java @@ -20,6 +20,7 @@ import android.app.AlarmManager; import android.content.Context; import android.os.Environment; import android.os.ParcelFileDescriptor; +import android.os.PowerManager; import android.os.SystemProperties; import android.os.storage.StorageManager; import android.util.Log; @@ -201,11 +202,16 @@ public final class BackgroundDexOptServiceIntegrationTests { fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER)); } - // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run private static void runBackgroundDexOpt() throws IOException { + runBackgroundDexOpt("Success"); + } + + // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run + private static void runBackgroundDexOpt(String expectedStatus) throws IOException { String result = runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME); - if (!result.trim().equals("Success")) { - throw new IllegalStateException("Expected command success, received >" + result + "<"); + if (!result.trim().equals(expectedStatus)) { + throw new IllegalStateException("Expected status: " + expectedStatus + + "; Received: " + result.trim()); } } @@ -242,6 +248,16 @@ public final class BackgroundDexOptServiceIntegrationTests { runShellCommand(String.format("cmd package compile -f -m %s %s", filter, pkg)); } + // Override the thermal status of the device + public static void overrideThermalStatus(int status) throws IOException { + runShellCommand("cmd thermalservice override-status " + status); + } + + // Reset the thermal status of the device + public static void resetThermalStatus() throws IOException { + runShellCommand("cmd thermalservice reset"); + } + // Test that background dexopt under normal conditions succeeds. @Test public void testBackgroundDexOpt() throws IOException { @@ -307,4 +323,17 @@ public final class BackgroundDexOptServiceIntegrationTests { } } + // Test that background dexopt job doesn't trigger if the device is under thermal throttling. + @Test + public void testBackgroundDexOptThermalThrottling() throws IOException { + try { + compilePackageWithFilter(PACKAGE_NAME, "verify"); + overrideThermalStatus(PowerManager.THERMAL_STATUS_MODERATE); + // The bgdexopt task should fail when onStartJob is run + runBackgroundDexOpt("Failure"); + Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME)); + } finally { + resetThermalStatus(); + } + } } diff --git a/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java new file mode 100644 index 000000000000..e65661298b7c --- /dev/null +++ b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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.util.test; + +import java.io.File; + +public class FsUtil { + + /** + * Deletes all files under a given directory. Deliberately ignores errors, on the assumption + * that test cleanup is only supposed to be best-effort. + * + * @param dir directory to clear its contents + */ + public static void deleteContents(File dir) { + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteContents(file); + } + file.delete(); + } + } + } +} diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS index 33b9f0f75f81..2441e772468c 100644 --- a/tests/vcn/OWNERS +++ b/tests/vcn/OWNERS @@ -3,5 +3,5 @@ set noparent benedictwong@google.com ckesting@google.com evitayan@google.com +junyin@google.com nharold@google.com -jchalard@google.com
\ No newline at end of file diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index c59dcf879b1c..4ce78aa4d8c1 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -92,10 +92,6 @@ public class VcnGatewayConnectionConfigTest { builder.addExposedCapability(caps); } - for (int caps : UNDERLYING_CAPS) { - builder.addRequiredUnderlyingCapability(caps); - } - return builder.build(); } @@ -141,9 +137,7 @@ public class VcnGatewayConnectionConfigTest { @Test public void testBuilderRequiresNonEmptyExposedCaps() { try { - newBuilder() - .addRequiredUnderlyingCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .build(); + newBuilder().build(); fail("Expected exception due to invalid exposed capabilities"); } catch (IllegalArgumentException e) { @@ -187,10 +181,6 @@ public class VcnGatewayConnectionConfigTest { Arrays.sort(exposedCaps); assertArrayEquals(EXPOSED_CAPS, exposedCaps); - int[] underlyingCaps = config.getRequiredUnderlyingCapabilities(); - Arrays.sort(underlyingCaps); - assertArrayEquals(UNDERLYING_CAPS, underlyingCaps); - assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams()); assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis()); diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java index 582275d0547d..abae81cf1742 100644 --- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java +++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java @@ -16,15 +16,17 @@ package android.net.vcn; -import static android.net.NetworkCapabilities.REDACT_ALL; -import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; +import static android.net.NetworkCapabilities.REDACT_NONE; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; +import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; +import android.os.Build; import android.os.Parcel; import org.junit.Test; @@ -39,12 +41,6 @@ public class VcnTransportInfoTest { private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO); @Test - public void testRedactionDefaults() { - assertEquals(REDACT_ALL, CELL_UNDERLYING_INFO.getRedaction()); - assertEquals(REDACT_ALL, WIFI_UNDERLYING_INFO.getRedaction()); - } - - @Test public void testGetWifiInfo() { assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo()); @@ -59,15 +55,19 @@ public class VcnTransportInfoTest { } @Test - public void testMakeCopySetsRedactions() { - assertEquals( - REDACT_FOR_NETWORK_SETTINGS, - ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS)) - .getRedaction()); + public void testMakeCopyRedactForAccessFineLocation() { assertEquals( - REDACT_FOR_NETWORK_SETTINGS, - ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS)) - .getRedaction()); + SUB_ID, + ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION)) + .getSubId()); + + // TODO: remove the if statement when S pushes to AOSP. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + assertEquals( + WifiConfiguration.INVALID_NETWORK_ID, + ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy( + REDACT_FOR_ACCESS_FINE_LOCATION)).getWifiInfo().getNetworkId()); + } } @Test @@ -78,35 +78,31 @@ public class VcnTransportInfoTest { } @Test - public void testParcelUnparcel() { - verifyParcelingIsNull(CELL_UNDERLYING_INFO); - verifyParcelingIsNull(WIFI_UNDERLYING_INFO); - } - - private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) { - // Verify redacted by default - Parcel parcel = Parcel.obtain(); - vcnTransportInfo.writeToParcel(parcel, 0 /* flags */); - parcel.setDataPosition(0); + public void testApplicableRedactions() { + assertEquals(REDACT_NONE, CELL_UNDERLYING_INFO.getApplicableRedactions()); - assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel)); + final long wifiRedactions = WIFI_INFO.getApplicableRedactions(); + assertEquals(wifiRedactions, WIFI_UNDERLYING_INFO.getApplicableRedactions()); } @Test - public void testParcelUnparcelNotRedactedForSysUi() { - verifyParcelingForSysUi(CELL_UNDERLYING_INFO); - verifyParcelingForSysUi(WIFI_UNDERLYING_INFO); + public void testParcelNotRedactedForSysUi() { + VcnTransportInfo cellRedacted = parcelForSysUi(CELL_UNDERLYING_INFO); + assertEquals(SUB_ID, cellRedacted.getSubId()); + VcnTransportInfo wifiRedacted = parcelForSysUi(WIFI_UNDERLYING_INFO); + assertEquals(NETWORK_ID, wifiRedacted.getWifiInfo().getNetworkId()); } - private void verifyParcelingForSysUi(VcnTransportInfo vcnTransportInfo) { + private VcnTransportInfo parcelForSysUi(VcnTransportInfo vcnTransportInfo) { // Allow fully unredacted; SysUI will have all the relevant permissions. - final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(0); + final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy( + REDACT_NONE); final Parcel parcel = Parcel.obtain(); unRedacted.writeToParcel(parcel, 0 /* flags */); parcel.setDataPosition(0); final VcnTransportInfo unparceled = VcnTransportInfo.CREATOR.createFromParcel(parcel); assertEquals(vcnTransportInfo, unparceled); - assertEquals(REDACT_ALL, unparceled.getRedaction()); + return unparceled; } } diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java index 528f240b9912..ca7463884d3a 100644 --- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -88,13 +88,13 @@ public class TelephonySubscriptionTrackerTest { private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class); private static final Map<ParcelUuid, Set<String>> TEST_PRIVILEGED_PACKAGES = Collections.singletonMap(TEST_PARCEL_UUID, Collections.singleton(PACKAGE_NAME)); - private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP; + private static final Map<Integer, SubscriptionInfo> TEST_SUBID_TO_INFO_MAP; static { - final Map<Integer, ParcelUuid> subIdToGroupMap = new HashMap<>(); - subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_PARCEL_UUID); - subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_PARCEL_UUID); - TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap); + final Map<Integer, SubscriptionInfo> subIdToGroupMap = new HashMap<>(); + subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_SUBINFO_1); + subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_SUBINFO_2); + TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap); } @NonNull private final Context mContext; @@ -190,13 +190,13 @@ public class TelephonySubscriptionTrackerTest { private TelephonySubscriptionSnapshot buildExpectedSnapshot( Map<ParcelUuid, Set<String>> privilegedPackages) { - return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, privilegedPackages); + return buildExpectedSnapshot(TEST_SUBID_TO_INFO_MAP, privilegedPackages); } private TelephonySubscriptionSnapshot buildExpectedSnapshot( - Map<Integer, ParcelUuid> subIdToGroupMap, + Map<Integer, SubscriptionInfo> subIdToInfoMap, Map<ParcelUuid, Set<String>> privilegedPackages) { - return new TelephonySubscriptionSnapshot(subIdToGroupMap, privilegedPackages); + return new TelephonySubscriptionSnapshot(subIdToInfoMap, privilegedPackages); } private void verifyNoActiveSubscriptions() { @@ -371,7 +371,7 @@ public class TelephonySubscriptionTrackerTest { @Test public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception { final TelephonySubscriptionSnapshot snapshot = - new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap()); + new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap()); assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1)); assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2)); @@ -380,7 +380,7 @@ public class TelephonySubscriptionTrackerTest { @Test public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception { final TelephonySubscriptionSnapshot snapshot = - new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap()); + new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap()); assertEquals( new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)), diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java index 0b72cd93e8b0..f91575b670d3 100644 --- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java @@ -42,12 +42,14 @@ import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; import android.os.ParcelUuid; import android.os.test.TestLooper; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; +import android.telephony.TelephonyManager; import android.util.ArraySet; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback; -import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback; +import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkListener; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; @@ -59,7 +61,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Arrays; -import java.util.Collections; import java.util.Set; import java.util.UUID; @@ -98,11 +99,13 @@ public class UnderlyingNetworkTrackerTest { @Mock private Context mContext; @Mock private VcnNetworkProvider mVcnNetworkProvider; @Mock private ConnectivityManager mConnectivityManager; + @Mock private TelephonyManager mTelephonyManager; + @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot; @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb; @Mock private Network mNetwork; - @Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor; + @Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor; private TestLooper mTestLooper; private VcnContext mVcnContext; @@ -127,6 +130,13 @@ public class UnderlyingNetworkTrackerTest { mConnectivityManager, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + setupSystemService( + mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); + setupSystemService( + mContext, + mCarrierConfigManager, + Context.CARRIER_CONFIG_SERVICE, + CarrierConfigManager.class); when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS); @@ -135,7 +145,6 @@ public class UnderlyingNetworkTrackerTest { mVcnContext, SUB_GROUP, mSubscriptionSnapshot, - Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET), mNetworkTrackerCb); } @@ -163,26 +172,25 @@ public class UnderlyingNetworkTrackerTest { @Test public void testNetworkCallbacksRegisteredOnStartupForTestMode() { + final ConnectivityManager cm = mock(ConnectivityManager.class); + setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); final VcnContext vcnContext = - spy( - new VcnContext( - mContext, - mTestLooper.getLooper(), - mVcnNetworkProvider, - true /* isInTestMode */)); - - mUnderlyingNetworkTracker = - new UnderlyingNetworkTracker( - vcnContext, - SUB_GROUP, - mSubscriptionSnapshot, - Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET), - mNetworkTrackerCb); - - verify(mConnectivityManager) - .requestBackgroundNetwork( + new VcnContext( + mContext, + mTestLooper.getLooper(), + mVcnNetworkProvider, + true /* isInTestMode */); + + new UnderlyingNetworkTracker( + vcnContext, + SUB_GROUP, + mSubscriptionSnapshot, + mNetworkTrackerCb); + + verify(cm) + .registerNetworkCallback( eq(getTestNetworkRequest(INITIAL_SUB_IDS)), - any(RouteSelectionCallback.class), + any(UnderlyingNetworkListener.class), any()); } @@ -200,9 +208,19 @@ public class UnderlyingNetworkTrackerTest { } verify(mConnectivityManager) - .requestBackgroundNetwork( + .registerNetworkCallback( eq(getRouteSelectionRequest(expectedSubIds)), - any(RouteSelectionCallback.class), + any(UnderlyingNetworkListener.class), + any()); + verify(mConnectivityManager) + .registerNetworkCallback( + eq(getWifiEntryRssiThresholdRequest(expectedSubIds)), + any(NetworkBringupCallback.class), + any()); + verify(mConnectivityManager) + .registerNetworkCallback( + eq(getWifiExitRssiThresholdRequest(expectedSubIds)), + any(NetworkBringupCallback.class), any()); } @@ -218,9 +236,10 @@ public class UnderlyingNetworkTrackerTest { mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate); // verify that initially-filed bringup requests are unregistered (cell + wifi) - verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 1)) + verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 3)) .unregisterNetworkCallback(any(NetworkBringupCallback.class)); - verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class)); + verify(mConnectivityManager) + .unregisterNetworkCallback(any(UnderlyingNetworkListener.class)); verifyNetworkRequestsRegistered(UPDATED_SUB_IDS); } @@ -231,6 +250,24 @@ public class UnderlyingNetworkTrackerTest { .build(); } + private NetworkRequest getWifiEntryRssiThresholdRequest(Set<Integer> netCapsSubIds) { + // TODO (b/187991063): Add tests for carrier-config based thresholds + return getExpectedRequestBase() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setSubscriptionIds(netCapsSubIds) + .setSignalStrength(UnderlyingNetworkTracker.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT) + .build(); + } + + private NetworkRequest getWifiExitRssiThresholdRequest(Set<Integer> netCapsSubIds) { + // TODO (b/187991063): Add tests for carrier-config based thresholds + return getExpectedRequestBase() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setSubscriptionIds(netCapsSubIds) + .setSignalStrength(UnderlyingNetworkTracker.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT) + .build(); + } + private NetworkRequest getCellRequestForSubId(int subId) { return getExpectedRequestBase() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) @@ -239,7 +276,11 @@ public class UnderlyingNetworkTrackerTest { } private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) { - return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build(); + return getExpectedRequestBase() + .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) + .setSubscriptionIds(netCapsSubIds) + .build(); } private NetworkRequest getTestNetworkRequest(Set<Integer> netCapsSubIds) { @@ -265,11 +306,12 @@ public class UnderlyingNetworkTrackerTest { public void testTeardown() { mUnderlyingNetworkTracker.teardown(); - // Expect 3 NetworkBringupCallbacks to be unregistered: 1 for WiFi and 2 for Cellular (1x - // for each subId) - verify(mConnectivityManager, times(3)) + // Expect 5 NetworkBringupCallbacks to be unregistered: 1 for WiFi, 2 for Cellular (1x for + // each subId), and 1 for each of the Wifi signal strength thresholds + verify(mConnectivityManager, times(5)) .unregisterNetworkCallback(any(NetworkBringupCallback.class)); - verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class)); + verify(mConnectivityManager) + .unregisterNetworkCallback(any(UnderlyingNetworkListener.class)); } @Test @@ -302,19 +344,19 @@ public class UnderlyingNetworkTrackerTest { verifyRegistrationOnAvailableAndGetCallback(); } - private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() { + private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() { return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES); } - private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback( + private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback( NetworkCapabilities networkCapabilities) { verify(mConnectivityManager) - .requestBackgroundNetwork( + .registerNetworkCallback( eq(getRouteSelectionRequest(INITIAL_SUB_IDS)), - mRouteSelectionCallbackCaptor.capture(), + mUnderlyingNetworkListenerCaptor.capture(), any()); - RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue(); + UnderlyingNetworkListener cb = mUnderlyingNetworkListenerCaptor.getValue(); cb.onAvailable(mNetwork); cb.onCapabilitiesChanged(mNetwork, networkCapabilities); cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES); @@ -332,7 +374,7 @@ public class UnderlyingNetworkTrackerTest { @Test public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() { - RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(); cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES); @@ -347,7 +389,7 @@ public class UnderlyingNetworkTrackerTest { @Test public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() { - RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(); cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES); @@ -362,7 +404,7 @@ public class UnderlyingNetworkTrackerTest { @Test public void testRecordTrackerCallbackNotifiedForNetworkSuspended() { - RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(); cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES); @@ -381,7 +423,7 @@ public class UnderlyingNetworkTrackerTest { @Test public void testRecordTrackerCallbackNotifiedForNetworkResumed() { - RouteSelectionCallback cb = + UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES); cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES); @@ -401,7 +443,7 @@ public class UnderlyingNetworkTrackerTest { @Test public void testRecordTrackerCallbackNotifiedForBlocked() { - RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(); cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */); @@ -416,7 +458,7 @@ public class UnderlyingNetworkTrackerTest { @Test public void testRecordTrackerCallbackNotifiedForNetworkLoss() { - RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(); cb.onLost(mNetwork); @@ -425,7 +467,7 @@ public class UnderlyingNetworkTrackerTest { @Test public void testRecordTrackerCallbackIgnoresDuplicateRecord() { - RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); + UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(); cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES); @@ -433,4 +475,6 @@ public class UnderlyingNetworkTrackerTest { // UnderlyingNetworkRecord does not actually change verifyNoMoreInteractions(mNetworkTrackerCb); } + + // TODO (b/187991063): Add tests for network prioritization } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 1ecb4c9ee298..860a919aa9b3 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -54,6 +54,7 @@ import android.net.vcn.VcnGatewayConnectionConfigTest; import android.os.ParcelUuid; import android.os.PowerManager; import android.os.test.TestLooper; +import android.telephony.SubscriptionInfo; import com.android.internal.util.State; import com.android.internal.util.WakeupMessage; @@ -73,6 +74,12 @@ import java.util.concurrent.TimeUnit; public class VcnGatewayConnectionTestBase { protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID()); + protected static final SubscriptionInfo TEST_SUB_INFO = mock(SubscriptionInfo.class); + + static { + doReturn(TEST_SUB_GRP).when(TEST_SUB_INFO).getGroupUuid(); + } + protected static final InetAddress TEST_DNS_ADDR = InetAddresses.parseNumericAddress("2001:DB8:0:1::"); protected static final InetAddress TEST_DNS_ADDR_2 = @@ -116,7 +123,7 @@ public class VcnGatewayConnectionTestBase { protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT = new TelephonySubscriptionSnapshot( - Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP); + Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO), Collections.EMPTY_MAP); @NonNull protected final Context mContext; @NonNull protected final TestLooper mTestLooper; @@ -166,7 +173,7 @@ public class VcnGatewayConnectionTestBase { doReturn(mUnderlyingNetworkTracker) .when(mDeps) - .newUnderlyingNetworkTracker(any(), any(), any(), any(), any()); + .newUnderlyingNetworkTracker(any(), any(), any(), any()); doReturn(mWakeLock) .when(mDeps) .newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any()); diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp index 5c47e0fa8a16..4e8dcb1bc6ee 100644 --- a/tools/aapt/pseudolocalize.cpp +++ b/tools/aapt/pseudolocalize.cpp @@ -194,7 +194,8 @@ static String16 pseudo_generate_expansion(const unsigned int length) { break; } } - result.remove(length + ext, 0); + // Just keep the first length + ext characters + result = String16(result, length + ext); } return result; } diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS index f1903a5a54a7..69dfcc98340d 100644 --- a/tools/aapt2/OWNERS +++ b/tools/aapt2/OWNERS @@ -1,3 +1,4 @@ set noparent toddke@google.com -rtmitchell@google.com
\ No newline at end of file +rtmitchell@google.com +patb@google.com
\ No newline at end of file diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh index f25fcdcb7479..514f17a042bc 100755 --- a/tools/aosp/aosp_sha.sh +++ b/tools/aosp/aosp_sha.sh @@ -5,7 +5,21 @@ if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then # Change appears to be in AOSP exit 0 else - # Change appears to be non-AOSP; search for files + # Change appears to be non-AOSP. + + # If this is a cherry-pick, then allow it. + cherrypick=0 + while read -r line ; do + if [[ $line =~ cherry\ picked\ from ]] ; then + (( cherrypick++ )) + fi + done < <(git show $1) + if (( cherrypick != 0 )); then + # This is a cherry-pick, so allow it. + exit 0 + fi + + # See if any files are affected. count=0 while read -r file ; do if (( count == 0 )); then diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index c19ae3b0b11f..a117aa09ab62 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -43,8 +43,8 @@ inline infix fun Int.times(action: () -> Unit) { * cccc dd */ fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String { - val col1w = map { (a, _) -> a.length }.max()!! - val col2w = map { (_, b) -> b.length }.max()!! + val col1w = map { (a, _) -> a.length }.maxOrNull()!! + val col2w = map { (_, b) -> b.length }.maxOrNull()!! return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n") } |