1 /* 2 * Copyright (C) 2016 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.hotspot2; 18 19 import android.util.Log; 20 21 import com.android.internal.annotations.VisibleForTesting; 22 import com.android.server.wifi.Clock; 23 import com.android.server.wifi.hotspot2.anqp.Constants; 24 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Map; 30 31 /** 32 * Class for managing sending of ANQP requests. This manager will ignore ANQP requests for a 33 * period of time (hold off time) to a specified AP if the previous request to that AP goes 34 * unanswered or failed. The hold off time will increase exponentially until the max is reached. 35 */ 36 public class ANQPRequestManager { 37 private static final String TAG = "ANQPRequestManager"; 38 39 private final PasspointEventHandler mPasspointHandler; 40 private final Clock mClock; 41 42 /** 43 * List of pending ANQP request associated with an AP (BSSID). 44 */ 45 private final Map<Long, ANQPNetworkKey> mPendingQueries; 46 47 /** 48 * List of hold off time information associated with APs specified by their BSSID. 49 * Used to determine when an ANQP request can be send to the corresponding AP after the 50 * previous request goes unanswered or failed. 51 */ 52 private final Map<Long, HoldOffInfo> mHoldOffInfo; 53 54 /** 55 * Minimum number of milliseconds to wait for before attempting ANQP queries to the same AP 56 * after previous request goes unanswered or failed. 57 */ 58 @VisibleForTesting 59 public static final int BASE_HOLDOFF_TIME_MILLISECONDS = 10000; 60 61 /** 62 * Max value for the hold off counter for unanswered/failed queries. This limits the maximum 63 * hold off time to: 64 * BASE_HOLDOFF_TIME_MILLISECONDS * 2^MAX_HOLDOFF_COUNT 65 * which is 640 seconds. 66 */ 67 @VisibleForTesting 68 public static final int MAX_HOLDOFF_COUNT = 6; 69 70 private static final List<Constants.ANQPElementType> R1_ANQP_BASE_SET = Arrays.asList( 71 Constants.ANQPElementType.ANQPVenueName, 72 Constants.ANQPElementType.ANQPIPAddrAvailability, 73 Constants.ANQPElementType.ANQPNAIRealm, 74 Constants.ANQPElementType.ANQP3GPPNetwork, 75 Constants.ANQPElementType.ANQPDomName); 76 77 private static final List<Constants.ANQPElementType> R2_ANQP_BASE_SET = Arrays.asList( 78 Constants.ANQPElementType.HSFriendlyName, 79 Constants.ANQPElementType.HSWANMetrics, 80 Constants.ANQPElementType.HSConnCapability, 81 Constants.ANQPElementType.HSOSUProviders); 82 83 /** 84 * Class to keep track of AP status for ANQP requests. 85 */ 86 private class HoldOffInfo { 87 /** 88 * Current hold off count. Will max out at {@link #MAX_HOLDOFF_COUNT}. 89 */ 90 public int holdOffCount; 91 /** 92 * The time stamp in milliseconds when we're allow to send ANQP request to the 93 * corresponding AP. 94 */ 95 public long holdOffExpirationTime; 96 } 97 ANQPRequestManager(PasspointEventHandler handler, Clock clock)98 public ANQPRequestManager(PasspointEventHandler handler, Clock clock) { 99 mPasspointHandler = handler; 100 mClock = clock; 101 mPendingQueries = new HashMap<>(); 102 mHoldOffInfo = new HashMap<>(); 103 } 104 105 /** 106 * Request ANQP elements from the specified AP. This will request the basic Release 1 ANQP 107 * elements {@link #R1_ANQP_BASE_SET}. Additional elements will be requested based on the 108 * information provided in the Information Element (Roaming Consortium OI count and the 109 * supported Hotspot 2.0 release version). 110 * 111 * @param bssid The BSSID of the AP 112 * @param anqpNetworkKey The unique network key associated with this request 113 * @param rcOIs Flag indicating the inclusion of roaming consortium OIs. When set to true, 114 * Roaming Consortium ANQP element will be requested 115 * @param hsReleaseR2 Flag indicating the support of Hotspot 2.0 Release 2. When set to true, 116 * the Release 2 ANQP elements {@link #R2_ANQP_BASE_SET} will be requested 117 * @return true if a request was sent successfully 118 */ requestANQPElements(long bssid, ANQPNetworkKey anqpNetworkKey, boolean rcOIs, boolean hsReleaseR2)119 public boolean requestANQPElements(long bssid, ANQPNetworkKey anqpNetworkKey, boolean rcOIs, 120 boolean hsReleaseR2) { 121 // Check if we are allow to send the request now. 122 if (!canSendRequestNow(bssid)) { 123 return false; 124 } 125 126 // No need to hold off future requests for send failures. 127 if (!mPasspointHandler.requestANQP(bssid, getRequestElementIDs(rcOIs, hsReleaseR2))) { 128 return false; 129 } 130 131 // Update hold off info on when we are allowed to send the next ANQP request to 132 // the given AP. 133 updateHoldOffInfo(bssid); 134 135 mPendingQueries.put(bssid, anqpNetworkKey); 136 return true; 137 } 138 139 /** 140 * Notification of the completion of an ANQP request. 141 * 142 * @param bssid The BSSID of the AP 143 * @param success Flag indicating the result of the query 144 * @return {@link ANQPNetworkKey} associated with the completed request 145 */ onRequestCompleted(long bssid, boolean success)146 public ANQPNetworkKey onRequestCompleted(long bssid, boolean success) { 147 if (success) { 148 // Query succeeded. No need to hold off request to the given AP. 149 mHoldOffInfo.remove(bssid); 150 } 151 return mPendingQueries.remove(bssid); 152 } 153 154 /** 155 * Check if we are allowed to send ANQP request to the specified AP now. 156 * 157 * @param bssid The BSSID of an AP 158 * @return true if we are allowed to send the request now 159 */ canSendRequestNow(long bssid)160 private boolean canSendRequestNow(long bssid) { 161 long currentTime = mClock.getElapsedSinceBootMillis(); 162 HoldOffInfo info = mHoldOffInfo.get(bssid); 163 if (info != null && info.holdOffExpirationTime > currentTime) { 164 Log.d(TAG, "Not allowed to send ANQP request to " + bssid + " for another " 165 + (info.holdOffExpirationTime - currentTime) / 1000 + " seconds"); 166 return false; 167 } 168 169 return true; 170 } 171 172 /** 173 * Update the ANQP request hold off info associated with the given AP. 174 * 175 * @param bssid The BSSID of an AP 176 */ updateHoldOffInfo(long bssid)177 private void updateHoldOffInfo(long bssid) { 178 HoldOffInfo info = mHoldOffInfo.get(bssid); 179 if (info == null) { 180 info = new HoldOffInfo(); 181 mHoldOffInfo.put(bssid, info); 182 } 183 info.holdOffExpirationTime = mClock.getElapsedSinceBootMillis() 184 + BASE_HOLDOFF_TIME_MILLISECONDS * (1 << info.holdOffCount); 185 if (info.holdOffCount < MAX_HOLDOFF_COUNT) { 186 info.holdOffCount++; 187 } 188 } 189 190 /** 191 * Get the list of ANQP element IDs to request based on the Hotspot 2.0 release number 192 * and the ANQP OI count indicated in the Information Element. 193 * 194 * @param rcOIs Flag indicating the inclusion of roaming consortium OIs 195 * @param hsReleaseR2 Flag indicating support of Hotspot 2.0 Release 2 196 * @return List of ANQP Element ID 197 */ getRequestElementIDs(boolean rcOIs, boolean hsReleaseR2)198 private static List<Constants.ANQPElementType> getRequestElementIDs(boolean rcOIs, 199 boolean hsReleaseR2) { 200 List<Constants.ANQPElementType> requestList = new ArrayList<>(); 201 requestList.addAll(R1_ANQP_BASE_SET); 202 if (rcOIs) { 203 requestList.add(Constants.ANQPElementType.ANQPRoamingConsortium); 204 } 205 206 if (hsReleaseR2) { 207 requestList.addAll(R2_ANQP_BASE_SET); 208 } 209 return requestList; 210 } 211 } 212