1 /* 2 * Copyright (C) 2022 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.annotation.NonNull; 20 import android.net.DscpPolicy; 21 import android.net.NetworkAgent; 22 import android.os.Handler; 23 import android.os.HandlerThread; 24 import android.util.Log; 25 import android.util.Pair; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.modules.utils.build.SdkLevel; 29 import com.android.server.wifi.SupplicantStaIfaceHal.QosPolicyRequest; 30 import com.android.server.wifi.SupplicantStaIfaceHal.QosPolicyStatus; 31 32 import java.io.FileDescriptor; 33 import java.io.PrintWriter; 34 import java.util.ArrayList; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Set; 38 39 /* 40 * Handler for QoS policy requests. 41 */ 42 public class QosPolicyRequestHandler { 43 private static final String TAG = "QosPolicyRequestHandler"; 44 @VisibleForTesting 45 public static final int PROCESSING_TIMEOUT_MILLIS = 500; 46 47 private final String mInterfaceName; 48 private final WifiNative mWifiNative; 49 private final ClientModeImpl mClientModeImpl; 50 private WifiNetworkAgent mNetworkAgent; 51 private Handler mHandler; 52 private boolean mVerboseLoggingEnabled; 53 54 private int mQosRequestDialogToken; 55 private int mNumQosPoliciesInRequest; 56 private boolean mQosResourcesAvailable; 57 private boolean mQosRequestIsProcessing = false; 58 private List<QosPolicyStatus> mQosPolicyStatusList = new ArrayList<>(); 59 private List<Pair<Integer, List<QosPolicyRequest>>> mQosPolicyRequestQueue = new ArrayList<>(); 60 QosPolicyRequestHandler( @onNull String ifaceName, @NonNull WifiNative wifiNative, @NonNull ClientModeImpl clientModeImpl, @NonNull HandlerThread handlerThread)61 public QosPolicyRequestHandler( 62 @NonNull String ifaceName, @NonNull WifiNative wifiNative, 63 @NonNull ClientModeImpl clientModeImpl, @NonNull HandlerThread handlerThread) { 64 mInterfaceName = ifaceName; 65 mWifiNative = wifiNative; 66 mClientModeImpl = clientModeImpl; 67 mHandler = new Handler(handlerThread.getLooper()); 68 } 69 70 /** 71 * Enable/disable verbose logging. 72 */ enableVerboseLogging(boolean verbose)73 public void enableVerboseLogging(boolean verbose) { 74 mVerboseLoggingEnabled = verbose; 75 } 76 77 /** 78 * Dump internal state regarding the policy request queue, and the request which is 79 * currently being processed. 80 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)81 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 82 pw.println("mQosRequestDialogToken: " + mQosRequestDialogToken); 83 pw.println("mNumQosPoliciesInRequest: " + mNumQosPoliciesInRequest); 84 pw.println("mQosResourcesAvailable: " + mQosResourcesAvailable); 85 pw.println("mQosPolicyStatusList size: " + mQosPolicyStatusList.size()); 86 for (QosPolicyStatus status : mQosPolicyStatusList) { 87 pw.println(" Policy id: " + status.policyId + ", status: " 88 + status.statusCode); 89 } 90 pw.println("mQosPolicyRequestQueue size: " + mQosPolicyRequestQueue.size()); 91 for (Pair<Integer, List<QosPolicyRequest>> request : mQosPolicyRequestQueue) { 92 pw.println(" Dialog token: " + request.first 93 + ", Num policies: " + request.second.size()); 94 for (QosPolicyRequest policy : request.second) { 95 pw.println(" " + policy); 96 } 97 } 98 } 99 100 /** 101 * Set the network agent. 102 */ setNetworkAgent(WifiNetworkAgent wifiNetworkAgent)103 public void setNetworkAgent(WifiNetworkAgent wifiNetworkAgent) { 104 WifiNetworkAgent oldNetworkAgent = mNetworkAgent; 105 mNetworkAgent = wifiNetworkAgent; 106 if (mNetworkAgent == null) { 107 mQosPolicyStatusList.clear(); 108 mQosPolicyRequestQueue.clear(); 109 mQosRequestIsProcessing = false; 110 } else if (oldNetworkAgent != null) { 111 // Existing network agent was replaced by a new one. 112 resetProcessingState(); 113 } 114 } 115 116 /** 117 * Queue a QoS policy request to be processed. 118 * @param dialogToken Token identifying the request. 119 * @param policies List of policies that we are requesting to set. 120 */ queueQosPolicyRequest(int dialogToken, List<QosPolicyRequest> policies)121 public void queueQosPolicyRequest(int dialogToken, List<QosPolicyRequest> policies) { 122 if (mNetworkAgent == null) { 123 Log.e(TAG, "Attempted to call queueQosPolicyRequest, but mNetworkAgent is null"); 124 return; 125 } 126 mQosPolicyRequestQueue.add(new Pair(dialogToken, policies)); 127 processNextQosPolicyRequestIfPossible(); 128 } 129 130 /** 131 * Set the status for a policy which was processed. 132 * @param policyId ID of the policy. 133 * @param status code received from the NetworkAgent. 134 */ setQosPolicyStatus(int policyId, int status)135 public void setQosPolicyStatus(int policyId, int status) { 136 if (mNetworkAgent == null) { 137 Log.e(TAG, "Attempted to call setQosPolicyStatus, but mNetworkAgent is null"); 138 return; 139 } 140 141 mQosPolicyStatusList.add(new QosPolicyStatus(policyId, status)); 142 if (status == NetworkAgent.DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES) { 143 mQosResourcesAvailable = false; 144 } 145 sendQosPolicyResponseIfReady(); 146 } 147 rejectQosPolicy(int policyId)148 private void rejectQosPolicy(int policyId) { 149 mQosPolicyStatusList.add(new QosPolicyStatus( 150 policyId, NetworkAgent.DSCP_POLICY_STATUS_REQUEST_DECLINED)); 151 sendQosPolicyResponseIfReady(); 152 } 153 sendQosPolicyResponseIfReady()154 private void sendQosPolicyResponseIfReady() { 155 if (mQosRequestIsProcessing && mQosPolicyStatusList.size() == mNumQosPoliciesInRequest) { 156 mWifiNative.sendQosPolicyResponse(mInterfaceName, mQosRequestDialogToken, 157 mQosResourcesAvailable, mQosPolicyStatusList); 158 mQosRequestIsProcessing = false; 159 mHandler.post(() -> processNextQosPolicyRequestIfPossible()); 160 } 161 } 162 processNextQosPolicyRequestIfPossible()163 private void processNextQosPolicyRequestIfPossible() { 164 if (!mQosRequestIsProcessing && mQosPolicyRequestQueue.size() != 0) { 165 Pair<Integer, List<QosPolicyRequest>> nextRequest = mQosPolicyRequestQueue.get(0); 166 mQosPolicyRequestQueue.remove(0); 167 mQosRequestIsProcessing = true; 168 processQosPolicyRequest(nextRequest.first, nextRequest.second); 169 } 170 } 171 checkForProcessingStall(int dialogToken)172 private void checkForProcessingStall(int dialogToken) { 173 if (mQosRequestIsProcessing && dialogToken == mQosRequestDialogToken) { 174 Log.e(TAG, "Stop processing stalled QoS request " + dialogToken); 175 resetProcessingState(); 176 } 177 } 178 resetProcessingState()179 private void resetProcessingState() { 180 mQosRequestIsProcessing = false; 181 mQosPolicyRequestQueue.clear(); 182 mClientModeImpl.clearQueuedQosMessages(); 183 mWifiNative.removeAllQosPolicies(mInterfaceName); 184 if (mNetworkAgent != null) { 185 mNetworkAgent.sendRemoveAllDscpPolicies(); 186 } 187 } 188 processQosPolicyRequest(int dialogToken, List<QosPolicyRequest> policies)189 private void processQosPolicyRequest(int dialogToken, List<QosPolicyRequest> policies) { 190 if (mNetworkAgent == null) { 191 Log.e(TAG, "Attempted to call processQosPolicyRequest, but mNetworkAgent is null"); 192 return; 193 } 194 195 mQosRequestDialogToken = dialogToken; 196 mQosResourcesAvailable = true; 197 mNumQosPoliciesInRequest = policies.size(); 198 mQosPolicyStatusList.clear(); 199 200 if (policies.size() == 0) { 201 sendQosPolicyResponseIfReady(); 202 return; 203 } 204 205 // Reject entire batch if any duplicate policy id's exist. 206 Set<Byte> uniquePolicyIds = new HashSet<>(); 207 for (QosPolicyRequest policy : policies) { 208 uniquePolicyIds.add(policy.policyId); 209 } 210 if (policies.size() != uniquePolicyIds.size()) { 211 for (QosPolicyRequest policy : policies) { 212 rejectQosPolicy(policy.policyId); 213 } 214 return; 215 } 216 217 if (SdkLevel.isAtLeastT()) { 218 for (QosPolicyRequest policy : policies) { 219 if (policy.isRemoveRequest()) { 220 mNetworkAgent.sendRemoveDscpPolicy(policy.policyId); 221 } else if (policy.isAddRequest()) { 222 if (!policy.classifierParams.isValid) { 223 rejectQosPolicy(policy.policyId); 224 continue; 225 } 226 DscpPolicy.Builder builder = new DscpPolicy.Builder( 227 policy.policyId, policy.dscp) 228 .setSourcePort(policy.classifierParams.srcPort) 229 .setProtocol(policy.classifierParams.protocol) 230 .setDestinationPortRange(policy.classifierParams.dstPortRange); 231 232 // Only set src and dest IP if a value exists in classifierParams. 233 if (policy.classifierParams.hasSrcIp) { 234 builder.setSourceAddress(policy.classifierParams.srcIp); 235 } 236 if (policy.classifierParams.hasDstIp) { 237 builder.setDestinationAddress(policy.classifierParams.dstIp); 238 } 239 240 try { 241 mNetworkAgent.sendAddDscpPolicy(builder.build()); 242 } catch (IllegalArgumentException e) { 243 Log.e(TAG, "Unable to send DSCP policy ", e); 244 rejectQosPolicy(policy.policyId); 245 continue; 246 } 247 } else { 248 Log.e(TAG, "Unknown request type received"); 249 rejectQosPolicy(policy.policyId); 250 continue; 251 } 252 } 253 mHandler.postDelayed(() -> checkForProcessingStall(dialogToken), 254 PROCESSING_TIMEOUT_MILLIS); 255 } 256 } 257 } 258