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.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.net.wifi.IListListener; 24 import android.net.wifi.QosPolicyParams; 25 import android.net.wifi.WifiManager; 26 import android.os.Handler; 27 import android.os.HandlerThread; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.modules.utils.build.SdkLevel; 34 import com.android.wifi.resources.R; 35 36 import java.io.PrintWriter; 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.ArrayList; 40 import java.util.Collections; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.Map; 44 45 /** 46 * Handler for QoS policy requests initiated by applications. 47 */ 48 public class ApplicationQosPolicyRequestHandler { 49 private static final String TAG = "ApplicationQosPolicyRequestHandler"; 50 51 // QosPolicyParams objects contain an integer policyId in the range [1, 255], 52 // while the HAL expects a byte policyId in the range [-128, 127]. 53 private static final int HAL_POLICY_ID_MIN = Byte.MIN_VALUE; 54 private static final int HAL_POLICY_ID_MAX = Byte.MAX_VALUE; 55 private static final int MAX_POLICIES_PER_TRANSACTION = 56 WifiManager.getMaxNumberOfPoliciesPerQosRequest(); 57 private static final int DEFAULT_UID = -1; 58 59 // HAL should automatically time out at 1000 ms. Perform a local check at 1500 ms to verify 60 // that either the expected callback, or the timeout callback, was received. 61 @VisibleForTesting 62 protected static final int CALLBACK_TIMEOUT_MILLIS = 1500; 63 64 private final ActiveModeWarden mActiveModeWarden; 65 private final WifiNative mWifiNative; 66 private final Handler mHandler; 67 private final ApCallback mApCallback; 68 private final ApplicationQosPolicyTrackingTable mPolicyTrackingTable; 69 private final ApplicationDeathRecipient mApplicationDeathRecipient; 70 private final DeviceConfigFacade mDeviceConfigFacade; 71 private final Context mContext; 72 private boolean mVerboseLoggingEnabled; 73 74 private Map<String, List<QueuedRequest>> mPerIfaceRequestQueue; 75 private Map<String, CallbackParams> mPendingCallbacks; 76 private Map<IBinder, Integer> mApplicationBinderToUidMap; 77 private Map<Integer, IBinder> mApplicationUidToBinderMap; 78 79 private static final int REQUEST_TYPE_ADD = 0; 80 private static final int REQUEST_TYPE_REMOVE = 1; 81 82 @IntDef(prefix = { "REQUEST_TYPE_" }, value = { 83 REQUEST_TYPE_ADD, 84 REQUEST_TYPE_REMOVE, 85 }) 86 @Retention(RetentionPolicy.SOURCE) 87 private @interface RequestType {} 88 89 private static class QueuedRequest { 90 // Initial state. 91 public final @RequestType int requestType; 92 public final @Nullable List<QosPolicyParams> policiesToAdd; 93 public final @Nullable List<Integer> policyIdsToRemove; 94 public final @NonNull ApplicationCallback callback; 95 public final @Nullable IBinder binder; 96 public final int requesterUid; 97 98 // Set during processing. 99 public boolean processedOnAnyIface; 100 public @Nullable List<Integer> initialStatusList; 101 public @Nullable List<Byte> virtualPolicyIdsToRemove; 102 QueuedRequest(@equestType int inRequestType, @Nullable List<QosPolicyParams> inPoliciesToAdd, @Nullable List<Integer> inPolicyIdsToRemove, @Nullable IListListener inListener, @Nullable IBinder inBinder, int inRequesterUid)103 QueuedRequest(@RequestType int inRequestType, 104 @Nullable List<QosPolicyParams> inPoliciesToAdd, 105 @Nullable List<Integer> inPolicyIdsToRemove, 106 @Nullable IListListener inListener, @Nullable IBinder inBinder, 107 int inRequesterUid) { 108 requestType = inRequestType; 109 policiesToAdd = inPoliciesToAdd; 110 policyIdsToRemove = inPolicyIdsToRemove; 111 callback = new ApplicationCallback(inListener); 112 binder = inBinder; 113 requesterUid = inRequesterUid; 114 processedOnAnyIface = false; 115 } 116 117 @Override toString()118 public String toString() { 119 return "{requestType: " + requestType + ", " 120 + "policiesToAdd: " + policiesToAdd + ", " 121 + "policyIdsToRemove: " + policyIdsToRemove + ", " 122 + "callback: " + callback + ", " 123 + "binder: " + binder + ", " 124 + "requesterUid: " + requesterUid + ", " 125 + "processedOnAnyIface: " + processedOnAnyIface + ", " 126 + "initialStatusList: " + initialStatusList + ", " 127 + "virtualPolicyIdsToRemove: " + virtualPolicyIdsToRemove + "}"; 128 } 129 } 130 131 /** 132 * Wrapper around the calling application's IListListener. 133 * Ensures that the listener is only called once. 134 */ 135 private static class ApplicationCallback { 136 private @Nullable IListListener mListener; 137 ApplicationCallback(@ullable IListListener inListener)138 ApplicationCallback(@Nullable IListListener inListener) { 139 mListener = inListener; 140 } 141 sendResult(List<Integer> statusList)142 public void sendResult(List<Integer> statusList) { 143 if (mListener == null) return; 144 try { 145 mListener.onResult(statusList); 146 } catch (RemoteException e) { 147 Log.e(TAG, "Listener received remote exception " + e); 148 } 149 150 // Set mListener to null to avoid calling again. 151 // The application should only be notified once. 152 mListener = null; 153 } 154 155 /** 156 * Use when all policies should be assigned the same status code. 157 * Ex. If all policies are rejected with the same error code. 158 */ sendResult(int size, @WifiManager.QosRequestStatus int statusCode)159 public void sendResult(int size, @WifiManager.QosRequestStatus int statusCode) { 160 List<Integer> statusList = new ArrayList<>(); 161 for (int i = 0; i < size; i++) { 162 statusList.add(statusCode); 163 } 164 sendResult(statusList); 165 } 166 167 @Override toString()168 public String toString() { 169 return mListener != null ? mListener.toString() : "null"; 170 } 171 } 172 173 /** 174 * Represents a request that has been sent to the HAL and is awaiting the AP callback. 175 */ 176 private static class CallbackParams { 177 public final @NonNull List<Byte> policyIds; 178 CallbackParams(@onNull List<Byte> inPolicyIds)179 CallbackParams(@NonNull List<Byte> inPolicyIds) { 180 Collections.sort(inPolicyIds); 181 policyIds = inPolicyIds; 182 } 183 matchesResults(List<SupplicantStaIfaceHal.QosPolicyStatus> resultList)184 public boolean matchesResults(List<SupplicantStaIfaceHal.QosPolicyStatus> resultList) { 185 List<Byte> resultPolicyIds = new ArrayList<>(); 186 for (SupplicantStaIfaceHal.QosPolicyStatus status : resultList) { 187 resultPolicyIds.add((byte) status.policyId); 188 } 189 Collections.sort(resultPolicyIds); 190 return policyIds.equals(resultPolicyIds); 191 } 192 193 @Override toString()194 public String toString() { 195 return "{policyIds: " + policyIds + "}"; 196 } 197 } 198 199 private class ApCallback implements SupplicantStaIfaceHal.QosScsResponseCallback { 200 @Override onApResponse(String ifaceName, List<SupplicantStaIfaceHal.QosPolicyStatus> halStatusList)201 public void onApResponse(String ifaceName, 202 List<SupplicantStaIfaceHal.QosPolicyStatus> halStatusList) { 203 mHandler.post(() -> { 204 logApCallbackMockable(ifaceName, halStatusList); 205 CallbackParams expectedParams = mPendingCallbacks.get(ifaceName); 206 if (expectedParams == null) { 207 Log.i(TAG, "Callback was not expected on this interface"); 208 return; 209 } 210 211 if (!expectedParams.matchesResults(halStatusList)) { 212 // Silently ignore this callback if it does not match the expected parameters. 213 Log.i(TAG, "Callback was unsolicited. statusList: " + halStatusList); 214 return; 215 } 216 217 Log.i(TAG, "Expected callback was received"); 218 mPendingCallbacks.remove(ifaceName); 219 processNextRequestIfPossible(ifaceName); 220 }); 221 } 222 } 223 224 private class ApplicationDeathRecipient implements IBinder.DeathRecipient { 225 @Override binderDied()226 public void binderDied() { 227 } 228 229 @Override binderDied(@onNull IBinder who)230 public void binderDied(@NonNull IBinder who) { 231 mHandler.post(() -> { 232 Integer uid = mApplicationBinderToUidMap.get(who); 233 Log.i(TAG, "Application binder died. who=" + who + ", uid=" + uid); 234 if (uid == null) { 235 // Application is not registered with us. 236 return; 237 } 238 239 // Remove this application from the tracking maps 240 // and clear out any policies that they own. 241 mApplicationBinderToUidMap.remove(who); 242 mApplicationUidToBinderMap.remove(uid); 243 queueRemoveAllRequest(uid); 244 }); 245 } 246 } 247 ApplicationQosPolicyRequestHandler(@onNull ActiveModeWarden activeModeWarden, @NonNull WifiNative wifiNative, @NonNull HandlerThread handlerThread, @NonNull DeviceConfigFacade deviceConfigFacade, @NonNull Context context)248 public ApplicationQosPolicyRequestHandler(@NonNull ActiveModeWarden activeModeWarden, 249 @NonNull WifiNative wifiNative, @NonNull HandlerThread handlerThread, 250 @NonNull DeviceConfigFacade deviceConfigFacade, @NonNull Context context) { 251 mActiveModeWarden = activeModeWarden; 252 mWifiNative = wifiNative; 253 mHandler = new Handler(handlerThread.getLooper()); 254 mPerIfaceRequestQueue = new HashMap<>(); 255 mPendingCallbacks = new HashMap<>(); 256 mApplicationBinderToUidMap = new HashMap<>(); 257 mApplicationUidToBinderMap = new HashMap<>(); 258 mApCallback = new ApCallback(); 259 mApplicationDeathRecipient = new ApplicationDeathRecipient(); 260 mDeviceConfigFacade = deviceConfigFacade; 261 mContext = context; 262 mVerboseLoggingEnabled = false; 263 mPolicyTrackingTable = 264 new ApplicationQosPolicyTrackingTable(HAL_POLICY_ID_MIN, HAL_POLICY_ID_MAX); 265 mWifiNative.registerQosScsResponseCallback(mApCallback); 266 } 267 268 /** 269 * Enable or disable verbose logging. 270 */ enableVerboseLogging(boolean enable)271 public void enableVerboseLogging(boolean enable) { 272 mVerboseLoggingEnabled = enable; 273 } 274 275 @VisibleForTesting logApCallbackMockable(String ifaceName, List<SupplicantStaIfaceHal.QosPolicyStatus> halStatusList)276 protected void logApCallbackMockable(String ifaceName, 277 List<SupplicantStaIfaceHal.QosPolicyStatus> halStatusList) { 278 Log.i(TAG, "Received AP callback on " + ifaceName + ", size=" + halStatusList.size()); 279 if (mVerboseLoggingEnabled) { 280 long numPoliciesAccepted = halStatusList.stream() 281 .filter(status -> status.statusCode 282 == SupplicantStaIfaceHal.QOS_POLICY_SCS_RESPONSE_STATUS_SUCCESS) 283 .count(); 284 Log.d(TAG, "AP accepted " + numPoliciesAccepted + " policies"); 285 } 286 } 287 288 /** 289 * Check whether the Application QoS policy feature is enabled. 290 * 291 * @return true if the feature is enabled, false otherwise. 292 */ isFeatureEnabled()293 public boolean isFeatureEnabled() { 294 // Both the experiment flag and overlay value must be enabled, 295 // and the HAL must support this feature. 296 return mDeviceConfigFacade.isApplicationQosPolicyApiEnabled() 297 && mContext.getResources().getBoolean( 298 R.bool.config_wifiApplicationCentricQosPolicyFeatureEnabled) 299 && mWifiNative.isSupplicantAidlServiceVersionAtLeast(2); 300 } 301 302 /** 303 * Request to add a list of new QoS policies. 304 * 305 * @param policies List of {@link QosPolicyParams} objects representing the policies. 306 * @param listener Listener to call when the operation is complete. 307 * @param uid UID of the requesting application. 308 */ queueAddRequest(@onNull List<QosPolicyParams> policies, @NonNull IListListener listener, @NonNull IBinder binder, int uid)309 public void queueAddRequest(@NonNull List<QosPolicyParams> policies, 310 @NonNull IListListener listener, @NonNull IBinder binder, int uid) { 311 Log.i(TAG, "Queueing add request. size=" + policies.size()); 312 QueuedRequest request = new QueuedRequest( 313 REQUEST_TYPE_ADD, policies, null, listener, binder, uid); 314 queueRequestOnAllIfaces(request); 315 processNextRequestOnAllIfacesIfPossible(); 316 } 317 318 /** 319 * Request to remove a list of existing QoS policies. 320 * 321 * @param policyIds List of integer policy IDs. 322 * @param uid UID of the requesting application. 323 */ queueRemoveRequest(@onNull List<Integer> policyIds, int uid)324 public void queueRemoveRequest(@NonNull List<Integer> policyIds, int uid) { 325 Log.i(TAG, "Queueing remove request. size=" + policyIds.size()); 326 QueuedRequest request = new QueuedRequest( 327 REQUEST_TYPE_REMOVE, null, policyIds, null, null, uid); 328 queueRequestOnAllIfaces(request); 329 processNextRequestOnAllIfacesIfPossible(); 330 } 331 332 /** 333 * Request to remove all policies owned by this requester. 334 * 335 * @param uid UID of the requesting application. 336 */ queueRemoveAllRequest(int uid)337 public void queueRemoveAllRequest(int uid) { 338 List<Integer> ownedPolicies = mPolicyTrackingTable.getAllPolicyIdsOwnedByUid(uid); 339 Log.i(TAG, "Queueing removeAll request. numOwnedPolicies=" + ownedPolicies.size()); 340 if (ownedPolicies.isEmpty()) return; 341 342 // Divide ownedPolicies into batches of size MAX_POLICIES_PER_TRANSACTION, 343 // and queue each batch on all interfaces. 344 List<List<Integer>> batches = divideRequestIntoBatches(ownedPolicies); 345 for (List<Integer> batch : batches) { 346 QueuedRequest request = new QueuedRequest( 347 REQUEST_TYPE_REMOVE, null, batch, null, null, uid); 348 queueRequestOnAllIfaces(request); 349 } 350 processNextRequestOnAllIfacesIfPossible(); 351 } 352 353 /** 354 * Request to send all tracked policies to the specified interface. 355 * 356 * @param ifaceName Interface name to send the policies to. 357 * @param apSupportsQosChars Whether the AP connected on this interface 358 * supports QosCharacteristics. 359 */ queueAllPoliciesOnIface(String ifaceName, boolean apSupportsQosChars)360 public void queueAllPoliciesOnIface(String ifaceName, boolean apSupportsQosChars) { 361 List<QosPolicyParams> policiesWithoutQosChars = 362 mPolicyTrackingTable.getAllPolicies(false); 363 List<QosPolicyParams> policiesWithQosChars = SdkLevel.isAtLeastV() && apSupportsQosChars 364 ? mPolicyTrackingTable.getAllPolicies(true) : Collections.emptyList(); 365 int totalNumPolicies = policiesWithoutQosChars.size() + policiesWithQosChars.size(); 366 367 Log.i(TAG, "Queueing all policies on iface=" + ifaceName + ". numPolicies=" 368 + totalNumPolicies); 369 Log.i(TAG, policiesWithQosChars.size() + " policies contain QosCharacteristics"); 370 if (totalNumPolicies == 0) return; 371 372 // Divide policies into batches of size MAX_POLICIES_PER_TRANSACTION. Separate batches 373 // should be created for policies that contain QosCharacteristics, and those that do 374 // not contain them. 375 List<List<QosPolicyParams>> batches = new ArrayList<>(); 376 if (!policiesWithoutQosChars.isEmpty()) { 377 batches.addAll(divideRequestIntoBatches(policiesWithoutQosChars)); 378 } 379 if (!policiesWithQosChars.isEmpty()) { 380 batches.addAll(divideRequestIntoBatches(policiesWithQosChars)); 381 } 382 383 // Queue all batches on the specified interface. 384 for (List<QosPolicyParams> batch : batches) { 385 QueuedRequest request = new QueuedRequest( 386 REQUEST_TYPE_ADD, batch, null, null, null, DEFAULT_UID); 387 388 // Indicate that all policies have already been processed and are in the table. 389 request.processedOnAnyIface = true; 390 request.initialStatusList = generateStatusList( 391 batch.size(), WifiManager.QOS_REQUEST_STATUS_TRACKING); 392 queueRequestOnIface(ifaceName, request); 393 } 394 processNextRequestIfPossible(ifaceName); 395 } 396 queueRequestOnAllIfaces(QueuedRequest request)397 private void queueRequestOnAllIfaces(QueuedRequest request) { 398 List<ClientModeManager> clientModeManagers = 399 mActiveModeWarden.getInternetConnectivityClientModeManagers(); 400 if (clientModeManagers.size() == 0) { 401 // Reject request if no ClientModeManagers are available. 402 request.callback.sendResult(request.policiesToAdd.size(), 403 WifiManager.QOS_REQUEST_STATUS_INSUFFICIENT_RESOURCES); 404 return; 405 } 406 407 // Pre-process each request before queueing. 408 if (request.requestType == REQUEST_TYPE_ADD) { 409 List<Integer> statusList = mPolicyTrackingTable.addPolicies( 410 request.policiesToAdd, request.requesterUid); 411 List<QosPolicyParams> acceptedPolicies = 412 filterPoliciesByStatusList(request.policiesToAdd, statusList); 413 if (acceptedPolicies.isEmpty()) { 414 // Tracking table rejected all policies in the request. Table may be full, 415 // or all policies are already being tracked. 416 request.callback.sendResult(statusList); 417 return; 418 } 419 request.initialStatusList = statusList; 420 } else if (request.requestType == REQUEST_TYPE_REMOVE) { 421 List<Integer> virtualPolicyIds = mPolicyTrackingTable.translatePolicyIds( 422 request.policyIdsToRemove, request.requesterUid); 423 if (virtualPolicyIds.isEmpty()) { 424 // None of these policies are being tracked by the table. 425 return; 426 } 427 mPolicyTrackingTable.removePolicies(request.policyIdsToRemove, request.requesterUid); 428 429 List<Byte> virtualPolicyIdBytes = new ArrayList<>(); 430 for (int policyId : virtualPolicyIds) { 431 virtualPolicyIdBytes.add((byte) policyId); 432 } 433 request.virtualPolicyIdsToRemove = virtualPolicyIdBytes; 434 435 // Unregister death handler if this application no longer owns any policies. 436 unregisterDeathHandlerIfNeeded(request.requesterUid); 437 } 438 439 for (ClientModeManager cmm : clientModeManagers) { 440 queueRequestOnIface(cmm.getInterfaceName(), request); 441 } 442 } 443 queueRequestOnIface(String ifaceName, QueuedRequest request)444 private void queueRequestOnIface(String ifaceName, QueuedRequest request) { 445 if (!mPerIfaceRequestQueue.containsKey(ifaceName)) { 446 mPerIfaceRequestQueue.put(ifaceName, new ArrayList<>()); 447 } 448 mPerIfaceRequestQueue.get(ifaceName).add(request); 449 } 450 processNextRequestOnAllIfacesIfPossible()451 private void processNextRequestOnAllIfacesIfPossible() { 452 for (String ifaceName : mPerIfaceRequestQueue.keySet()) { 453 processNextRequestIfPossible(ifaceName); 454 } 455 } 456 processNextRequestIfPossible(String ifaceName)457 private void processNextRequestIfPossible(String ifaceName) { 458 if (mPendingCallbacks.containsKey(ifaceName)) { 459 // Supplicant is still processing a request on this interface. 460 return; 461 } else if (mPerIfaceRequestQueue.get(ifaceName).isEmpty()) { 462 // No requests in this queue. 463 return; 464 } 465 466 QueuedRequest request = mPerIfaceRequestQueue.get(ifaceName).get(0); 467 mPerIfaceRequestQueue.get(ifaceName).remove(0); 468 if (request.requestType == REQUEST_TYPE_ADD) { 469 processAddRequest(ifaceName, request); 470 } else if (request.requestType == REQUEST_TYPE_REMOVE) { 471 processRemoveRequest(ifaceName, request); 472 } 473 } 474 checkForStalledCallback(String ifaceName, CallbackParams processedParams)475 private void checkForStalledCallback(String ifaceName, CallbackParams processedParams) { 476 CallbackParams pendingParams = mPendingCallbacks.get(ifaceName); 477 if (pendingParams == processedParams) { 478 Log.e(TAG, "Callback timed out. Expected params " + pendingParams); 479 mPendingCallbacks.remove(ifaceName); 480 processNextRequestIfPossible(ifaceName); 481 } 482 } 483 484 /** 485 * Divide a large request into batches of max size {@link #MAX_POLICIES_PER_TRANSACTION}. 486 */ 487 @VisibleForTesting divideRequestIntoBatches(List<T> request)488 protected <T> List<List<T>> divideRequestIntoBatches(List<T> request) { 489 List<List<T>> batches = new ArrayList<>(); 490 int startIndex = 0; 491 int endIndex = Math.min(request.size(), MAX_POLICIES_PER_TRANSACTION); 492 while (startIndex < endIndex) { 493 batches.add(request.subList(startIndex, endIndex)); 494 startIndex += MAX_POLICIES_PER_TRANSACTION; 495 endIndex = Math.min(request.size(), endIndex + MAX_POLICIES_PER_TRANSACTION); 496 } 497 return batches; 498 } 499 generateStatusList(int size, @WifiManager.QosRequestStatus int status)500 private List<Integer> generateStatusList(int size, @WifiManager.QosRequestStatus int status) { 501 List<Integer> statusList = new ArrayList<>(); 502 for (int i = 0; i < size; i++) { 503 statusList.add(status); 504 } 505 return statusList; 506 } 507 508 /** 509 * Filter out policies that do not have status code 510 * {@link WifiManager#QOS_REQUEST_STATUS_TRACKING}. 511 */ filterPoliciesByStatusList(List<QosPolicyParams> policyList, List<Integer> statusList)512 private List<QosPolicyParams> filterPoliciesByStatusList(List<QosPolicyParams> policyList, 513 List<Integer> statusList) { 514 List<QosPolicyParams> filteredPolicies = new ArrayList<>(); 515 for (int i = 0; i < statusList.size(); i++) { 516 if (statusList.get(i) == WifiManager.QOS_REQUEST_STATUS_TRACKING) { 517 filteredPolicies.add(policyList.get(i)); 518 } 519 } 520 return filteredPolicies; 521 } 522 processAddRequest(String ifaceName, QueuedRequest request)523 private void processAddRequest(String ifaceName, QueuedRequest request) { 524 boolean previouslyProcessed = request.processedOnAnyIface; 525 request.processedOnAnyIface = true; 526 if (mVerboseLoggingEnabled) { 527 Log.d(TAG, "Processing add request on iface=" + ifaceName + ", size=" 528 + request.policiesToAdd.size()); 529 } 530 531 // Verify that the requesting application is still alive. 532 if (request.binder != null && !request.binder.pingBinder()) { 533 Log.e(TAG, "Requesting application died before processing. request=" + request); 534 processNextRequestIfPossible(ifaceName); 535 return; 536 } 537 538 // Filter out policies that were already in the table during pre-processing. 539 List<Integer> statusList = new ArrayList(request.initialStatusList); 540 List<QosPolicyParams> policyList = filterPoliciesByStatusList( 541 request.policiesToAdd, request.initialStatusList); 542 543 // Filter out policies that were removed from the table in processSynchronousHalResponse(). 544 // Only applies to new policy requests that are queued on multiple interfaces. 545 if (previouslyProcessed && request.requesterUid != DEFAULT_UID) { 546 policyList = mPolicyTrackingTable.filterUntrackedPolicies(policyList, 547 request.requesterUid); 548 } 549 550 if (policyList.isEmpty()) { 551 Log.e(TAG, "All policies were removed during filtering"); 552 processNextRequestIfPossible(ifaceName); 553 return; 554 } 555 556 List<SupplicantStaIfaceHal.QosPolicyStatus> halStatusList = 557 mWifiNative.addQosPolicyRequestForScs(ifaceName, policyList); 558 if (halStatusList == null) { 559 if (!previouslyProcessed) { 560 statusList = handleHalPolicyAddError( 561 statusList, request.policiesToAdd, request.requesterUid); 562 request.callback.sendResult(statusList); 563 } 564 processNextRequestIfPossible(ifaceName); 565 return; 566 } 567 568 if (!previouslyProcessed) { 569 // Send the status list to the requesting application. 570 // Should only be done the first time that a request is processed. 571 statusList = processSynchronousHalResponse( 572 statusList, halStatusList, request.policiesToAdd, request.requesterUid); 573 request.callback.sendResult(statusList); 574 575 // Register death handler if this application owns any policies in the table. 576 registerDeathHandlerIfNeeded(request.requesterUid, request.binder); 577 } 578 579 // Policies that were sent to the AP expect a response from the callback. 580 List<Byte> policiesAwaitingCallback = getPoliciesAwaitingCallback(halStatusList); 581 if (policiesAwaitingCallback.isEmpty()) { 582 processNextRequestIfPossible(ifaceName); 583 } else { 584 CallbackParams cbParams = new CallbackParams(policiesAwaitingCallback); 585 mPendingCallbacks.put(ifaceName, cbParams); 586 mHandler.postDelayed(() -> checkForStalledCallback(ifaceName, cbParams), 587 CALLBACK_TIMEOUT_MILLIS); 588 } 589 } 590 processRemoveRequest(String ifaceName, QueuedRequest request)591 private void processRemoveRequest(String ifaceName, QueuedRequest request) { 592 if (mVerboseLoggingEnabled) { 593 Log.i(TAG, "Processing remove request on iface=" + ifaceName + ", size=" 594 + request.policyIdsToRemove.size()); 595 } 596 List<SupplicantStaIfaceHal.QosPolicyStatus> halStatusList = 597 mWifiNative.removeQosPolicyForScs(ifaceName, request.virtualPolicyIdsToRemove); 598 if (halStatusList == null) { 599 processNextRequestIfPossible(ifaceName); 600 return; 601 } 602 603 // Policies that were sent to the AP expect a response from the callback. 604 List<Byte> policiesAwaitingCallback = getPoliciesAwaitingCallback(halStatusList); 605 if (policiesAwaitingCallback.isEmpty()) { 606 processNextRequestIfPossible(ifaceName); 607 } else { 608 CallbackParams cbParams = new CallbackParams(policiesAwaitingCallback); 609 mPendingCallbacks.put(ifaceName, cbParams); 610 mHandler.postDelayed(() -> checkForStalledCallback(ifaceName, cbParams), 611 CALLBACK_TIMEOUT_MILLIS); 612 } 613 } 614 615 /** 616 * Get the list of policy IDs that are expected in the AP callback. 617 * 618 * Any policies that were sent to the AP will appear in the list. 619 */ getPoliciesAwaitingCallback( List<SupplicantStaIfaceHal.QosPolicyStatus> halStatusList)620 private List<Byte> getPoliciesAwaitingCallback( 621 List<SupplicantStaIfaceHal.QosPolicyStatus> halStatusList) { 622 List<Byte> policiesAwaitingCallback = new ArrayList<>(); 623 for (SupplicantStaIfaceHal.QosPolicyStatus status : halStatusList) { 624 if (status.statusCode == SupplicantStaIfaceHal.QOS_POLICY_SCS_REQUEST_STATUS_SENT) { 625 policiesAwaitingCallback.add((byte) status.policyId); 626 } 627 } 628 629 if (mVerboseLoggingEnabled) { 630 Log.d(TAG, policiesAwaitingCallback.size() 631 + " policies were sent to the AP and are awaiting callback"); 632 } 633 return policiesAwaitingCallback; 634 } 635 halToWifiManagerSyncStatus( @upplicantStaIfaceHal.QosPolicyScsRequestStatusCode int halStatus)636 private static @WifiManager.QosRequestStatus int halToWifiManagerSyncStatus( 637 @SupplicantStaIfaceHal.QosPolicyScsRequestStatusCode int halStatus) { 638 switch (halStatus) { 639 case SupplicantStaIfaceHal.QOS_POLICY_SCS_REQUEST_STATUS_SENT: 640 return WifiManager.QOS_REQUEST_STATUS_TRACKING; 641 case SupplicantStaIfaceHal.QOS_POLICY_SCS_REQUEST_STATUS_ALREADY_ACTIVE: 642 return WifiManager.QOS_REQUEST_STATUS_ALREADY_ACTIVE; 643 case SupplicantStaIfaceHal.QOS_POLICY_SCS_REQUEST_STATUS_INVALID: 644 return WifiManager.QOS_REQUEST_STATUS_INVALID_PARAMETERS; 645 default: 646 return WifiManager.QOS_REQUEST_STATUS_FAILURE_UNKNOWN; 647 } 648 } 649 650 /** 651 * Handle the case where {@link WifiNative#addQosPolicyRequestForScs(String, List)} fails. 652 * 653 * For any policy that was sent to the HAL, assign the proper error code and 654 * remove that policy from the tracking table. 655 */ handleHalPolicyAddError(List<Integer> statusList, List<QosPolicyParams> policyList, int uid)656 private List<Integer> handleHalPolicyAddError(List<Integer> statusList, 657 List<QosPolicyParams> policyList, int uid) { 658 List<Integer> rejectedPolicies = new ArrayList<>(); 659 for (int i = 0; i < statusList.size(); i++) { 660 if (statusList.get(i) != WifiManager.QOS_REQUEST_STATUS_TRACKING) { 661 // Policy was assigned an error code by the tracking table 662 // and was not sent to the HAL. 663 continue; 664 } 665 statusList.set(i, WifiManager.QOS_REQUEST_STATUS_FAILURE_UNKNOWN); 666 rejectedPolicies.add(policyList.get(i).getPolicyId()); 667 } 668 669 // Remove policies that were sent to the HAL from the tracking table. 670 mPolicyTrackingTable.removePolicies(rejectedPolicies, uid); 671 return statusList; 672 } 673 674 /** 675 * Process the status list from {@link WifiNative#addQosPolicyRequestForScs(String, List)}. 676 * 677 * For each policy that was sent to the HAL, merge the HAL status into the main status list. 678 * If any policies were rejected by the HAL, remove them from the policy tracking table. 679 */ 680 @VisibleForTesting processSynchronousHalResponse(List<Integer> statusList, List<SupplicantStaIfaceHal.QosPolicyStatus> halResults, List<QosPolicyParams> policyList, int uid)681 protected List<Integer> processSynchronousHalResponse(List<Integer> statusList, 682 List<SupplicantStaIfaceHal.QosPolicyStatus> halResults, 683 List<QosPolicyParams> policyList, int uid) { 684 int halIndex = 0; 685 List<Integer> rejectedPolicies = new ArrayList<>(); 686 for (int i = 0; i < statusList.size(); i++) { 687 if (statusList.get(i) != WifiManager.QOS_REQUEST_STATUS_TRACKING) { 688 // Policy was assigned an error code by the tracking table 689 // and was not sent to the HAL. 690 continue; 691 } 692 int statusCode = halToWifiManagerSyncStatus(halResults.get(halIndex).statusCode); 693 if (statusCode != WifiManager.QOS_REQUEST_STATUS_TRACKING) { 694 rejectedPolicies.add(policyList.get(i).getPolicyId()); 695 } 696 statusList.set(i, statusCode); 697 halIndex++; 698 } 699 700 if (!rejectedPolicies.isEmpty()) { 701 // Remove policies rejected by the HAL from the tracking table. 702 mPolicyTrackingTable.removePolicies(rejectedPolicies, uid); 703 } 704 return statusList; 705 } 706 707 /** 708 * Register death handler for this application if it owns policies in the tracking table, 709 * and no death handlers have been registered before. 710 */ registerDeathHandlerIfNeeded(int uid, @NonNull IBinder binder)711 private void registerDeathHandlerIfNeeded(int uid, @NonNull IBinder binder) { 712 if (mApplicationUidToBinderMap.containsKey(uid)) { 713 // Application has already been linked to the death recipient. 714 return; 715 } else if (!mPolicyTrackingTable.tableContainsUid(uid)) { 716 // Application does not own any policies in the tracking table. 717 return; 718 } 719 720 try { 721 binder.linkToDeath(mApplicationDeathRecipient, /* flags */ 0); 722 mApplicationBinderToUidMap.put(binder, uid); 723 mApplicationUidToBinderMap.put(uid, binder); 724 } catch (RemoteException e) { 725 Log.wtf(TAG, "Exception occurred while linking to death: " + e); 726 } 727 } 728 729 /** 730 * Unregister the death handler for this application if it 731 * no longer owns any policies in the tracking table. 732 */ unregisterDeathHandlerIfNeeded(int uid)733 private void unregisterDeathHandlerIfNeeded(int uid) { 734 if (!mApplicationUidToBinderMap.containsKey(uid)) { 735 // Application has already been unlinked from the death recipient. 736 return; 737 } else if (mPolicyTrackingTable.tableContainsUid(uid)) { 738 // Application still owns policies in the tracking table. 739 return; 740 } 741 742 IBinder binder = mApplicationUidToBinderMap.get(uid); 743 binder.unlinkToDeath(mApplicationDeathRecipient, /* flags */ 0); 744 mApplicationBinderToUidMap.remove(binder); 745 mApplicationUidToBinderMap.remove(uid); 746 } 747 748 /** 749 * Dump information about the internal state. 750 * 751 * @param pw PrintWriter to write the dump to. 752 */ dump(PrintWriter pw)753 public void dump(PrintWriter pw) { 754 pw.println("Dump of ApplicationQosPolicyRequestHandler"); 755 pw.println("mPerIfaceRequestQueue: " + mPerIfaceRequestQueue); 756 pw.println("mPendingCallbacks: " + mPendingCallbacks); 757 pw.println(); 758 mPolicyTrackingTable.dump(pw); 759 } 760 } 761