• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.Nullable;
21 import android.annotation.SystemApi;
22 import android.os.Bundle;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import java.lang.Math;
27 import java.lang.UnsupportedOperationException;
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.util.Objects;
31 
32 /**
33  * A network identifier along with a score for the quality of that network.
34  *
35  * @hide
36  */
37 @SystemApi
38 public class ScoredNetwork implements Parcelable {
39 
40   /**
41      * Key used with the {@link #attributes} bundle to define the badging curve.
42      *
43      * <p>The badging curve is a {@link RssiCurve} used to map different RSSI values to {@link
44      * NetworkBadging.Badging} enums.
45      */
46     public static final String ATTRIBUTES_KEY_BADGING_CURVE =
47             "android.net.attributes.key.BADGING_CURVE";
48     /**
49      * Extra used with {@link #attributes} to specify whether the
50      * network is believed to have a captive portal.
51      * <p>
52      * This data may be used, for example, to display a visual indicator
53      * in a network selection list.
54      * <p>
55      * Note that the this extra conveys the possible presence of a
56      * captive portal, not its state or the user's ability to open
57      * the portal.
58      * <p>
59      * If no value is associated with this key then it's unknown.
60      */
61     public static final String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL =
62             "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
63 
64     /**
65      * Key used with the {@link #attributes} bundle to define the rankingScoreOffset int value.
66      *
67      * <p>The rankingScoreOffset is used when calculating the ranking score used to rank networks
68      * against one another. See {@link #calculateRankingScore} for more information.
69      */
70     public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET =
71             "android.net.attributes.key.RANKING_SCORE_OFFSET";
72 
73     /** A {@link NetworkKey} uniquely identifying this network. */
74     public final NetworkKey networkKey;
75 
76     /**
77      * The {@link RssiCurve} representing the scores for this network based on the RSSI.
78      *
79      * <p>This field is optional and may be set to null to indicate that no score is available for
80      * this network at this time. Such networks, along with networks for which the scorer has not
81      * responded, are always prioritized below scored networks, regardless of the score.
82      */
83     public final RssiCurve rssiCurve;
84 
85     /**
86      * A boolean value that indicates whether or not the network is believed to be metered.
87      *
88      * <p>A network can be classified as metered if the user would be
89      * sensitive to heavy data usage on that connection due to monetary costs,
90      * data limitations or battery/performance issues. A typical example would
91      * be a wifi connection where the user would be charged for usage.
92      */
93     public final boolean meteredHint;
94 
95     /**
96      * An additional collection of optional attributes set by
97      * the Network Recommendation Provider.
98      *
99      * @see #ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL
100      * @see #ATTRIBUTES_KEY_RANKING_SCORE_OFFSET
101      */
102     @Nullable
103     public final Bundle attributes;
104 
105     /**
106      * Construct a new {@link ScoredNetwork}.
107      *
108      * @param networkKey the {@link NetworkKey} uniquely identifying this network.
109      * @param rssiCurve the {@link RssiCurve} representing the scores for this network based on the
110      *     RSSI. This field is optional, and may be skipped to represent a network which the scorer
111      *     has opted not to score at this time. Passing a null value here is strongly preferred to
112      *     not returning any {@link ScoredNetwork} for a given {@link NetworkKey} because it
113      *     indicates to the system not to request scores for this network in the future, although
114      *     the scorer may choose to issue an out-of-band update at any time.
115      */
ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve)116     public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve) {
117         this(networkKey, rssiCurve, false /* meteredHint */);
118     }
119 
120     /**
121      * Construct a new {@link ScoredNetwork}.
122      *
123      * @param networkKey the {@link NetworkKey} uniquely identifying this network.
124      * @param rssiCurve the {@link RssiCurve} representing the scores for this network based on the
125      *     RSSI. This field is optional, and may be skipped to represent a network which the scorer
126      *     has opted not to score at this time. Passing a null value here is strongly preferred to
127      *     not returning any {@link ScoredNetwork} for a given {@link NetworkKey} because it
128      *     indicates to the system not to request scores for this network in the future, although
129      *     the scorer may choose to issue an out-of-band update at any time.
130      * @param meteredHint A boolean value indicating whether or not the network is believed to be
131      *     metered.
132      */
ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint)133     public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint) {
134         this(networkKey, rssiCurve, meteredHint, null /* attributes */);
135     }
136 
137     /**
138      * Construct a new {@link ScoredNetwork}.
139      *
140      * @param networkKey the {@link NetworkKey} uniquely identifying this network
141      * @param rssiCurve the {@link RssiCurve} representing the scores for this network based on the
142      *     RSSI. This field is optional, and may be skipped to represent a network which the scorer
143      *     has opted not to score at this time. Passing a null value here is strongly preferred to
144      *     not returning any {@link ScoredNetwork} for a given {@link NetworkKey} because it
145      *     indicates to the system not to request scores for this network in the future, although
146      *     the scorer may choose to issue an out-of-band update at any time.
147      * @param meteredHint a boolean value indicating whether or not the network is believed to be
148      *                    metered
149      * @param attributes optional provider specific attributes
150      */
ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint, @Nullable Bundle attributes)151     public ScoredNetwork(NetworkKey networkKey, RssiCurve rssiCurve, boolean meteredHint,
152             @Nullable Bundle attributes) {
153         this.networkKey = networkKey;
154         this.rssiCurve = rssiCurve;
155         this.meteredHint = meteredHint;
156         this.attributes = attributes;
157     }
158 
ScoredNetwork(Parcel in)159     private ScoredNetwork(Parcel in) {
160         networkKey = NetworkKey.CREATOR.createFromParcel(in);
161         if (in.readByte() == 1) {
162             rssiCurve = RssiCurve.CREATOR.createFromParcel(in);
163         } else {
164             rssiCurve = null;
165         }
166         meteredHint = (in.readByte() == 1);
167         attributes = in.readBundle();
168     }
169 
170     @Override
describeContents()171     public int describeContents() {
172         return 0;
173     }
174 
175     @Override
writeToParcel(Parcel out, int flags)176     public void writeToParcel(Parcel out, int flags) {
177         networkKey.writeToParcel(out, flags);
178         if (rssiCurve != null) {
179             out.writeByte((byte) 1);
180             rssiCurve.writeToParcel(out, flags);
181         } else {
182             out.writeByte((byte) 0);
183         }
184         out.writeByte((byte) (meteredHint ? 1 : 0));
185         out.writeBundle(attributes);
186     }
187 
188     @Override
equals(Object o)189     public boolean equals(Object o) {
190         if (this == o) return true;
191         if (o == null || getClass() != o.getClass()) return false;
192 
193         ScoredNetwork that = (ScoredNetwork) o;
194 
195         return Objects.equals(networkKey, that.networkKey)
196                 && Objects.equals(rssiCurve, that.rssiCurve)
197                 && Objects.equals(meteredHint, that.meteredHint)
198                 && Objects.equals(attributes, that.attributes);
199     }
200 
201     @Override
hashCode()202     public int hashCode() {
203         return Objects.hash(networkKey, rssiCurve, meteredHint, attributes);
204     }
205 
206     @Override
toString()207     public String toString() {
208         StringBuilder out = new StringBuilder(
209                 "ScoredNetwork{" +
210                 "networkKey=" + networkKey +
211                 ", rssiCurve=" + rssiCurve +
212                 ", meteredHint=" + meteredHint);
213         // calling isEmpty will unparcel the bundle so its contents can be converted to a string
214         if (attributes != null && !attributes.isEmpty()) {
215             out.append(", attributes=" + attributes);
216         }
217         out.append('}');
218         return out.toString();
219     }
220 
221     /**
222      * Returns true if a ranking score can be calculated for this network.
223      *
224      * @hide
225      */
hasRankingScore()226     public boolean hasRankingScore() {
227         return (rssiCurve != null)
228                 || (attributes != null
229                         && attributes.containsKey(ATTRIBUTES_KEY_RANKING_SCORE_OFFSET));
230     }
231 
232     /**
233      * Returns a ranking score for a given RSSI which can be used to comparatively
234      * rank networks.
235      *
236      * <p>The score obtained by the rssiCurve is bitshifted left by 8 bits to expand it to an
237      * integer and then the offset is added. If the addition operation overflows or underflows,
238      * Integer.MAX_VALUE and Integer.MIN_VALUE will be returned respectively.
239      *
240      * <p>{@link #hasRankingScore} should be called first to ensure this network is capable
241      * of returning a ranking score.
242      *
243      * @throws UnsupportedOperationException if there is no RssiCurve and no rankingScoreOffset
244      * for this network (hasRankingScore returns false).
245      *
246      * @hide
247      */
calculateRankingScore(int rssi)248     public int calculateRankingScore(int rssi) throws UnsupportedOperationException {
249         if (!hasRankingScore()) {
250             throw new UnsupportedOperationException(
251                     "Either rssiCurve or rankingScoreOffset is required to calculate the "
252                             + "ranking score");
253         }
254 
255         int offset = 0;
256         if (attributes != null) {
257              offset += attributes.getInt(ATTRIBUTES_KEY_RANKING_SCORE_OFFSET, 0 /* default */);
258         }
259 
260         int score = (rssiCurve == null) ? 0 : rssiCurve.lookupScore(rssi) << Byte.SIZE;
261 
262         try {
263             return Math.addExact(score, offset);
264         } catch (ArithmeticException e) {
265             return (score < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
266         }
267     }
268 
269     /**
270      * Return the {@link NetworkBadging.Badging} enum for this network for the given RSSI, derived from the
271      * badging curve.
272      *
273      * <p>If no badging curve is present, {@link #BADGE_NONE} will be returned.
274      *
275      * @param rssi The rssi level for which the badge should be calculated
276      */
277     @NetworkBadging.Badging
calculateBadge(int rssi)278     public int calculateBadge(int rssi) {
279         if (attributes != null && attributes.containsKey(ATTRIBUTES_KEY_BADGING_CURVE)) {
280             RssiCurve badgingCurve =
281                     attributes.getParcelable(ATTRIBUTES_KEY_BADGING_CURVE);
282             return badgingCurve.lookupScore(rssi);
283         }
284 
285         return NetworkBadging.BADGING_NONE;
286     }
287 
288     public static final Parcelable.Creator<ScoredNetwork> CREATOR =
289             new Parcelable.Creator<ScoredNetwork>() {
290                 @Override
291                 public ScoredNetwork createFromParcel(Parcel in) {
292                     return new ScoredNetwork(in);
293                 }
294 
295                 @Override
296                 public ScoredNetwork[] newArray(int size) {
297                     return new ScoredNetwork[size];
298                 }
299             };
300 }
301