1 /*
2  * Copyright (C) 2021 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.AlarmManager;
20 import android.content.Context;
21 import android.net.NetworkCapabilities;
22 import android.net.NetworkFactory;
23 import android.net.NetworkRequest;
24 import android.net.wifi.ScanResult;
25 import android.net.wifi.WifiNetworkSpecifier;
26 import android.os.Looper;
27 import android.os.WorkSource;
28 import android.util.LocalLog;
29 import android.util.Log;
30 import android.util.SparseArray;
31 
32 import com.android.server.wifi.util.WifiPermissionsUtil;
33 
34 import java.io.FileDescriptor;
35 import java.io.PrintWriter;
36 
37 /**
38  * Network factory to handle multi internet wifi network requests.
39  */
40 public class MultiInternetWifiNetworkFactory extends NetworkFactory {
41     private static final String TAG = "MultiInternetWifiNetworkFactory";
42     private static final int SCORE_FILTER = Integer.MAX_VALUE;
43 
44     private final WifiConnectivityManager mWifiConnectivityManager;
45     private final MultiInternetManager mMultiInternetManager;
46     private final WifiPermissionsUtil mWifiPermissionsUtil;
47     private final FrameworkFacade mFacade;
48     private final Context mContext;
49     private final AlarmManager mAlarmManager;
50     private final LocalLog mLocalLog;
51 
52     // Verbose logging flag.
53     private boolean mVerboseLoggingEnabled = false;
54     // Connection request state per each band.
55     private SparseArray<NetworkRequestState> mNetworkRequestStates = new SparseArray<>();
56     // Connection request count per each band.
57     private SparseArray<Integer> mConnectionReqCount = new SparseArray<>();
58 
59     // A helper to log debugging information in the local log buffer, which can
60     // be retrieved in bugreport.
localLog(String log)61     private void localLog(String log) {
62         mLocalLog.log(log);
63         if (mVerboseLoggingEnabled) Log.v(TAG, log);
64     }
65 
66     /**
67      * Internal network request state for multi internet networks
68      */
69     public static class NetworkRequestState {
70         public final NetworkRequest networkRequest;
71         public final WifiNetworkSpecifier networkRequestSpecifier;
72         public final boolean isFromSetting;
73         public final boolean isFromForegroundApp;
74         public final boolean isFromForegroundAppOrService;
75 
NetworkRequestState(NetworkRequest request, WifiNetworkSpecifier specifier, boolean setting, boolean foregroundApp, boolean foregroundAppOrService)76         NetworkRequestState(NetworkRequest request,
77                 WifiNetworkSpecifier specifier,
78                 boolean setting,
79                 boolean foregroundApp,
80                 boolean foregroundAppOrService) {
81             networkRequest = request;
82             networkRequestSpecifier = specifier;
83             isFromSetting = setting;
84             isFromForegroundApp = foregroundApp;
85             isFromForegroundAppOrService = foregroundAppOrService;
86         }
87     }
88 
89     /**
90      * Check if the network request is for multi internet Wifi network.
91      * @param networkRequest the network requested by connectivity service
92      * @return true if the request if for multi internet Wifi network, false if not.
93      */
isWifiMultiInternetRequest(NetworkRequest networkRequest, boolean isFromSettings)94     public static boolean isWifiMultiInternetRequest(NetworkRequest networkRequest,
95             boolean isFromSettings) {
96         if (networkRequest.getNetworkSpecifier() == null
97                 || !(networkRequest.getNetworkSpecifier() instanceof WifiNetworkSpecifier)) {
98             return false;
99         }
100         WifiNetworkSpecifier wns = (WifiNetworkSpecifier) networkRequest.getNetworkSpecifier();
101         // Multi internet request must have internet capability, with specifier of band request.
102         // It must not have SSID/BSSID pattern matcher - except a request from Settings which can
103         // specify a BSSID (while an SSID specification is allowed here, it is dropped later on).
104         if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
105                 && wns.getBand() != ScanResult.UNSPECIFIED
106                 && (isFromSettings || WifiConfigurationUtil.isMatchAllNetworkSpecifier(wns))) {
107             return true;
108         }
109         return false;
110     }
111 
MultiInternetWifiNetworkFactory(Looper looper, Context context, NetworkCapabilities nc, FrameworkFacade facade, AlarmManager alarmManager, WifiPermissionsUtil wifiPermissionsUtil, MultiInternetManager multiInternetManager, WifiConnectivityManager connectivityManager, LocalLog localLog)112     public MultiInternetWifiNetworkFactory(Looper looper, Context context, NetworkCapabilities nc,
113             FrameworkFacade facade, AlarmManager alarmManager,
114             WifiPermissionsUtil wifiPermissionsUtil,
115             MultiInternetManager multiInternetManager,
116             WifiConnectivityManager connectivityManager,
117             LocalLog localLog) {
118         super(looper, context, TAG, nc);
119         mContext = context;
120         mFacade = facade;
121         mAlarmManager = alarmManager;
122         mWifiPermissionsUtil = wifiPermissionsUtil;
123         mMultiInternetManager = multiInternetManager;
124         mWifiConnectivityManager = connectivityManager;
125         mLocalLog = localLog;
126         setScoreFilter(SCORE_FILTER);
127     }
128 
129     /**
130      * Enable verbose logging.
131      */
enableVerboseLogging(boolean verbose)132     public void enableVerboseLogging(boolean verbose) {
133         mVerboseLoggingEnabled = verbose;
134     }
135 
136     /**
137      * Check whether to accept the new network connection request. Validate the incoming request
138      * and return true if valid.
139      */
140     @Override
acceptRequest(NetworkRequest networkRequest)141     public boolean acceptRequest(NetworkRequest networkRequest) {
142         final int uid = networkRequest.getRequestorUid();
143         boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
144                 || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid);
145         if (!mMultiInternetManager.isStaConcurrencyForMultiInternetEnabled()
146                 || !isWifiMultiInternetRequest(networkRequest, isFromSetting)) {
147             return false;
148         }
149         boolean isFromNetworkStack = mWifiPermissionsUtil.checkNetworkStackPermission(uid)
150                 || mWifiPermissionsUtil.checkMainlineNetworkStackPermission(uid);
151         // Only allow specific wifi network request with band from apps or services with settings
152         // or network stack permission.
153         if (!isFromSetting && !isFromNetworkStack) {
154             // Do not release the network request. The app will not get onUnavailable right away,
155             // it can wait when another app with permission make the request and obtain the network.
156             Log.w(TAG, "Request is from app or service does not have the permission."
157                     + " Rejecting request from " + networkRequest.getRequestorPackageName());
158             return false;
159         }
160         WifiNetworkSpecifier wns = (WifiNetworkSpecifier) networkRequest.getNetworkSpecifier();
161         final int band = wns.getBand();
162         // TODO: b/181741503 Check if the band is supported.
163         localLog("Accepted network request with specifier for band " + band);
164         return true;
165     }
166 
167     @Override
needNetworkFor(NetworkRequest networkRequest)168     protected void needNetworkFor(NetworkRequest networkRequest) {
169         boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission(
170                 networkRequest.getRequestorUid());
171         if (!mMultiInternetManager.isStaConcurrencyForMultiInternetEnabled()
172                 || !isWifiMultiInternetRequest(networkRequest, isFromSetting)) {
173             return;
174         }
175         WifiNetworkSpecifier wns = (WifiNetworkSpecifier) networkRequest.getNetworkSpecifier();
176         final int band = wns.getBand();
177         boolean isFromForegroundApp = mFacade.isRequestFromForegroundApp(mContext,
178                 networkRequest.getRequestorPackageName());
179         boolean isFromForegroundAppOrService =
180                 mFacade.isRequestFromForegroundAppOrService(mContext,
181                         networkRequest.getRequestorPackageName());
182         NetworkRequestState nrs = new NetworkRequestState(networkRequest,
183                 new WifiNetworkSpecifier(
184                 wns.ssidPatternMatcher, wns.bssidPatternMatcher, wns.getBand(),
185                 wns.wifiConfiguration, wns.getPreferredChannelFrequenciesMhz()),
186                 isFromSetting,
187                 isFromForegroundApp,
188                 isFromForegroundAppOrService);
189         mNetworkRequestStates.put(band, nrs);
190         // If multi internet is requested, without specifying SSID or BSSID,
191         // The WifiConnectivityManager will perform network selection to choose a candidate.
192         // Create a worksource using the caller's UID.
193         WorkSource workSource = new WorkSource(networkRequest.getRequestorUid());
194         int reqCount = 0;
195         if (mConnectionReqCount.contains(band)) {
196             reqCount = mConnectionReqCount.get(band);
197         }
198         if (reqCount == 0 || isFromSetting) {
199             localLog("Need network : Uid " + networkRequest.getRequestorUid() + " PackageName "
200                     + networkRequest.getRequestorPackageName() + " for band " + band
201                     + " is rom Setting " + isFromSetting + " ForegroundApp " + isFromForegroundApp
202                     + " ForegroundAppOrService " + isFromForegroundApp);
203             mMultiInternetManager.setMultiInternetConnectionWorksource(
204                     band, wns.wifiConfiguration.BSSID,
205                     new WorkSource(networkRequest.getRequestorUid(),
206                     networkRequest.getRequestorPackageName()));
207         }
208         mConnectionReqCount.put(band, reqCount + 1);
209     }
210 
211     @Override
releaseNetworkFor(NetworkRequest networkRequest)212     protected void releaseNetworkFor(NetworkRequest networkRequest) {
213         boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission(
214                 networkRequest.getRequestorUid());
215         if (!isWifiMultiInternetRequest(networkRequest, isFromSetting)) {
216             return;
217         }
218         localLog("releaseNetworkFor " + networkRequest);
219         final int band = ((WifiNetworkSpecifier) networkRequest.getNetworkSpecifier()).getBand();
220         int reqCount = mConnectionReqCount.contains(band)
221                 ? mConnectionReqCount.get(band) : 0;
222         if (reqCount == 0) {
223             Log.e(TAG, "No valid network request to release");
224             return;
225         }
226         if (reqCount == 1) {
227             mMultiInternetManager.setMultiInternetConnectionWorksource(band, null, null);
228         }
229         mConnectionReqCount.put(band, reqCount - 1);
230     }
231 
232     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)233     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
234         super.dump(fd, pw, args);
235         pw.println("Dump of MultiInternetWifiNetworkFactory");
236         for (int i = 0; i < mConnectionReqCount.size(); i++) {
237             final int band = mConnectionReqCount.keyAt(i);
238             NetworkRequestState state = mNetworkRequestStates.get(band);
239             pw.println("    Band " + band + " Req count " + mConnectionReqCount.valueAt(i)
240                     + " isFromSetting " + state.isFromSetting
241                     + " isFromForegroundApp " + state.isFromForegroundApp
242                     + " isFromForegroundAppOrService " + state.isFromForegroundAppOrService
243                     + " Uid " + state.networkRequest.getRequestorUid()
244                     + " PackageName " + state.networkRequest.getRequestorPackageName());
245         }
246         mMultiInternetManager.dump(fd, pw, args);
247     }
248 }
249