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