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
17package com.android.server.wifi;
18
19import android.Manifest.permission;
20import android.content.Context;
21import android.net.INetworkScoreCache;
22import android.net.NetworkKey;
23import android.net.ScoredNetwork;
24import android.net.wifi.ScanResult;
25import android.net.wifi.WifiManager;
26import android.util.Log;
27
28import java.io.FileDescriptor;
29import java.io.PrintWriter;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33
34public class WifiNetworkScoreCache extends INetworkScoreCache.Stub
35 {
36
37    // A Network scorer returns a score in the range [-128, +127]
38    // We treat the lowest possible score as though there were no score, effectively allowing the
39    // scorer to provide an RSSI threshold below which a network should not be used.
40    public static int INVALID_NETWORK_SCORE = Byte.MIN_VALUE;
41
42    private static String TAG = "WifiNetworkScoreCache";
43    private boolean DBG = true;
44    private final Context mContext;
45
46    // The key is of the form "<ssid>"<bssid>
47    // TODO: What about SSIDs that can't be encoded as UTF-8?
48    private final Map<String, ScoredNetwork> mNetworkCache;
49
50    public WifiNetworkScoreCache(Context context) {
51        mContext = context;
52        mNetworkCache = new HashMap<String, ScoredNetwork>();
53    }
54
55     @Override public final void updateScores(List<android.net.ScoredNetwork> networks) {
56        if (networks == null) {
57            return;
58        }
59        Log.e(TAG, "updateScores list size=" + networks.size());
60
61        synchronized(mNetworkCache) {
62            for (ScoredNetwork network : networks) {
63                String networkKey = buildNetworkKey(network);
64                if (networkKey == null) continue;
65                mNetworkCache.put(networkKey, network);
66            }
67        }
68     }
69
70     @Override public final void clearScores() {
71         synchronized (mNetworkCache) {
72             mNetworkCache.clear();
73         }
74     }
75
76    /**
77     * Returns whether there is any score info for the given ScanResult.
78     *
79     * This includes null-score info, so it should only be used when determining whether to request
80     * scores from the network scorer.
81     */
82    public boolean isScoredNetwork(ScanResult result) {
83        return getScoredNetwork(result) != null;
84    }
85
86    /**
87     * Returns whether there is a non-null score curve for the given ScanResult.
88     *
89     * A null score curve has special meaning - we should never connect to an ephemeral network if
90     * the score curve is null.
91     */
92    public boolean hasScoreCurve(ScanResult result) {
93        ScoredNetwork network = getScoredNetwork(result);
94        return network != null && network.rssiCurve != null;
95    }
96
97    public int getNetworkScore(ScanResult result) {
98
99        int score = INVALID_NETWORK_SCORE;
100
101        ScoredNetwork network = getScoredNetwork(result);
102        if (network != null && network.rssiCurve != null) {
103            score = network.rssiCurve.lookupScore(result.level);
104            if (DBG) {
105                Log.e(TAG, "getNetworkScore found scored network " + network.networkKey
106                        + " score " + Integer.toString(score)
107                        + " RSSI " + result.level);
108            }
109        }
110        return score;
111    }
112
113    public int getNetworkScore(ScanResult result, boolean isActiveNetwork) {
114
115        int score = INVALID_NETWORK_SCORE;
116
117        ScoredNetwork network = getScoredNetwork(result);
118        if (network != null && network.rssiCurve != null) {
119            score = network.rssiCurve.lookupScore(result.level, isActiveNetwork);
120            if (DBG) {
121                Log.e(TAG, "getNetworkScore found scored network " + network.networkKey
122                        + " score " + Integer.toString(score)
123                        + " RSSI " + result.level
124                        + " isActiveNetwork " + isActiveNetwork);
125            }
126        }
127        return score;
128    }
129
130    private ScoredNetwork getScoredNetwork(ScanResult result) {
131        String key = buildNetworkKey(result);
132        if (key == null) return null;
133
134        //find it
135        synchronized(mNetworkCache) {
136            ScoredNetwork network = mNetworkCache.get(key);
137            return network;
138        }
139    }
140
141     private String buildNetworkKey(ScoredNetwork network) {
142        if (network.networkKey == null) return null;
143        if (network.networkKey.wifiKey == null) return null;
144        if (network.networkKey.type == NetworkKey.TYPE_WIFI) {
145            String key = network.networkKey.wifiKey.ssid;
146            if (key == null) return null;
147            if (network.networkKey.wifiKey.bssid != null) {
148                key = key + network.networkKey.wifiKey.bssid;
149            }
150            return key;
151        }
152        return null;
153    }
154
155    private String buildNetworkKey(ScanResult result) {
156        if (result.SSID == null) {
157            return null;
158        }
159        StringBuilder key = new StringBuilder("\"");
160        key.append(result.SSID);
161        key.append("\"");
162        if (result.BSSID != null) {
163            key.append(result.BSSID);
164        }
165        return key.toString();
166    }
167
168    @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
169        mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
170        writer.println("WifiNetworkScoreCache");
171        writer.println("  All score curves:");
172        for (Map.Entry<String, ScoredNetwork> entry : mNetworkCache.entrySet()) {
173            writer.println("    " + entry.getKey() + ": " + entry.getValue().rssiCurve);
174        }
175        writer.println("  Current network scores:");
176        WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
177        for (ScanResult scanResult : wifiManager.getScanResults()) {
178            writer.println("    " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult));
179        }
180    }
181
182}
183