1 /*
2  * Copyright (C) 2023 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 com.android.server.wifi;
18 
19 import android.app.StatsManager;
20 import android.content.Context;
21 import android.content.pm.PackageInfo;
22 import android.content.pm.PackageManager;
23 import android.net.wifi.WifiConfiguration;
24 import android.net.wifi.WifiManager;
25 import android.net.wifi.WifiNetworkSuggestion;
26 import android.net.wifi.WifiSsid;
27 import android.os.Handler;
28 import android.os.Process;
29 import android.util.Log;
30 import android.util.StatsEvent;
31 
32 import com.android.server.wifi.proto.WifiStatsLog;
33 
34 import java.util.List;
35 import java.util.Set;
36 
37 /**
38  * This is used to log pulled atoms to StatsD via Callback.
39  */
40 public class WifiPulledAtomLogger {
41     public static final String WIFI_BUILD_FROM_SOURCE_PACKAGE_NAME = "com.android.wifi";
42     private static final String WIFI_PACKAGE_NAME_SUFFIX = ".android.wifi";
43     private int mWifiBuildType = 0;
44 
45     private static final String TAG = "WifiPulledAtomLogger";
46     private final StatsManager mStatsManager;
47     private final Handler mWifiHandler;
48     private final Context mContext;
49     private final WifiInjector mWifiInjector;
50     private StatsManager.StatsPullAtomCallback mStatsPullAtomCallback;
51 
52     private int mApexVersionNumber = -1;
53 
WifiPulledAtomLogger(StatsManager statsManager, Handler handler, Context context, WifiInjector wifiInjector)54     public WifiPulledAtomLogger(StatsManager statsManager, Handler handler, Context context,
55             WifiInjector wifiInjector) {
56         mStatsManager = statsManager;
57         mWifiHandler = handler;
58         mStatsPullAtomCallback = new WifiPullAtomCallback();
59         mContext = context;
60         mWifiInjector = wifiInjector;
61     }
62 
63     /**
64      * Set up an atom to get pulled.
65      * @param atomTag
66      */
setPullAtomCallback(int atomTag)67     public void setPullAtomCallback(int atomTag) {
68         if (mStatsManager == null) {
69             Log.e(TAG, "StatsManager is null. Failed to set wifi pull atom callback for atomTag="
70                     + atomTag);
71             return;
72         }
73         mStatsManager.setPullAtomCallback(
74                 atomTag,
75                 null, // use default meta data values
76                 command -> mWifiHandler.post(command), // Executor posting to wifi handler thread
77                 mStatsPullAtomCallback
78         );
79     }
80 
81     /**
82      * Implementation of StatsPullAtomCallback. This will check the atom tag and log data
83      * correspondingly.
84      */
85     public class WifiPullAtomCallback implements StatsManager.StatsPullAtomCallback {
86         @Override
onPullAtom(int atomTag, List<StatsEvent> data)87         public int onPullAtom(int atomTag, List<StatsEvent> data) {
88             switch (atomTag) {
89                 case WifiStatsLog.WIFI_MODULE_INFO:
90                     return handleWifiVersionPull(atomTag, data);
91                 case WifiStatsLog.WIFI_SETTING_INFO:
92                     return handleWifiSettingsPull(atomTag, data);
93                 case WifiStatsLog.WIFI_COMPLEX_SETTING_INFO:
94                     return handleWifiComplexSettingsPull(atomTag, data);
95                 case WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO:
96                     return handleWifiConfiguredNetworkInfoPull(atomTag, data);
97                 default:
98                     return StatsManager.PULL_SKIP;
99             }
100         }
101     }
102 
handleWifiVersionPull(int atomTag, List<StatsEvent> data)103     private int handleWifiVersionPull(int atomTag, List<StatsEvent> data) {
104         if (mWifiBuildType != 0) {
105             // build type already cached. No need to get it again.
106             data.add(WifiStatsLog.buildStatsEvent(atomTag, mApexVersionNumber, mWifiBuildType));
107             return StatsManager.PULL_SUCCESS;
108         }
109         PackageManager pm = mContext.getPackageManager();
110         if (pm == null) {
111             Log.e(TAG, "Failed to get package manager");
112             return StatsManager.PULL_SKIP;
113         }
114         updateBuildTypeAndVersionCode(pm);
115 
116         data.add(WifiStatsLog.buildStatsEvent(atomTag, mApexVersionNumber, mWifiBuildType));
117         return StatsManager.PULL_SUCCESS;
118     }
119 
handleWifiSettingsPull(int atomTag, List<StatsEvent> data)120     private int handleWifiSettingsPull(int atomTag, List<StatsEvent> data) {
121         WifiSettingsStore settingsStore = mWifiInjector.getWifiSettingsStore();
122         data.add(WifiStatsLog.buildStatsEvent(atomTag,
123                 WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_SCAN_ALWAYS_AVAILABLE,
124                 settingsStore.isScanAlwaysAvailable()));
125         data.add(WifiStatsLog.buildStatsEvent(atomTag,
126                 WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_SCAN_THROTTLE,
127                 settingsStore.isWifiScanThrottleEnabled()));
128         data.add(WifiStatsLog.buildStatsEvent(atomTag,
129                 WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_SCORING,
130                 settingsStore.isWifiScoringEnabled()));
131         data.add(WifiStatsLog.buildStatsEvent(atomTag,
132                 WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_PASSPOINT,
133                 settingsStore.isWifiPasspointEnabled()));
134 
135         boolean nonPersistentMacRandEnabled = mWifiInjector.getFrameworkFacade().getIntegerSetting(
136                 mContext,
137                 WifiConfigManager.NON_PERSISTENT_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG, 0)
138                 == 1 ? true : false;
139         data.add(WifiStatsLog.buildStatsEvent(atomTag,
140                 WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_ENHANCED_MAC_RANDOMIZATION,
141                 nonPersistentMacRandEnabled));
142 
143         data.add(WifiStatsLog.buildStatsEvent(atomTag,
144                 WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_WAKE,
145                 mWifiInjector.getWakeupController().isEnabled()));
146         data.add(WifiStatsLog.buildStatsEvent(atomTag,
147                 WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_NETWORKS_AVAILABLE_NOTIFICATION,
148                 mWifiInjector.getOpenNetworkNotifier().isSettingEnabled()));
149         data.add(WifiStatsLog.buildStatsEvent(atomTag,
150                 WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__LOCATION_MODE,
151                 mWifiInjector.getWifiPermissionsUtil().isLocationModeEnabled()));
152         return StatsManager.PULL_SUCCESS;
153     }
154 
frameworkToAtomMultiInternetMode( @ifiManager.WifiMultiInternetMode int mode)155     private static int frameworkToAtomMultiInternetMode(
156             @WifiManager.WifiMultiInternetMode int mode) {
157         switch (mode) {
158             case WifiManager.WIFI_MULTI_INTERNET_MODE_DISABLED:
159                 return WifiStatsLog
160                         .WIFI_COMPLEX_SETTING_INFO__MULTI_INTERNET_MODE__MULTI_INTERNET_MODE_DISABLED;
161             case WifiManager.WIFI_MULTI_INTERNET_MODE_DBS_AP:
162                 return WifiStatsLog
163                         .WIFI_COMPLEX_SETTING_INFO__MULTI_INTERNET_MODE__MULTI_INTERNET_MODE_DBS_AP;
164             case WifiManager.WIFI_MULTI_INTERNET_MODE_MULTI_AP:
165                 return WifiStatsLog
166                         .WIFI_COMPLEX_SETTING_INFO__MULTI_INTERNET_MODE__MULTI_INTERNET_MODE_MULTI_AP;
167             default:
168                 Log.i(TAG, "Invalid multi-internet mode: " + mode);
169                 return -1;
170         }
171     }
172 
handleWifiComplexSettingsPull(int atomTag, List<StatsEvent> data)173     private int handleWifiComplexSettingsPull(int atomTag, List<StatsEvent> data) {
174         int multiInternetMode = frameworkToAtomMultiInternetMode(
175                 mWifiInjector.getWifiSettingsStore().getWifiMultiInternetMode());
176         if (multiInternetMode == -1) {
177             return StatsManager.PULL_SKIP;
178         }
179         data.add(WifiStatsLog.buildStatsEvent(atomTag, multiInternetMode));
180         return StatsManager.PULL_SUCCESS;
181     }
182 
updateBuildTypeAndVersionCode(PackageManager pm)183     private void updateBuildTypeAndVersionCode(PackageManager pm) {
184         // Query build type and cache if not already cached.
185         List<PackageInfo> packageInfos = pm.getInstalledPackages(PackageManager.MATCH_APEX);
186         boolean found = false;
187         String wifiPackageName = null;
188         for (PackageInfo packageInfo : packageInfos) {
189             if (packageInfo.packageName.endsWith(WIFI_PACKAGE_NAME_SUFFIX)) {
190                 mApexVersionNumber = (int) packageInfo.getLongVersionCode();
191                 wifiPackageName = packageInfo.packageName;
192                 if (packageInfo.packageName.equals(WIFI_BUILD_FROM_SOURCE_PACKAGE_NAME)) {
193                     found = true;
194                 }
195                 break;
196             }
197         }
198         mWifiBuildType = found
199                 ? WifiStatsLog.WIFI_MODULE_INFO__BUILD_TYPE__TYPE_BUILT_FROM_SOURCE
200                 : WifiStatsLog.WIFI_MODULE_INFO__BUILD_TYPE__TYPE_PREBUILT;
201         Log.i(TAG, "Wifi Module package name is " + wifiPackageName
202                 + ", version is " + mApexVersionNumber);
203     }
204 
configHasUtf8Ssid(WifiConfiguration config)205     private static boolean configHasUtf8Ssid(WifiConfiguration config) {
206         return WifiSsid.fromString(config.SSID).getUtf8Text() != null;
207     }
208 
wifiConfigToStatsEvent( int atomTag, WifiConfiguration config, boolean isSuggestion)209     private StatsEvent wifiConfigToStatsEvent(
210             int atomTag, WifiConfiguration config, boolean isSuggestion) {
211         return WifiStatsLog.buildStatsEvent(
212                 atomTag,
213                 0,  // deprecated network ID field
214                 config.isEnterprise(),
215                 config.hiddenSSID,
216                 config.isPasspoint(),
217                 isSuggestion,
218                 configHasUtf8Ssid(config),
219                 mWifiInjector.getSsidTranslator().isSsidTranslationEnabled(),
220                 false, // legacy TOFU field
221                 !config.getNetworkSelectionStatus().hasNeverDetectedCaptivePortal(),
222                 config.allowAutojoin,
223                 WifiMetrics.convertSecurityModeToProto(config),
224                 WifiMetrics.convertMacRandomizationToProto(config.getMacRandomizationSetting()),
225                 WifiMetrics.convertMeteredOverrideToProto(config.meteredOverride),
226                 WifiMetrics.convertEapMethodToProto(config),
227                 WifiMetrics.convertEapInnerMethodToProto(config),
228                 WifiMetrics.isFreeOpenRoaming(config),
229                 WifiMetrics.isSettledOpenRoaming(config),
230                 WifiMetrics.convertTofuConnectionStateToProto(config),
231                 WifiMetrics.convertTofuDialogStateToProto(config));
232     }
233 
handleWifiConfiguredNetworkInfoPull(int atomTag, List<StatsEvent> data)234     private int handleWifiConfiguredNetworkInfoPull(int atomTag, List<StatsEvent> data) {
235         List<WifiConfiguration> savedConfigs =
236                 mWifiInjector.getWifiConfigManager().getSavedNetworks(Process.WIFI_UID);
237         for (WifiConfiguration config : savedConfigs) {
238             if (!config.isPasspoint()) {
239                 data.add(wifiConfigToStatsEvent(atomTag, config, false));
240             }
241         }
242 
243         Set<WifiNetworkSuggestion> approvedSuggestions =
244                 mWifiInjector.getWifiNetworkSuggestionsManager().getAllApprovedNetworkSuggestions();
245         for (WifiNetworkSuggestion suggestion : approvedSuggestions) {
246             WifiConfiguration config = suggestion.getWifiConfiguration();
247             if (!config.isPasspoint()) {
248                 data.add(wifiConfigToStatsEvent(atomTag, config, true));
249             }
250         }
251 
252         List<WifiConfiguration> passpointConfigs =
253                 mWifiInjector.getPasspointManager().getWifiConfigsForPasspointProfiles(false);
254         for (WifiConfiguration config : passpointConfigs) {
255             data.add(wifiConfigToStatsEvent(atomTag, config, false));
256         }
257 
258         List<WifiConfiguration> passpointSuggestions =
259                 mWifiInjector.getWifiNetworkSuggestionsManager()
260                         .getAllPasspointScanOptimizationSuggestionNetworks(false);
261         for (WifiConfiguration config : passpointSuggestions) {
262             data.add(wifiConfigToStatsEvent(atomTag, config, true));
263         }
264 
265         return StatsManager.PULL_SUCCESS;
266     }
267 }
268