1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.SystemApi;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 
30 /**
31  * Object representing the quality of a network as perceived by the user.
32  *
33  * A NetworkScore object represents the characteristics of a network that affects how good the
34  * network is considered for a particular use.
35  * @hide
36  */
37 @SystemApi
38 public final class NetworkScore implements Parcelable {
39     // This will be removed soon. Do *NOT* depend on it for any new code that is not part of
40     // a migration.
41     private final int mLegacyInt;
42 
43     /** @hide */
44     @Retention(RetentionPolicy.SOURCE)
45     @IntDef(value = {
46             KEEP_CONNECTED_NONE,
47             KEEP_CONNECTED_FOR_HANDOVER,
48             KEEP_CONNECTED_FOR_TEST,
49             KEEP_CONNECTED_LOCAL_NETWORK
50     })
51     public @interface KeepConnectedReason { }
52 
53     /**
54      * Do not keep this network connected if there is no outstanding request for it.
55      */
56     public static final int KEEP_CONNECTED_NONE = 0;
57     /**
58      * Keep this network connected even if there is no outstanding request for it, because it
59      * is being considered for handover.
60      */
61     public static final int KEEP_CONNECTED_FOR_HANDOVER = 1;
62     /**
63      * Keep this network connected even if there is no outstanding request for it, because it
64      * is used in a test and it's not necessarily easy to file the right request for it.
65      * @hide
66      */
67     public static final int KEEP_CONNECTED_FOR_TEST = 2;
68     /**
69      * Keep this network connected even if there is no outstanding request for it, because
70      * it is a local network.
71      * @hide
72      */
73     public static final int KEEP_CONNECTED_LOCAL_NETWORK = 3;
74 
75     // Agent-managed policies
76     // This network should lose to a wifi that has ever been validated
77     // NOTE : temporarily this policy is managed by ConnectivityService, because of legacy. The
78     // legacy design has this bit global to the system and tacked on WiFi which means it will affect
79     // networks from carriers who don't want it and non-carrier networks, which is bad for users.
80     // The S design has this on mobile networks only, so this can be fixed eventually ; as CS
81     // doesn't know what carriers need this bit, the initial S implementation will continue to
82     // affect other carriers but will at least leave non-mobile networks alone. Eventually Telephony
83     // should set this on networks from carriers that require it.
84     /** @hide */
85     public static final int POLICY_YIELD_TO_BAD_WIFI = 1;
86     // This network is primary for this transport.
87     /** @hide */
88     public static final int POLICY_TRANSPORT_PRIMARY = 2;
89     // This network is exiting : it will likely disconnect in a few seconds.
90     /** @hide */
91     public static final int POLICY_EXITING = 3;
92 
93     /** @hide */
94     public static final int MIN_AGENT_MANAGED_POLICY = POLICY_YIELD_TO_BAD_WIFI;
95     /** @hide */
96     public static final int MAX_AGENT_MANAGED_POLICY = POLICY_EXITING;
97 
98     // Bitmask of all the policies applied to this score.
99     private final long mPolicies;
100 
101     private final int mKeepConnectedReason;
102 
103     /** @hide */
NetworkScore(final int legacyInt, final long policies, @KeepConnectedReason final int keepConnectedReason)104     NetworkScore(final int legacyInt, final long policies,
105             @KeepConnectedReason final int keepConnectedReason) {
106         mLegacyInt = legacyInt;
107         mPolicies = policies;
108         mKeepConnectedReason = keepConnectedReason;
109     }
110 
NetworkScore(@onNull final Parcel in)111     private NetworkScore(@NonNull final Parcel in) {
112         mLegacyInt = in.readInt();
113         mPolicies = in.readLong();
114         mKeepConnectedReason = in.readInt();
115     }
116 
117     /**
118      * Get the legacy int score embedded in this NetworkScore.
119      * @see Builder#setLegacyInt(int)
120      */
getLegacyInt()121     public int getLegacyInt() {
122         return mLegacyInt;
123     }
124 
125     /**
126      * Returns the keep-connected reason, or KEEP_CONNECTED_NONE.
127      */
getKeepConnectedReason()128     public int getKeepConnectedReason() {
129         return mKeepConnectedReason;
130     }
131 
132     /**
133      * @return whether this score has a particular policy.
134      *
135      * @hide
136      */
137     @VisibleForTesting
hasPolicy(final int policy)138     public boolean hasPolicy(final int policy) {
139         return 0 != (mPolicies & (1L << policy));
140     }
141 
142     /**
143      * To the exclusive usage of FullScore
144      * @hide
145      */
getPolicies()146     public long getPolicies() {
147         return mPolicies;
148     }
149 
150     /**
151      * Whether this network should yield to a previously validated wifi gone bad.
152      *
153      * If this policy is set, other things being equal, the device will prefer a previously
154      * validated WiFi even if this network is validated and the WiFi is not.
155      * If this policy is not set, the device prefers the validated network.
156      *
157      * @hide
158      */
159     // TODO : Unhide this for telephony and have telephony call it on the relevant carriers.
160     // In the mean time this is handled by Connectivity in a backward-compatible manner.
shouldYieldToBadWifi()161     public boolean shouldYieldToBadWifi() {
162         return hasPolicy(POLICY_YIELD_TO_BAD_WIFI);
163     }
164 
165     /**
166      * Whether this network is primary for this transport.
167      *
168      * When multiple networks of the same transport are active, the device prefers the ones that
169      * are primary. This is meant in particular for DS-DA devices with a user setting to choose the
170      * default SIM card, or for WiFi STA+STA and make-before-break cases.
171      *
172      * @hide
173      */
174     @SystemApi
isTransportPrimary()175     public boolean isTransportPrimary() {
176         return hasPolicy(POLICY_TRANSPORT_PRIMARY);
177     }
178 
179     /**
180      * Whether this network is exiting.
181      *
182      * If this policy is set, the device will expect this network to disconnect within seconds.
183      * It will try to migrate to some other network if any is available, policy permitting, to
184      * avoid service disruption.
185      * This is useful in particular when a good cellular network is available and WiFi is getting
186      * weak and risks disconnecting soon. The WiFi network should be marked as exiting so that
187      * the device will prefer the reliable mobile network over this soon-to-be-lost WiFi.
188      *
189      * @hide
190      */
191     @SystemApi
isExiting()192     public boolean isExiting() {
193         return hasPolicy(POLICY_EXITING);
194     }
195 
196     @Override
toString()197     public String toString() {
198         return "Score(Policies : " + mPolicies + ")";
199     }
200 
201     @Override
writeToParcel(@onNull final Parcel dest, final int flags)202     public void writeToParcel(@NonNull final Parcel dest, final int flags) {
203         dest.writeInt(mLegacyInt);
204         dest.writeLong(mPolicies);
205         dest.writeInt(mKeepConnectedReason);
206     }
207 
208     @Override
describeContents()209     public int describeContents() {
210         return 0;
211     }
212 
213     @NonNull public static final Creator<NetworkScore> CREATOR = new Creator<>() {
214         @Override
215         @NonNull
216         public NetworkScore createFromParcel(@NonNull final Parcel in) {
217             return new NetworkScore(in);
218         }
219 
220         @Override
221         @NonNull
222         public NetworkScore[] newArray(int size) {
223             return new NetworkScore[size];
224         }
225     };
226 
227     /**
228      * A builder for NetworkScore.
229      */
230     public static final class Builder {
231         private static final long POLICY_NONE = 0L;
232         private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
233         private int mLegacyInt = INVALID_LEGACY_INT;
234         private int mKeepConnectedReason = KEEP_CONNECTED_NONE;
235         private int mPolicies = 0;
236 
237         /**
238          * Sets the legacy int for this score.
239          *
240          * This will be used for measurements and logs, but will no longer be used for ranking
241          * networks against each other. Callers that existed before Android S should send what
242          * they used to send as the int score.
243          *
244          * @param score the legacy int
245          * @return this
246          */
247         @NonNull
setLegacyInt(final int score)248         public Builder setLegacyInt(final int score) {
249             mLegacyInt = score;
250             return this;
251         }
252 
253 
254         /**
255          * Set for a network that should never be preferred to a wifi that has ever been validated
256          *
257          * If this policy is set, other things being equal, the device will prefer a previously
258          * validated WiFi even if this network is validated and the WiFi is not.
259          * If this policy is not set, the device prefers the validated network.
260          *
261          * @return this builder
262          * @hide
263          */
264         // TODO : Unhide this for telephony and have telephony call it on the relevant carriers.
265         // In the mean time this is handled by Connectivity in a backward-compatible manner.
266         @NonNull
setShouldYieldToBadWifi(final boolean val)267         public Builder setShouldYieldToBadWifi(final boolean val) {
268             if (val) {
269                 mPolicies |= (1L << POLICY_YIELD_TO_BAD_WIFI);
270             } else {
271                 mPolicies &= ~(1L << POLICY_YIELD_TO_BAD_WIFI);
272             }
273             return this;
274         }
275 
276         /**
277          * Set for a network that is primary for this transport.
278          *
279          * When multiple networks of the same transport are active, the device prefers the ones that
280          * are primary. This is meant in particular for DS-DA devices with a user setting to choose
281          * the default SIM card, or for WiFi STA+STA and make-before-break cases.
282          *
283          * @return this builder
284          * @hide
285          */
286         @SystemApi
287         @NonNull
setTransportPrimary(final boolean val)288         public Builder setTransportPrimary(final boolean val) {
289             if (val) {
290                 mPolicies |= (1L << POLICY_TRANSPORT_PRIMARY);
291             } else {
292                 mPolicies &= ~(1L << POLICY_TRANSPORT_PRIMARY);
293             }
294             return this;
295         }
296 
297         /**
298          * Set for a network that will likely disconnect in a few seconds.
299          *
300          * If this policy is set, the device will expect this network to disconnect within seconds.
301          * It will try to migrate to some other network if any is available, policy permitting, to
302          * avoid service disruption.
303          * This is useful in particular when a good cellular network is available and WiFi is
304          * getting weak and risks disconnecting soon. The WiFi network should be marked as exiting
305          * so that the device will prefer the reliable mobile network over this soon-to-be-lost
306          * WiFi.
307          *
308          * @return this builder
309          * @hide
310          */
311         @SystemApi
312         @NonNull
setExiting(final boolean val)313         public Builder setExiting(final boolean val) {
314             if (val) {
315                 mPolicies |= (1L << POLICY_EXITING);
316             } else {
317                 mPolicies &= ~(1L << POLICY_EXITING);
318             }
319             return this;
320         }
321 
322         /**
323          * Set the keep-connected reason.
324          *
325          * This can be reset by calling it again with {@link KEEP_CONNECTED_NONE}.
326          */
327         @NonNull
setKeepConnectedReason(@eepConnectedReason final int reason)328         public Builder setKeepConnectedReason(@KeepConnectedReason final int reason) {
329             mKeepConnectedReason = reason;
330             return this;
331         }
332 
333         /**
334          * Builds this NetworkScore.
335          * @return The built NetworkScore object.
336          */
337         @NonNull
build()338         public NetworkScore build() {
339             return new NetworkScore(mLegacyInt, mPolicies, mKeepConnectedReason);
340         }
341     }
342 }
343