Home | History | Annotate | Download | only in net
      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      */
    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      */
    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      */
    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 
    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
    171     public int describeContents() {
    172         return 0;
    173     }
    174 
    175     @Override
    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
    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
    202     public int hashCode() {
    203         return Objects.hash(networkKey, rssiCurve, meteredHint, attributes);
    204     }
    205 
    206     @Override
    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      */
    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      */
    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
    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