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.SystemApi;
     20 import android.os.Parcel;
     21 import android.os.Parcelable;
     22 
     23 import java.util.Arrays;
     24 import java.util.Objects;
     25 
     26 /**
     27  * A curve defining the network score over a range of RSSI values.
     28  *
     29  * <p>For each RSSI bucket, the score may be any byte. Scores have no absolute meaning and are only
     30  * considered relative to other scores assigned by the same scorer. Networks with no score are
     31  * treated equivalently to a network with score {@link Byte#MIN_VALUE}, and will not be used.
     32  *
     33  * <p>For example, consider a curve starting at -110 dBm with a bucket width of 10 and the
     34  * following buckets: {@code [-20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]}.
     35  * This represents a linear curve between -110 dBm and 30 dBm. It scores progressively higher at
     36  * stronger signal strengths.
     37  *
     38  * <p>A network can be assigned a fixed score independent of RSSI by setting
     39  * {@link #rssiBuckets} to a one-byte array whose element is the fixed score. {@link #start}
     40  * should be set to the lowest RSSI value at which this fixed score should apply, and
     41  * {@link #bucketWidth} should be set such that {@code start + bucketWidth} is equal to the
     42  * highest RSSI value at which this fixed score should apply.
     43  *
     44  * <p>Note that RSSI values below -110 dBm or above 30 dBm are unlikely to cause any difference
     45  * in connectivity behavior from those endpoints. That is, the connectivity framework will treat
     46  * a network with a -120 dBm signal exactly as it would treat one with a -110 dBm signal.
     47  * Therefore, graphs which specify scores outside this range may be truncated to this range by
     48  * the system.
     49  *
     50  * @see ScoredNetwork
     51  * @hide
     52  */
     53 @SystemApi
     54 public class RssiCurve implements Parcelable {
     55     private static final int DEFAULT_ACTIVE_NETWORK_RSSI_BOOST = 25;
     56 
     57     /** The starting dBm of the curve. */
     58     public final int start;
     59 
     60     /** The width of each RSSI bucket, in dBm. */
     61     public final int bucketWidth;
     62 
     63     /** The score for each RSSI bucket. */
     64     public final byte[] rssiBuckets;
     65 
     66     /**
     67      * The RSSI boost to give this network when active, in dBm.
     68      *
     69      * <p>When the system is connected to this network, it will pretend that the network has this
     70      * much higher of an RSSI. This is to avoid switching networks when another network has only a
     71      * slightly higher score.
     72      */
     73     public final int activeNetworkRssiBoost;
     74 
     75     /**
     76      * Construct a new {@link RssiCurve}.
     77      *
     78      * @param start the starting dBm of the curve.
     79      * @param bucketWidth the width of each RSSI bucket, in dBm.
     80      * @param rssiBuckets the score for each RSSI bucket.
     81      */
     82     public RssiCurve(int start, int bucketWidth, byte[] rssiBuckets) {
     83         this(start, bucketWidth, rssiBuckets, DEFAULT_ACTIVE_NETWORK_RSSI_BOOST);
     84     }
     85 
     86     /**
     87      * Construct a new {@link RssiCurve}.
     88      *
     89      * @param start the starting dBm of the curve.
     90      * @param bucketWidth the width of each RSSI bucket, in dBm.
     91      * @param rssiBuckets the score for each RSSI bucket.
     92      * @param activeNetworkRssiBoost the RSSI boost to apply when this network is active, in dBm.
     93      */
     94     public RssiCurve(int start, int bucketWidth, byte[] rssiBuckets, int activeNetworkRssiBoost) {
     95         this.start = start;
     96         this.bucketWidth = bucketWidth;
     97         if (rssiBuckets == null || rssiBuckets.length == 0) {
     98             throw new IllegalArgumentException("rssiBuckets must be at least one element large.");
     99         }
    100         this.rssiBuckets = rssiBuckets;
    101         this.activeNetworkRssiBoost = activeNetworkRssiBoost;
    102     }
    103 
    104     private RssiCurve(Parcel in) {
    105         start = in.readInt();
    106         bucketWidth = in.readInt();
    107         int bucketCount = in.readInt();
    108         rssiBuckets = new byte[bucketCount];
    109         in.readByteArray(rssiBuckets);
    110         activeNetworkRssiBoost = in.readInt();
    111     }
    112 
    113     @Override
    114     public int describeContents() {
    115         return 0;
    116     }
    117 
    118     @Override
    119     public void writeToParcel(Parcel out, int flags) {
    120         out.writeInt(start);
    121         out.writeInt(bucketWidth);
    122         out.writeInt(rssiBuckets.length);
    123         out.writeByteArray(rssiBuckets);
    124         out.writeInt(activeNetworkRssiBoost);
    125     }
    126 
    127     /**
    128      * Lookup the score for a given RSSI value.
    129      *
    130      * @param rssi The RSSI to lookup. If the RSSI falls below the start of the curve, the score at
    131      *         the start of the curve will be returned. If it falls after the end of the curve, the
    132      *         score at the end of the curve will be returned.
    133      * @return the score for the given RSSI.
    134      */
    135     public byte lookupScore(int rssi) {
    136         return lookupScore(rssi, false /* isActiveNetwork */);
    137     }
    138 
    139     /**
    140      * Lookup the score for a given RSSI value.
    141      *
    142      * @param rssi The RSSI to lookup. If the RSSI falls below the start of the curve, the score at
    143      *         the start of the curve will be returned. If it falls after the end of the curve, the
    144      *         score at the end of the curve will be returned.
    145      * @param isActiveNetwork Whether this network is currently active.
    146      * @return the score for the given RSSI.
    147      */
    148     public byte lookupScore(int rssi, boolean isActiveNetwork) {
    149         if (isActiveNetwork) {
    150             rssi += activeNetworkRssiBoost;
    151         }
    152 
    153         int index = (rssi - start) / bucketWidth;
    154 
    155         // Snap the index to the closest bucket if it falls outside the curve.
    156         if (index < 0) {
    157             index = 0;
    158         } else if (index > rssiBuckets.length - 1) {
    159             index = rssiBuckets.length - 1;
    160         }
    161 
    162         return rssiBuckets[index];
    163     }
    164 
    165     /**
    166      * Determine if two RSSI curves are defined in the same way.
    167      *
    168      * <p>Note that two curves can be equivalent but defined differently, e.g. if one bucket in one
    169      * curve is split into two buckets in another. For the purpose of this method, these curves are
    170      * not considered equal to each other.
    171      */
    172     @Override
    173     public boolean equals(Object o) {
    174         if (this == o) return true;
    175         if (o == null || getClass() != o.getClass()) return false;
    176 
    177         RssiCurve rssiCurve = (RssiCurve) o;
    178 
    179         return start == rssiCurve.start &&
    180                 bucketWidth == rssiCurve.bucketWidth &&
    181                 Arrays.equals(rssiBuckets, rssiCurve.rssiBuckets) &&
    182                 activeNetworkRssiBoost == rssiCurve.activeNetworkRssiBoost;
    183     }
    184 
    185     @Override
    186     public int hashCode() {
    187         return Objects.hash(start, bucketWidth, activeNetworkRssiBoost) ^ Arrays.hashCode(rssiBuckets);
    188     }
    189 
    190     @Override
    191     public String toString() {
    192         StringBuilder sb = new StringBuilder();
    193         sb.append("RssiCurve[start=")
    194                 .append(start)
    195                 .append(",bucketWidth=")
    196                 .append(bucketWidth)
    197                 .append(",activeNetworkRssiBoost=")
    198                 .append(activeNetworkRssiBoost);
    199 
    200         sb.append(",buckets=");
    201         for (int i = 0; i < rssiBuckets.length; i++) {
    202             sb.append(rssiBuckets[i]);
    203             if (i < rssiBuckets.length - 1) {
    204                 sb.append(",");
    205             }
    206         }
    207         sb.append("]");
    208 
    209         return sb.toString();
    210     }
    211 
    212     public static final Creator<RssiCurve> CREATOR =
    213             new Creator<RssiCurve>() {
    214                 @Override
    215                 public RssiCurve createFromParcel(Parcel in) {
    216                     return new RssiCurve(in);
    217                 }
    218 
    219                 @Override
    220                 public RssiCurve[] newArray(int size) {
    221                     return new RssiCurve[size];
    222                 }
    223             };
    224 }
    225