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.net.wifi.QosPolicyParams; 20 import android.net.wifi.WifiManager; 21 22 import com.android.modules.utils.build.SdkLevel; 23 24 import java.io.PrintWriter; 25 import java.util.ArrayDeque; 26 import java.util.ArrayList; 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Queue; 31 import java.util.stream.Collectors; 32 import java.util.stream.Stream; 33 34 /** 35 * Table containing application-added QoS policies that are being tracked by the framework. 36 */ 37 public class ApplicationQosPolicyTrackingTable { 38 private Queue<Integer> mAvailableVirtualPolicyIds = new ArrayDeque<>(); 39 40 // Mapping between a policy hash and a policy object. 41 // See combinePolicyIdAndUid() for more information about the policy hash format. 42 private Map<Long, QosPolicyParams> mPolicyHashToPolicyMap = new HashMap<>(); 43 private Map<Integer, List<Long>> mUidToPolicyHashesMap = new HashMap<>(); 44 ApplicationQosPolicyTrackingTable(int minVirtualPolicyId, int maxVirtualPolicyId)45 public ApplicationQosPolicyTrackingTable(int minVirtualPolicyId, int maxVirtualPolicyId) { 46 for (int i = minVirtualPolicyId; i <= maxVirtualPolicyId; i++) { 47 mAvailableVirtualPolicyIds.add(i); 48 } 49 } 50 generateStatusList( int size, @WifiManager.QosRequestStatus int statusCode)51 private List<Integer> generateStatusList( 52 int size, @WifiManager.QosRequestStatus int statusCode) { 53 List<Integer> statusList = new ArrayList<>(); 54 for (int i = 0; i < size; i++) { 55 statusList.add(statusCode); 56 } 57 return statusList; 58 } 59 60 /** 61 * Combine the provided policyId and UID into a long with the format: 62 * 63 * | Bits 63-32 | Bits 31-0 | 64 * |------------|-----------| 65 * | policyId | uid | 66 * 67 * Long can be used as a unique hash identifying each policy in the table. 68 */ combinePolicyIdAndUid(int policyId, int uid)69 private static long combinePolicyIdAndUid(int policyId, int uid) { 70 long shiftedPolicyId = Integer.toUnsignedLong(policyId) << 32; 71 return shiftedPolicyId | Integer.toUnsignedLong(uid); 72 } 73 getPolicyIdFromCombinedLong(long combined)74 private static int getPolicyIdFromCombinedLong(long combined) { 75 return (int) (combined >> 32); 76 } 77 getUidFromCombinedLong(long combined)78 private static int getUidFromCombinedLong(long combined) { 79 return (int) (combined & 0xFFFFFFFF); 80 } 81 82 /** 83 * Add a list of QoS policies to the tracking table. 84 * 85 * Each accepted policy will be assigned a virtual policy ID using 86 * {@link QosPolicyParams#setTranslatedPolicyId(int)}. 87 * 88 * @param policies List of policies to add. 89 * @param uid UID of the requesting application. 90 * @return List of status codes from {@link WifiManager.QosRequestStatus}. Status list will be 91 * the same length as the input list, and each status code will correspond to the 92 * policy at that index in the input list. 93 */ addPolicies(List<QosPolicyParams> policies, int uid)94 public List<Integer> addPolicies(List<QosPolicyParams> policies, int uid) { 95 if (mAvailableVirtualPolicyIds.size() < policies.size()) { 96 // Not enough space in the table. 97 return generateStatusList( 98 policies.size(), WifiManager.QOS_REQUEST_STATUS_INSUFFICIENT_RESOURCES); 99 } 100 List<Integer> statusList = generateStatusList( 101 policies.size(), WifiManager.QOS_REQUEST_STATUS_TRACKING); 102 103 for (int i = 0; i < policies.size(); i++) { 104 QosPolicyParams policy = policies.get(i); 105 long policyHash = combinePolicyIdAndUid(policy.getPolicyId(), uid); 106 if (mPolicyHashToPolicyMap.containsKey(policyHash)) { 107 // Policy is already in the table. 108 statusList.set(i, WifiManager.QOS_REQUEST_STATUS_ALREADY_ACTIVE); 109 continue; 110 } 111 112 int virtualPolicyId = mAvailableVirtualPolicyIds.remove(); 113 policy.setTranslatedPolicyId(virtualPolicyId); 114 mPolicyHashToPolicyMap.put(policyHash, policy); 115 if (!mUidToPolicyHashesMap.containsKey(uid)) { 116 mUidToPolicyHashesMap.put(uid, new ArrayList<>()); 117 } 118 mUidToPolicyHashesMap.get(uid).add(policyHash); 119 } 120 return statusList; 121 } 122 123 /** 124 * Remove a list of policies from the tracking table. 125 * 126 * Method should be considered best-effort. Any policies in the batch 127 * that are not found will be silently ignored. 128 * 129 * @param policyIds List of policy IDs which should be removed. 130 * @param uid UID of the requesting application. 131 */ removePolicies(List<Integer> policyIds, int uid)132 public void removePolicies(List<Integer> policyIds, int uid) { 133 for (int policyId : policyIds) { 134 long policyHash = combinePolicyIdAndUid(policyId, uid); 135 QosPolicyParams policy = mPolicyHashToPolicyMap.get(policyHash); 136 if (policy == null) { 137 continue; 138 } 139 int virtualPolicyId = policy.getTranslatedPolicyId(); 140 mAvailableVirtualPolicyIds.add(virtualPolicyId); 141 mPolicyHashToPolicyMap.remove(policyHash); 142 mUidToPolicyHashesMap.get(uid).remove(Long.valueOf(policyHash)); 143 if (mUidToPolicyHashesMap.get(uid).isEmpty()) { 144 mUidToPolicyHashesMap.remove(uid); 145 } 146 } 147 } 148 149 /** 150 * Given a list of policies, filter out any polices that are not tracked by the table. 151 * 152 * @param policyList List of policies to filter. 153 * @param uid UID of the requesting application. 154 * @return Filtered list of policies, containing only the policies that are in the table. 155 */ filterUntrackedPolicies( List<QosPolicyParams> policyList, int uid)156 public List<QosPolicyParams> filterUntrackedPolicies( 157 List<QosPolicyParams> policyList, int uid) { 158 List<QosPolicyParams> trackedPolicies = new ArrayList<>(); 159 for (QosPolicyParams policy : policyList) { 160 long policyHash = combinePolicyIdAndUid(policy.getPolicyId(), uid); 161 if (mPolicyHashToPolicyMap.containsKey(policyHash)) { 162 trackedPolicies.add(policy); 163 } 164 } 165 return trackedPolicies; 166 } 167 168 /** 169 * Translate a list of physical policy IDs to virtual. 170 * 171 * Method should be considered best-effort. Any policies in the batch 172 * that are not found will be silently excluded from the returned list. 173 * 174 * @param policyIds List of policy IDs to translate. 175 * @param uid UID of the requesting application. 176 * @return List of virtual policy IDs. 177 */ translatePolicyIds(List<Integer> policyIds, int uid)178 public List<Integer> translatePolicyIds(List<Integer> policyIds, int uid) { 179 List<Integer> virtualPolicyIds = new ArrayList<>(); 180 for (int policyId : policyIds) { 181 long policyHash = combinePolicyIdAndUid(policyId, uid); 182 QosPolicyParams policy = mPolicyHashToPolicyMap.get(policyHash); 183 if (policy == null) { 184 continue; 185 } 186 virtualPolicyIds.add(policy.getTranslatedPolicyId()); 187 } 188 return virtualPolicyIds; 189 } 190 191 /** 192 * Retrieve the IDs for all policies owned by this requester. 193 * 194 * @param uid UID of the requesting application. 195 * @return List of policy IDs. 196 */ getAllPolicyIdsOwnedByUid(int uid)197 public List<Integer> getAllPolicyIdsOwnedByUid(int uid) { 198 List<Integer> policyIds = new ArrayList<>(); 199 List<Long> policyHashes = mUidToPolicyHashesMap.get(uid); 200 if (policyHashes == null) return policyIds; 201 for (long policyHash : policyHashes) { 202 int policyId = getPolicyIdFromCombinedLong(policyHash); 203 policyIds.add(policyId); 204 } 205 return policyIds; 206 } 207 208 /** 209 * Check whether this requester owns any policies in the table. 210 * 211 * @param uid UID of the requesting application. 212 * @return true if the requester owns any policies in the table, false otherwise. 213 */ tableContainsUid(int uid)214 public boolean tableContainsUid(int uid) { 215 return mUidToPolicyHashesMap.containsKey(uid); 216 } 217 218 /** 219 * Get all policies that are tracked by this table. 220 * 221 * @param shouldContainQosChars Whether the returned policies should contain QosCharacteristics. 222 * Only applicable if SDK >= V. 223 * @return List of policies, or empty list if there are no policies in the table. 224 */ getAllPolicies(boolean shouldContainQosChars)225 public List<QosPolicyParams> getAllPolicies(boolean shouldContainQosChars) { 226 if (mPolicyHashToPolicyMap.isEmpty()) { 227 return new ArrayList<>(); 228 } 229 Stream<QosPolicyParams> policyStream = mPolicyHashToPolicyMap.values().stream(); 230 if (SdkLevel.isAtLeastV()) { 231 policyStream = policyStream.filter(p -> 232 shouldContainQosChars == (p.getQosCharacteristics() != null)); 233 } 234 return policyStream.collect(Collectors.toList()); 235 } 236 237 /** 238 * Dump information about the internal state. 239 * 240 * @param pw PrintWriter to write the dump to. 241 */ dump(PrintWriter pw)242 public void dump(PrintWriter pw) { 243 pw.println("Dump of ApplicationQosPolicyTrackingTable"); 244 int numAvailableVirtualPolicyIds = mAvailableVirtualPolicyIds.size(); 245 int numTrackedPolicies = mPolicyHashToPolicyMap.size(); 246 pw.println("Total table size: " + (numAvailableVirtualPolicyIds + numTrackedPolicies)); 247 pw.println("Num available virtual policy IDs: " + numAvailableVirtualPolicyIds); 248 pw.println("Num tracked policies: " + numTrackedPolicies); 249 pw.println(); 250 251 pw.println("Available virtual policy IDs: " + mAvailableVirtualPolicyIds); 252 pw.println("Tracked policies:"); 253 for (Map.Entry<Long, QosPolicyParams> entry : mPolicyHashToPolicyMap.entrySet()) { 254 long policyHash = entry.getKey(); 255 pw.println(" Policy ID: " + getPolicyIdFromCombinedLong(policyHash)); 256 pw.println(" Requester UID: " + getUidFromCombinedLong(policyHash)); 257 pw.println(entry.getValue()); 258 } 259 pw.println(); 260 } 261 } 262