| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi; |
| |
| import android.app.AlarmManager; |
| import android.content.Context; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkFactory; |
| import android.net.NetworkRequest; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.WifiNetworkSpecifier; |
| import android.os.Looper; |
| import android.os.WorkSource; |
| import android.util.LocalLog; |
| import android.util.Log; |
| import android.util.SparseArray; |
| |
| import com.android.server.wifi.util.WifiPermissionsUtil; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| |
| /** |
| * Network factory to handle multi internet wifi network requests. |
| */ |
| public class MultiInternetWifiNetworkFactory extends NetworkFactory { |
| private static final String TAG = "MultiInternetWifiNetworkFactory"; |
| private static final int SCORE_FILTER = Integer.MAX_VALUE; |
| |
| private final WifiConnectivityManager mWifiConnectivityManager; |
| private final MultiInternetManager mMultiInternetManager; |
| private final WifiPermissionsUtil mWifiPermissionsUtil; |
| private final FrameworkFacade mFacade; |
| private final Context mContext; |
| private final AlarmManager mAlarmManager; |
| private final LocalLog mLocalLog; |
| |
| // Verbose logging flag. |
| private boolean mVerboseLoggingEnabled = false; |
| // Connection request state per each band. |
| private SparseArray<NetworkRequestState> mNetworkRequestStates = new SparseArray<>(); |
| // Connection request count per each band. |
| private SparseArray<Integer> mConnectionReqCount = new SparseArray<>(); |
| |
| // A helper to log debugging information in the local log buffer, which can |
| // be retrieved in bugreport. |
| private void localLog(String log) { |
| mLocalLog.log(log); |
| if (mVerboseLoggingEnabled) Log.v(TAG, log); |
| } |
| |
| /** |
| * Internal network request state for multi internet networks |
| */ |
| public static class NetworkRequestState { |
| public final NetworkRequest networkRequest; |
| public final WifiNetworkSpecifier networkRequestSpecifier; |
| public final boolean isFromSetting; |
| public final boolean isFromForegroundApp; |
| public final boolean isFromForegroundAppOrService; |
| |
| NetworkRequestState(NetworkRequest request, |
| WifiNetworkSpecifier specifier, |
| boolean setting, |
| boolean foregroundApp, |
| boolean foregroundAppOrService) { |
| networkRequest = request; |
| networkRequestSpecifier = specifier; |
| isFromSetting = setting; |
| isFromForegroundApp = foregroundApp; |
| isFromForegroundAppOrService = foregroundAppOrService; |
| } |
| } |
| |
| /** |
| * Check if the network request is for multi internet Wifi network. |
| * @param networkRequest the network requested by connectivity service |
| * @return true if the request if for multi internet Wifi network, false if not. |
| */ |
| public static boolean isWifiMultiInternetRequest(NetworkRequest networkRequest) { |
| if (networkRequest.getNetworkSpecifier() == null |
| || !(networkRequest.getNetworkSpecifier() instanceof WifiNetworkSpecifier)) { |
| return false; |
| } |
| WifiNetworkSpecifier wns = (WifiNetworkSpecifier) networkRequest.getNetworkSpecifier(); |
| // Multi internet request must have internet capability, with specifier of band request, |
| // and must not have SSID/BSSID pattern matcher. |
| if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
| && wns.getBand() != ScanResult.UNSPECIFIED |
| && WifiConfigurationUtil.isMatchAllNetworkSpecifier(wns)) { |
| return true; |
| } |
| return false; |
| } |
| |
| public MultiInternetWifiNetworkFactory(Looper looper, Context context, NetworkCapabilities nc, |
| FrameworkFacade facade, AlarmManager alarmManager, |
| WifiPermissionsUtil wifiPermissionsUtil, |
| MultiInternetManager multiInternetManager, |
| WifiConnectivityManager connectivityManager, |
| LocalLog localLog) { |
| super(looper, context, TAG, nc); |
| mContext = context; |
| mFacade = facade; |
| mAlarmManager = alarmManager; |
| mWifiPermissionsUtil = wifiPermissionsUtil; |
| mMultiInternetManager = multiInternetManager; |
| mWifiConnectivityManager = connectivityManager; |
| mLocalLog = localLog; |
| setScoreFilter(SCORE_FILTER); |
| } |
| |
| /** |
| * Enable verbose logging. |
| */ |
| public void enableVerboseLogging(boolean verbose) { |
| mVerboseLoggingEnabled = verbose; |
| } |
| |
| /** |
| * Check whether to accept the new network connection request. Validate the incoming request |
| * and return true if valid. |
| */ |
| @Override |
| public boolean acceptRequest(NetworkRequest networkRequest) { |
| if (!mMultiInternetManager.isStaConcurrencyForMultiInternetEnabled() |
| || !isWifiMultiInternetRequest(networkRequest)) { |
| return false; |
| } |
| final int uid = networkRequest.getRequestorUid(); |
| boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission(uid) |
| || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid); |
| boolean isFromNetworkStack = mWifiPermissionsUtil.checkNetworkStackPermission(uid) |
| || mWifiPermissionsUtil.checkMainlineNetworkStackPermission(uid); |
| // Only allow specific wifi network request with band from apps or services with settings |
| // or network stack permission. |
| if (!isFromSetting && !isFromNetworkStack) { |
| // Do not release the network request. The app will not get onUnavailable right away, |
| // it can wait when another app with permission make the request and obtain the network. |
| Log.w(TAG, "Request is from app or service does not have the permission." |
| + " Rejecting request from " + networkRequest.getRequestorPackageName()); |
| return false; |
| } |
| WifiNetworkSpecifier wns = (WifiNetworkSpecifier) networkRequest.getNetworkSpecifier(); |
| final int band = wns.getBand(); |
| // TODO: b/181741503 Check if the band is supported. |
| localLog("Accepted network request with specifier for band " + band); |
| return true; |
| } |
| |
| @Override |
| protected void needNetworkFor(NetworkRequest networkRequest) { |
| if (!mMultiInternetManager.isStaConcurrencyForMultiInternetEnabled() |
| || !isWifiMultiInternetRequest(networkRequest)) { |
| return; |
| } |
| WifiNetworkSpecifier wns = (WifiNetworkSpecifier) networkRequest.getNetworkSpecifier(); |
| final int band = wns.getBand(); |
| boolean isFromSetting = mWifiPermissionsUtil.checkNetworkSettingsPermission( |
| networkRequest.getRequestorUid()); |
| boolean isFromForegroundApp = mFacade.isRequestFromForegroundApp(mContext, |
| networkRequest.getRequestorPackageName()); |
| boolean isFromForegroundAppOrService = |
| mFacade.isRequestFromForegroundAppOrService(mContext, |
| networkRequest.getRequestorPackageName()); |
| NetworkRequestState nrs = new NetworkRequestState(networkRequest, |
| new WifiNetworkSpecifier( |
| wns.ssidPatternMatcher, wns.bssidPatternMatcher, wns.getBand(), |
| wns.wifiConfiguration), |
| isFromSetting, |
| isFromForegroundApp, |
| isFromForegroundAppOrService); |
| mNetworkRequestStates.put(band, nrs); |
| // If multi internet is requested, without specifying SSID or BSSID, |
| // The WifiConnectivityManager will perform network selection to choose a candidate. |
| // Create a worksource using the caller's UID. |
| WorkSource workSource = new WorkSource(networkRequest.getRequestorUid()); |
| int reqCount = 0; |
| if (mConnectionReqCount.contains(band)) { |
| reqCount = mConnectionReqCount.get(band); |
| } |
| if (reqCount == 0) { |
| localLog("Need network : Uid " + networkRequest.getRequestorUid() + " PackageName " |
| + networkRequest.getRequestorPackageName() + " for band " + band |
| + " is rom Setting " + isFromSetting + " ForegroundApp " + isFromForegroundApp |
| + " ForegroundAppOrService " + isFromForegroundApp); |
| mMultiInternetManager.setMultiInternetConnectionWorksource( |
| band, new WorkSource(networkRequest.getRequestorUid(), |
| networkRequest.getRequestorPackageName())); |
| } |
| mConnectionReqCount.put(band, reqCount + 1); |
| } |
| |
| @Override |
| protected void releaseNetworkFor(NetworkRequest networkRequest) { |
| if (!isWifiMultiInternetRequest(networkRequest)) { |
| return; |
| } |
| localLog("releaseNetworkFor " + networkRequest); |
| final int band = ((WifiNetworkSpecifier) networkRequest.getNetworkSpecifier()).getBand(); |
| int reqCount = mConnectionReqCount.contains(band) |
| ? mConnectionReqCount.get(band) : 0; |
| if (reqCount == 0) { |
| Log.e(TAG, "No valid network request to release"); |
| return; |
| } |
| if (reqCount == 1) { |
| mMultiInternetManager.setMultiInternetConnectionWorksource(band, null); |
| } |
| mConnectionReqCount.put(band, reqCount - 1); |
| } |
| |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| super.dump(fd, pw, args); |
| pw.println("Dump of MultiInternetWifiNetworkFactory"); |
| for (int i = 0; i < mConnectionReqCount.size(); i++) { |
| final int band = mConnectionReqCount.keyAt(i); |
| NetworkRequestState state = mNetworkRequestStates.get(band); |
| pw.println(" Band " + band + " Req count " + mConnectionReqCount.valueAt(i) |
| + " isFromSetting " + state.isFromSetting |
| + " isFromForegroundApp " + state.isFromForegroundApp |
| + " isFromForegroundAppOrService " + state.isFromForegroundAppOrService |
| + " Uid " + state.networkRequest.getRequestorUid() |
| + " PackageName " + state.networkRequest.getRequestorPackageName()); |
| } |
| mLocalLog.dump(fd, pw, args); |
| mMultiInternetManager.dump(fd, pw, args); |
| } |
| } |