1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Portions copyright (C) 2023 Broadcom Limited
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include <stdint.h>
20 #include <fcntl.h>
21 #include <sys/socket.h>
22 #include <netlink/genl/genl.h>
23 #include <netlink/genl/family.h>
24 #include <netlink/genl/ctrl.h>
25 #include <linux/rtnetlink.h>
26 #include <netpacket/packet.h>
27 #include <linux/filter.h>
28 #include <linux/errqueue.h>
29 
30 #include <linux/pkt_sched.h>
31 #include <netlink/object-api.h>
32 #include <netlink/netlink.h>
33 #include <netlink/socket.h>
34 #include <netlink/handlers.h>
35 
36 #include "sync.h"
37 
38 #define LOG_TAG  "WifiHAL"
39 
40 #include <utils/Log.h>
41 #include <hardware_legacy/wifi_hal.h>
42 #include "common.h"
43 #include "cpp_bindings.h"
44 
45 #define CACHE_SCAN_RESULT_SIZE sizeof(wifi_cached_scan_result)
46 #define MAX_CACHE_SCAN_RESULT_SIZE (MAX_CACHED_SCAN_RESULT*CACHE_SCAN_RESULT_SIZE)
47 typedef enum {
48     WIFI_ATTRIBUTE_CACHED_SCAN_INVALID        = 0,
49     WIFI_ATTRIBUTE_CACHED_SCAN_BOOT_TIMESTAMP = 1,
50     WIFI_ATTRIBUTE_CACHED_SCANNED_FREQ_NUM    = 2,
51     WIFI_ATTRIBUTE_CACHED_SCANNED_FREQ_LIST   = 3,
52     WIFI_ATTRIBUTE_CACHED_SCAN_RESULT_CNT     = 4,
53     WIFI_ATTRIBUTE_CACHED_SCAN_RESULTS        = 5,
54     WIFI_ATTRIBUTE_CACHED_SCAN_MAX
55 } SCAN_ATTRIBUTE;
56 /////////////////////////////////////////////////////////////////////////////
57 
58 class GetCachedScanResultsCommand : public WifiCommand {
59     wifi_cached_scan_result_handler mHandler;
60     wifi_cached_scan_report *mReport;
61 
62 public:
GetCachedScanResultsCommand(wifi_interface_handle iface,int id,wifi_cached_scan_result_handler handler)63     GetCachedScanResultsCommand(wifi_interface_handle iface, int id,
64             wifi_cached_scan_result_handler handler)
65         : WifiCommand("GetCachedScanResultsCommand", iface, id), mHandler(handler)
66     {
67         mReport = NULL;
68     }
69 
create()70     virtual int create() {
71         ALOGE("Creating message to get cached scan results");
72 
73         int ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_GET_CACHED_SCAN_RESULTS);
74         if (ret < 0) {
75             return ret;
76         }
77 
78         return ret;
79     }
80 
81     protected:
handleResponse(WifiEvent & reply)82     virtual int handleResponse(WifiEvent& reply) {
83         ALOGI("In GetCachedScanResultsCommand::handleResponse");
84 
85         if (reply.get_cmd() != NL80211_CMD_VENDOR) {
86             ALOGD("Ignoring reply with cmd = %d", reply.get_cmd());
87             return NL_SKIP;
88         }
89 
90         int id = reply.get_vendor_id();
91         int subcmd = reply.get_vendor_subcmd();
92         int valid_entries = 0;
93         int result_size = 0;
94         wifi_timestamp timestamp = 0;
95         int size = 0;
96 
97         ALOGV("Id = %0x, subcmd = %d", id, subcmd);
98 
99         nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA);
100         int len = reply.get_vendor_data_len();
101 
102         if (vendor_data == NULL || len == 0) {
103             ALOGE("no vendor data in GetCachedScanResults response; ignoring it");
104             return NL_SKIP;
105         }
106 
107         mReport = (wifi_cached_scan_report *)malloc(sizeof(wifi_cached_scan_report));
108         if (mReport == NULL) {
109             ALOGE("Failed to allocate!!\n");
110             return NL_SKIP;
111         }
112         memset(mReport, 0, sizeof(wifi_cached_scan_report));
113 
114         ALOGV("Id = %0x, subcmd = %d, len = %d", id, subcmd, len);
115         for (nl_iterator it(vendor_data); it.has_next(); it.next()) {
116             if (it.get_type() == WIFI_ATTRIBUTE_CACHED_SCAN_BOOT_TIMESTAMP) {
117                 timestamp = it.get_u32();
118                 ALOGV("Time since boot_ms: %ld\n", timestamp);
119             } else if (it.get_type() == WIFI_ATTRIBUTE_CACHED_SCAN_RESULT_CNT) {
120                 valid_entries = min(it.get_u16(), MAX_CACHED_SCAN_RESULT);
121                 ALOGV("cached scan result cnt: %d", valid_entries);
122             } else if ((it.get_type() == WIFI_ATTRIBUTE_CACHED_SCAN_RESULTS) &&
123                 valid_entries && timestamp) {
124                 size = min(it.get_len(), MAX_CACHE_SCAN_RESULT_SIZE);
125                 if (size > (valid_entries*CACHE_SCAN_RESULT_SIZE)) {
126                     ALOGE("Not enough space to copy!!\n");
127                     return NL_SKIP;
128                 }
129                 result_size = valid_entries * CACHE_SCAN_RESULT_SIZE;
130                 mReport->results = (wifi_cached_scan_result *)malloc(result_size);
131                 if (mReport->results == NULL) {
132                     ALOGE("Failed to allocate!!\n");
133                     return NL_SKIP;
134                 }
135                 memcpy((void *)mReport->results, (void *)it.get_data(), size);
136             } else {
137                 ALOGW("Ignoring invalid attribute type = %d, size = %d",
138                         it.get_type(), it.get_len());
139             }
140         }
141 
142         mReport->ts = timestamp;
143         mReport->result_cnt = valid_entries;
144 
145         if (*mHandler.on_cached_scan_results && mReport) {
146             (*mHandler.on_cached_scan_results)(mReport);
147             ALOGV("Notified cache scan report!!");
148         }
149 
150         if (mReport) {
151             if (mReport->results) {
152                 free((void *)mReport->results);
153             }
154             free(mReport);
155         }
156         return NL_OK;
157     }
158 };
159 
wifi_get_cached_scan_results(wifi_interface_handle iface,wifi_cached_scan_result_handler handler)160 wifi_error wifi_get_cached_scan_results(wifi_interface_handle iface,
161     wifi_cached_scan_result_handler handler)
162 {
163     ALOGI("Getting cached scan results, iface handle = %p", iface);
164     GetCachedScanResultsCommand command(iface, 0, handler);
165     return (wifi_error) command.requestResponse();
166 }
167