1/*
2 * Copyright (C) 2015 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.net.wifi.AnqpInformationElement;
20import android.net.wifi.ScanResult;
21import android.net.wifi.WifiSsid;
22
23import com.android.server.wifi.anqp.ANQPElement;
24import com.android.server.wifi.anqp.Constants;
25import com.android.server.wifi.anqp.HSFriendlyNameElement;
26import com.android.server.wifi.anqp.RawByteElement;
27import com.android.server.wifi.anqp.VenueNameElement;
28import com.android.server.wifi.hotspot2.NetworkDetail;
29import com.android.server.wifi.hotspot2.PasspointMatch;
30import com.android.server.wifi.hotspot2.Utils;
31import com.android.server.wifi.hotspot2.pps.HomeSP;
32
33import java.util.List;
34import java.util.Map;
35
36/**
37 * Wifi scan result details.
38 */
39public class ScanDetail {
40    private final ScanResult mScanResult;
41    private volatile NetworkDetail mNetworkDetail;
42    private final Map<HomeSP, PasspointMatch> mMatches;
43    private long mSeen = 0;
44
45    public ScanDetail(NetworkDetail networkDetail, WifiSsid wifiSsid, String bssid,
46            String caps, int level, int frequency, long tsf,
47            ScanResult.InformationElement[] informationElements, List<String> anqpLines) {
48        mNetworkDetail = networkDetail;
49        mScanResult = new ScanResult(wifiSsid, bssid, networkDetail.getHESSID(),
50                networkDetail.getAnqpDomainID(), networkDetail.getOsuProviders(),
51                caps, level, frequency, tsf);
52        mSeen = System.currentTimeMillis();
53        //mScanResult.seen = mSeen;
54        mScanResult.channelWidth = networkDetail.getChannelWidth();
55        mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
56        mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
57        mScanResult.informationElements = informationElements;
58        mScanResult.anqpLines = anqpLines;
59        if (networkDetail.is80211McResponderSupport()) {
60            mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
61        }
62        if (networkDetail.isInterworking()) {
63            mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
64        }
65        mMatches = null;
66    }
67
68    public ScanDetail(WifiSsid wifiSsid, String bssid, String caps, int level, int frequency,
69                      long tsf, long seen) {
70        mNetworkDetail = null;
71        mScanResult = new ScanResult(wifiSsid, bssid, 0L, -1, null, caps, level, frequency, tsf);
72        mSeen = seen;
73        //mScanResult.seen = mSeen;
74        mScanResult.channelWidth = 0;
75        mScanResult.centerFreq0 = 0;
76        mScanResult.centerFreq1 = 0;
77        mScanResult.flags = 0;
78        mMatches = null;
79    }
80
81    public ScanDetail(ScanResult scanResult, NetworkDetail networkDetail,
82                       Map<HomeSP, PasspointMatch> matches) {
83        mScanResult = scanResult;
84        mNetworkDetail = networkDetail;
85        mMatches = matches;
86        mSeen = mScanResult.seen;
87    }
88
89    /**
90     * Update the data stored in the scan result with the provided information.
91     *
92     * @param networkDetail NetworkDetail
93     * @param level int
94     * @param wssid WifiSsid
95     * @param ssid String
96     * @param flags String
97     * @param freq int
98     * @param tsf long
99     */
100    public void updateResults(NetworkDetail networkDetail, int level, WifiSsid wssid, String ssid,
101                              String flags, int freq, long tsf) {
102        mScanResult.level = level;
103        mScanResult.wifiSsid = wssid;
104        // Keep existing API
105        mScanResult.SSID = ssid;
106        mScanResult.capabilities = flags;
107        mScanResult.frequency = freq;
108        mScanResult.timestamp = tsf;
109        mSeen = System.currentTimeMillis();
110        //mScanResult.seen = mSeen;
111        mScanResult.channelWidth = networkDetail.getChannelWidth();
112        mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
113        mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
114        if (networkDetail.is80211McResponderSupport()) {
115            mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
116        }
117        if (networkDetail.isInterworking()) {
118            mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
119        }
120    }
121
122    /**
123     * Store ANQ element information
124     *
125     * @param anqpElements Map<Constants.ANQPElementType, ANQPElement>
126     */
127    public void propagateANQPInfo(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
128        if (anqpElements.isEmpty()) {
129            return;
130        }
131        mNetworkDetail = mNetworkDetail.complete(anqpElements);
132        HSFriendlyNameElement fne = (HSFriendlyNameElement) anqpElements.get(
133                Constants.ANQPElementType.HSFriendlyName);
134        // !!! Match with language
135        if (fne != null && !fne.getNames().isEmpty()) {
136            mScanResult.venueName = fne.getNames().get(0).getText();
137        } else {
138            VenueNameElement vne =
139                    (((VenueNameElement) anqpElements.get(
140                            Constants.ANQPElementType.ANQPVenueName)));
141            if (vne != null && !vne.getNames().isEmpty()) {
142                mScanResult.venueName = vne.getNames().get(0).getText();
143            }
144        }
145        RawByteElement osuProviders = (RawByteElement) anqpElements
146                .get(Constants.ANQPElementType.HSOSUProviders);
147        if (osuProviders != null) {
148            mScanResult.anqpElements = new AnqpInformationElement[1];
149            mScanResult.anqpElements[0] =
150                    new AnqpInformationElement(AnqpInformationElement.HOTSPOT20_VENDOR_ID,
151                            AnqpInformationElement.HS_OSU_PROVIDERS, osuProviders.getPayload());
152        }
153    }
154
155    public ScanResult getScanResult() {
156        return mScanResult;
157    }
158
159    public NetworkDetail getNetworkDetail() {
160        return mNetworkDetail;
161    }
162
163    public String getSSID() {
164        return mNetworkDetail == null ? mScanResult.SSID : mNetworkDetail.getSSID();
165    }
166
167    public String getBSSIDString() {
168        return  mNetworkDetail == null ? mScanResult.BSSID : mNetworkDetail.getBSSIDString();
169    }
170
171    /**
172     *  Return the network detail key string.
173     */
174    public String toKeyString() {
175        NetworkDetail networkDetail = mNetworkDetail;
176        if (networkDetail != null) {
177            return networkDetail.toKeyString();
178        } else {
179            return String.format("'%s':%012x",
180                                 mScanResult.BSSID,
181                                 Utils.parseMac(mScanResult.BSSID));
182        }
183    }
184
185    /**
186     * Return the time this network was last seen.
187     */
188    public long getSeen() {
189        return mSeen;
190    }
191
192    /**
193     * Update the time this network was last seen to the current system time.
194     */
195    public long setSeen() {
196        mSeen = System.currentTimeMillis();
197        mScanResult.seen = mSeen;
198        return mSeen;
199    }
200
201    @Override
202    public String toString() {
203        try {
204            return String.format("'%s'/%012x",
205                                 mScanResult.SSID,
206                                 Utils.parseMac(mScanResult.BSSID));
207        } catch (IllegalArgumentException iae) {
208            return String.format("'%s'/----", mScanResult.BSSID);
209        }
210    }
211}
212