258 lines
9.9 KiB
Java
258 lines
9.9 KiB
Java
/*
|
|
* Copyright (C) 2022 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.annotation.NonNull;
|
|
import android.net.DscpPolicy;
|
|
import android.net.NetworkAgent;
|
|
import android.os.Handler;
|
|
import android.os.HandlerThread;
|
|
import android.util.Log;
|
|
import android.util.Pair;
|
|
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import com.android.modules.utils.build.SdkLevel;
|
|
import com.android.server.wifi.SupplicantStaIfaceHal.QosPolicyRequest;
|
|
import com.android.server.wifi.SupplicantStaIfaceHal.QosPolicyStatus;
|
|
|
|
import java.io.FileDescriptor;
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
/*
|
|
* Handler for QoS policy requests.
|
|
*/
|
|
public class QosPolicyRequestHandler {
|
|
private static final String TAG = "QosPolicyRequestHandler";
|
|
@VisibleForTesting
|
|
public static final int PROCESSING_TIMEOUT_MILLIS = 500;
|
|
|
|
private final String mInterfaceName;
|
|
private final WifiNative mWifiNative;
|
|
private final ClientModeImpl mClientModeImpl;
|
|
private WifiNetworkAgent mNetworkAgent;
|
|
private Handler mHandler;
|
|
private boolean mVerboseLoggingEnabled;
|
|
|
|
private int mQosRequestDialogToken;
|
|
private int mNumQosPoliciesInRequest;
|
|
private boolean mQosResourcesAvailable;
|
|
private boolean mQosRequestIsProcessing = false;
|
|
private List<QosPolicyStatus> mQosPolicyStatusList = new ArrayList<>();
|
|
private List<Pair<Integer, List<QosPolicyRequest>>> mQosPolicyRequestQueue = new ArrayList<>();
|
|
|
|
public QosPolicyRequestHandler(
|
|
@NonNull String ifaceName, @NonNull WifiNative wifiNative,
|
|
@NonNull ClientModeImpl clientModeImpl, @NonNull HandlerThread handlerThread) {
|
|
mInterfaceName = ifaceName;
|
|
mWifiNative = wifiNative;
|
|
mClientModeImpl = clientModeImpl;
|
|
mHandler = new Handler(handlerThread.getLooper());
|
|
}
|
|
|
|
/**
|
|
* Enable/disable verbose logging.
|
|
*/
|
|
public void enableVerboseLogging(boolean verbose) {
|
|
mVerboseLoggingEnabled = verbose;
|
|
}
|
|
|
|
/**
|
|
* Dump internal state regarding the policy request queue, and the request which is
|
|
* currently being processed.
|
|
*/
|
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
|
pw.println("mQosRequestDialogToken: " + mQosRequestDialogToken);
|
|
pw.println("mNumQosPoliciesInRequest: " + mNumQosPoliciesInRequest);
|
|
pw.println("mQosResourcesAvailable: " + mQosResourcesAvailable);
|
|
pw.println("mQosPolicyStatusList size: " + mQosPolicyStatusList.size());
|
|
for (QosPolicyStatus status : mQosPolicyStatusList) {
|
|
pw.println(" Policy id: " + status.policyId + ", status: "
|
|
+ status.dscpPolicyStatus);
|
|
}
|
|
pw.println("mQosPolicyRequestQueue size: " + mQosPolicyRequestQueue.size());
|
|
for (Pair<Integer, List<QosPolicyRequest>> request : mQosPolicyRequestQueue) {
|
|
pw.println(" Dialog token: " + request.first
|
|
+ ", Num policies: " + request.second.size());
|
|
for (QosPolicyRequest policy : request.second) {
|
|
pw.println(" " + policy);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the network agent.
|
|
*/
|
|
public void setNetworkAgent(WifiNetworkAgent wifiNetworkAgent) {
|
|
WifiNetworkAgent oldNetworkAgent = mNetworkAgent;
|
|
mNetworkAgent = wifiNetworkAgent;
|
|
if (mNetworkAgent == null) {
|
|
mQosPolicyStatusList.clear();
|
|
mQosPolicyRequestQueue.clear();
|
|
mQosRequestIsProcessing = false;
|
|
} else if (oldNetworkAgent != null) {
|
|
// Existing network agent was replaced by a new one.
|
|
resetProcessingState();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Queue a QoS policy request to be processed.
|
|
* @param dialogToken Token identifying the request.
|
|
* @param policies List of policies that we are requesting to set.
|
|
*/
|
|
public void queueQosPolicyRequest(int dialogToken, List<QosPolicyRequest> policies) {
|
|
if (mNetworkAgent == null) {
|
|
Log.e(TAG, "Attempted to call queueQosPolicyRequest, but mNetworkAgent is null");
|
|
return;
|
|
}
|
|
mQosPolicyRequestQueue.add(new Pair(dialogToken, policies));
|
|
processNextQosPolicyRequestIfPossible();
|
|
}
|
|
|
|
/**
|
|
* Set the status for a policy which was processed.
|
|
* @param policyId ID of the policy.
|
|
* @param status code received from the NetworkAgent.
|
|
*/
|
|
public void setQosPolicyStatus(int policyId, int status) {
|
|
if (mNetworkAgent == null) {
|
|
Log.e(TAG, "Attempted to call setQosPolicyStatus, but mNetworkAgent is null");
|
|
return;
|
|
}
|
|
|
|
mQosPolicyStatusList.add(new QosPolicyStatus(policyId, status));
|
|
if (status == NetworkAgent.DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES) {
|
|
mQosResourcesAvailable = false;
|
|
}
|
|
sendQosPolicyResponseIfReady();
|
|
}
|
|
|
|
private void rejectQosPolicy(int policyId) {
|
|
mQosPolicyStatusList.add(new QosPolicyStatus(
|
|
policyId, NetworkAgent.DSCP_POLICY_STATUS_REQUEST_DECLINED));
|
|
sendQosPolicyResponseIfReady();
|
|
}
|
|
|
|
private void sendQosPolicyResponseIfReady() {
|
|
if (mQosRequestIsProcessing && mQosPolicyStatusList.size() == mNumQosPoliciesInRequest) {
|
|
mWifiNative.sendQosPolicyResponse(mInterfaceName, mQosRequestDialogToken,
|
|
mQosResourcesAvailable, mQosPolicyStatusList);
|
|
mQosRequestIsProcessing = false;
|
|
mHandler.post(() -> processNextQosPolicyRequestIfPossible());
|
|
}
|
|
}
|
|
|
|
private void processNextQosPolicyRequestIfPossible() {
|
|
if (!mQosRequestIsProcessing && mQosPolicyRequestQueue.size() != 0) {
|
|
Pair<Integer, List<QosPolicyRequest>> nextRequest = mQosPolicyRequestQueue.get(0);
|
|
mQosPolicyRequestQueue.remove(0);
|
|
mQosRequestIsProcessing = true;
|
|
processQosPolicyRequest(nextRequest.first, nextRequest.second);
|
|
}
|
|
}
|
|
|
|
private void checkForProcessingStall(int dialogToken) {
|
|
if (mQosRequestIsProcessing && dialogToken == mQosRequestDialogToken) {
|
|
Log.e(TAG, "Stop processing stalled QoS request " + dialogToken);
|
|
resetProcessingState();
|
|
}
|
|
}
|
|
|
|
private void resetProcessingState() {
|
|
mQosRequestIsProcessing = false;
|
|
mQosPolicyRequestQueue.clear();
|
|
mClientModeImpl.clearQueuedQosMessages();
|
|
mWifiNative.removeAllQosPolicies(mInterfaceName);
|
|
if (mNetworkAgent != null) {
|
|
mNetworkAgent.sendRemoveAllDscpPolicies();
|
|
}
|
|
}
|
|
|
|
private void processQosPolicyRequest(int dialogToken, List<QosPolicyRequest> policies) {
|
|
if (mNetworkAgent == null) {
|
|
Log.e(TAG, "Attempted to call processQosPolicyRequest, but mNetworkAgent is null");
|
|
return;
|
|
}
|
|
|
|
mQosRequestDialogToken = dialogToken;
|
|
mQosResourcesAvailable = true;
|
|
mNumQosPoliciesInRequest = policies.size();
|
|
mQosPolicyStatusList.clear();
|
|
|
|
if (policies.size() == 0) {
|
|
sendQosPolicyResponseIfReady();
|
|
return;
|
|
}
|
|
|
|
// Reject entire batch if any duplicate policy id's exist.
|
|
Set<Byte> uniquePolicyIds = new HashSet<>();
|
|
for (QosPolicyRequest policy : policies) {
|
|
uniquePolicyIds.add(policy.policyId);
|
|
}
|
|
if (policies.size() != uniquePolicyIds.size()) {
|
|
for (QosPolicyRequest policy : policies) {
|
|
rejectQosPolicy(policy.policyId);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (SdkLevel.isAtLeastT()) {
|
|
for (QosPolicyRequest policy : policies) {
|
|
if (policy.isRemoveRequest()) {
|
|
mNetworkAgent.sendRemoveDscpPolicy(policy.policyId);
|
|
} else if (policy.isAddRequest()) {
|
|
if (!policy.classifierParams.isValid) {
|
|
rejectQosPolicy(policy.policyId);
|
|
continue;
|
|
}
|
|
DscpPolicy.Builder builder = new DscpPolicy.Builder(
|
|
policy.policyId, policy.dscp)
|
|
.setSourcePort(policy.classifierParams.srcPort)
|
|
.setProtocol(policy.classifierParams.protocol)
|
|
.setDestinationPortRange(policy.classifierParams.dstPortRange);
|
|
|
|
// Only set src and dest IP if a value exists in classifierParams.
|
|
if (policy.classifierParams.hasSrcIp) {
|
|
builder.setSourceAddress(policy.classifierParams.srcIp);
|
|
}
|
|
if (policy.classifierParams.hasDstIp) {
|
|
builder.setDestinationAddress(policy.classifierParams.dstIp);
|
|
}
|
|
|
|
try {
|
|
mNetworkAgent.sendAddDscpPolicy(builder.build());
|
|
} catch (IllegalArgumentException e) {
|
|
Log.e(TAG, "Unable to send DSCP policy ", e);
|
|
rejectQosPolicy(policy.policyId);
|
|
continue;
|
|
}
|
|
} else {
|
|
Log.e(TAG, "Unknown request type received");
|
|
rejectQosPolicy(policy.policyId);
|
|
continue;
|
|
}
|
|
}
|
|
mHandler.postDelayed(() -> checkForProcessingStall(dialogToken),
|
|
PROCESSING_TIMEOUT_MILLIS);
|
|
}
|
|
}
|
|
}
|