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