/* * 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.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Object representing the quality of a network as perceived by the user. * * A NetworkScore object represents the characteristics of a network that affects how good the * network is considered for a particular use. * @hide */ @SystemApi public final class NetworkScore implements Parcelable { // This will be removed soon. Do *NOT* depend on it for any new code that is not part of // a migration. private final int mLegacyInt; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { KEEP_CONNECTED_NONE, KEEP_CONNECTED_FOR_HANDOVER }) public @interface KeepConnectedReason { } /** * Do not keep this network connected if there is no outstanding request for it. */ public static final int KEEP_CONNECTED_NONE = 0; /** * Keep this network connected even if there is no outstanding request for it, because it * is being considered for handover. */ public static final int KEEP_CONNECTED_FOR_HANDOVER = 1; // Agent-managed policies // This network should lose to a wifi that has ever been validated // NOTE : temporarily this policy is managed by ConnectivityService, because of legacy. The // legacy design has this bit global to the system and tacked on WiFi which means it will affect // networks from carriers who don't want it and non-carrier networks, which is bad for users. // The S design has this on mobile networks only, so this can be fixed eventually ; as CS // doesn't know what carriers need this bit, the initial S implementation will continue to // affect other carriers but will at least leave non-mobile networks alone. Eventually Telephony // should set this on networks from carriers that require it. /** @hide */ public static final int POLICY_YIELD_TO_BAD_WIFI = 1; // This network is primary for this transport. /** @hide */ public static final int POLICY_TRANSPORT_PRIMARY = 2; // This network is exiting : it will likely disconnect in a few seconds. /** @hide */ public static final int POLICY_EXITING = 3; /** @hide */ public static final int MIN_AGENT_MANAGED_POLICY = POLICY_YIELD_TO_BAD_WIFI; /** @hide */ public static final int MAX_AGENT_MANAGED_POLICY = POLICY_EXITING; // Bitmask of all the policies applied to this score. private final long mPolicies; private final int mKeepConnectedReason; /** @hide */ NetworkScore(final int legacyInt, final long policies, @KeepConnectedReason final int keepConnectedReason) { mLegacyInt = legacyInt; mPolicies = policies; mKeepConnectedReason = keepConnectedReason; } private NetworkScore(@NonNull final Parcel in) { mLegacyInt = in.readInt(); mPolicies = in.readLong(); mKeepConnectedReason = in.readInt(); } /** * Get the legacy int score embedded in this NetworkScore. * @see Builder#setLegacyInt(int) */ public int getLegacyInt() { return mLegacyInt; } /** * Returns the keep-connected reason, or KEEP_CONNECTED_NONE. */ public int getKeepConnectedReason() { return mKeepConnectedReason; } /** * @return whether this score has a particular policy. * * @hide */ @VisibleForTesting public boolean hasPolicy(final int policy) { return 0 != (mPolicies & (1L << policy)); } /** * To the exclusive usage of FullScore * @hide */ public long getPolicies() { return mPolicies; } /** * Whether this network should yield to a previously validated wifi gone bad. * * If this policy is set, other things being equal, the device will prefer a previously * validated WiFi even if this network is validated and the WiFi is not. * If this policy is not set, the device prefers the validated network. * * @hide */ // TODO : Unhide this for telephony and have telephony call it on the relevant carriers. // In the mean time this is handled by Connectivity in a backward-compatible manner. public boolean shouldYieldToBadWifi() { return hasPolicy(POLICY_YIELD_TO_BAD_WIFI); } /** * Whether this network is primary for this transport. * * When multiple networks of the same transport are active, the device prefers the ones that * are primary. This is meant in particular for DS-DA devices with a user setting to choose the * default SIM card, or for WiFi STA+STA and make-before-break cases. * * @hide */ @SystemApi public boolean isTransportPrimary() { return hasPolicy(POLICY_TRANSPORT_PRIMARY); } /** * Whether this network is exiting. * * If this policy is set, the device will expect this network to disconnect within seconds. * It will try to migrate to some other network if any is available, policy permitting, to * avoid service disruption. * This is useful in particular when a good cellular network is available and WiFi is getting * weak and risks disconnecting soon. The WiFi network should be marked as exiting so that * the device will prefer the reliable mobile network over this soon-to-be-lost WiFi. * * @hide */ @SystemApi public boolean isExiting() { return hasPolicy(POLICY_EXITING); } @Override public String toString() { return "Score(" + mLegacyInt + " ; Policies : " + mPolicies + ")"; } @Override public void writeToParcel(@NonNull final Parcel dest, final int flags) { dest.writeInt(mLegacyInt); dest.writeLong(mPolicies); dest.writeInt(mKeepConnectedReason); } @Override public int describeContents() { return 0; } @NonNull public static final Creator CREATOR = new Creator<>() { @Override @NonNull public NetworkScore createFromParcel(@NonNull final Parcel in) { return new NetworkScore(in); } @Override @NonNull public NetworkScore[] newArray(int size) { return new NetworkScore[size]; } }; /** * A builder for NetworkScore. */ public static final class Builder { private static final long POLICY_NONE = 0L; private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE; private int mLegacyInt = INVALID_LEGACY_INT; private int mKeepConnectedReason = KEEP_CONNECTED_NONE; private int mPolicies = 0; /** * Sets the legacy int for this score. * * This will be used for measurements and logs, but will no longer be used for ranking * networks against each other. Callers that existed before Android S should send what * they used to send as the int score. * * @param score the legacy int * @return this */ @NonNull public Builder setLegacyInt(final int score) { mLegacyInt = score; return this; } /** * Set for a network that should never be preferred to a wifi that has ever been validated * * If this policy is set, other things being equal, the device will prefer a previously * validated WiFi even if this network is validated and the WiFi is not. * If this policy is not set, the device prefers the validated network. * * @return this builder * @hide */ // TODO : Unhide this for telephony and have telephony call it on the relevant carriers. // In the mean time this is handled by Connectivity in a backward-compatible manner. @NonNull public Builder setShouldYieldToBadWifi(final boolean val) { if (val) { mPolicies |= (1L << POLICY_YIELD_TO_BAD_WIFI); } else { mPolicies &= ~(1L << POLICY_YIELD_TO_BAD_WIFI); } return this; } /** * Set for a network that is primary for this transport. * * When multiple networks of the same transport are active, the device prefers the ones that * are primary. This is meant in particular for DS-DA devices with a user setting to choose * the default SIM card, or for WiFi STA+STA and make-before-break cases. * * @return this builder * @hide */ @SystemApi @NonNull public Builder setTransportPrimary(final boolean val) { if (val) { mPolicies |= (1L << POLICY_TRANSPORT_PRIMARY); } else { mPolicies &= ~(1L << POLICY_TRANSPORT_PRIMARY); } return this; } /** * Set for a network that will likely disconnect in a few seconds. * * If this policy is set, the device will expect this network to disconnect within seconds. * It will try to migrate to some other network if any is available, policy permitting, to * avoid service disruption. * This is useful in particular when a good cellular network is available and WiFi is * getting weak and risks disconnecting soon. The WiFi network should be marked as exiting * so that the device will prefer the reliable mobile network over this soon-to-be-lost * WiFi. * * @return this builder * @hide */ @SystemApi @NonNull public Builder setExiting(final boolean val) { if (val) { mPolicies |= (1L << POLICY_EXITING); } else { mPolicies &= ~(1L << POLICY_EXITING); } return this; } /** * Set the keep-connected reason. * * This can be reset by calling it again with {@link KEEP_CONNECTED_NONE}. */ @NonNull public Builder setKeepConnectedReason(@KeepConnectedReason final int reason) { mKeepConnectedReason = reason; return this; } /** * Builds this NetworkScore. * @return The built NetworkScore object. */ @NonNull public NetworkScore build() { return new NetworkScore(mLegacyInt, mPolicies, mKeepConnectedReason); } } }