1 /* 2 * Copyright (C) 2020 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.connectivity; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.net.IQosCallback; 22 import android.net.Network; 23 import android.net.QosCallbackException; 24 import android.net.QosFilter; 25 import android.net.QosSession; 26 import android.os.Binder; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.telephony.data.EpsBearerQosSessionAttributes; 30 import android.telephony.data.NrQosSessionAttributes; 31 import android.util.Log; 32 33 import com.android.net.module.util.CollectionUtils; 34 import com.android.server.ConnectivityService; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 39 /** 40 * Tracks qos callbacks and handles the communication between the network agent and application. 41 * <p/> 42 * Any method prefixed by handle must be called from the 43 * {@link com.android.server.ConnectivityService} handler thread. 44 * 45 * @hide 46 */ 47 public class QosCallbackTracker { 48 private static final String TAG = QosCallbackTracker.class.getSimpleName(); 49 private static final boolean DBG = true; 50 51 @NonNull 52 private final Handler mConnectivityServiceHandler; 53 54 @NonNull 55 private final ConnectivityService.RequestInfoPerUidCounter mNetworkRequestCounter; 56 57 /** 58 * Each agent gets a unique callback id that is used to proxy messages back to the original 59 * callback. 60 * <p/> 61 * Note: The fact that this is initialized to 0 is to ensure that the thread running 62 * {@link #handleRegisterCallback(IQosCallback, QosFilter, int, NetworkAgentInfo)} sees the 63 * initialized value. This would not necessarily be the case if the value was initialized to 64 * the non-default value. 65 * <p/> 66 * Note: The term previous does not apply to the first callback id that is assigned. 67 */ 68 private int mPreviousAgentCallbackId = 0; 69 70 @NonNull 71 private final List<QosCallbackAgentConnection> mConnections = new ArrayList<>(); 72 73 /** 74 * 75 * @param connectivityServiceHandler must be the same handler used with 76 * {@link com.android.server.ConnectivityService} 77 * @param networkRequestCounter keeps track of the number of open requests under a given 78 * uid 79 */ QosCallbackTracker(@onNull final Handler connectivityServiceHandler, final ConnectivityService.RequestInfoPerUidCounter networkRequestCounter)80 public QosCallbackTracker(@NonNull final Handler connectivityServiceHandler, 81 final ConnectivityService.RequestInfoPerUidCounter networkRequestCounter) { 82 mConnectivityServiceHandler = connectivityServiceHandler; 83 mNetworkRequestCounter = networkRequestCounter; 84 } 85 86 /** 87 * Registers the callback with the tracker 88 * 89 * @param callback the callback to register 90 * @param filter the filter being registered alongside the callback 91 */ registerCallback(@onNull final IQosCallback callback, @NonNull final QosFilter filter, @NonNull final NetworkAgentInfo networkAgentInfo)92 public void registerCallback(@NonNull final IQosCallback callback, 93 @NonNull final QosFilter filter, @NonNull final NetworkAgentInfo networkAgentInfo) { 94 final int uid = Binder.getCallingUid(); 95 96 // Enforce that the number of requests under this uid has exceeded the allowed number 97 mNetworkRequestCounter.incrementCountOrThrow(uid); 98 99 mConnectivityServiceHandler.post( 100 () -> handleRegisterCallback(callback, filter, uid, networkAgentInfo)); 101 } 102 handleRegisterCallback(@onNull final IQosCallback callback, @NonNull final QosFilter filter, final int uid, @NonNull final NetworkAgentInfo networkAgentInfo)103 private void handleRegisterCallback(@NonNull final IQosCallback callback, 104 @NonNull final QosFilter filter, final int uid, 105 @NonNull final NetworkAgentInfo networkAgentInfo) { 106 final QosCallbackAgentConnection ac = 107 handleRegisterCallbackInternal(callback, filter, uid, networkAgentInfo); 108 if (ac != null) { 109 if (DBG) log("handleRegisterCallback: added callback " + ac.getAgentCallbackId()); 110 mConnections.add(ac); 111 } else { 112 mNetworkRequestCounter.decrementCount(uid); 113 } 114 } 115 handleRegisterCallbackInternal( @onNull final IQosCallback callback, @NonNull final QosFilter filter, final int uid, @NonNull final NetworkAgentInfo networkAgentInfo)116 private QosCallbackAgentConnection handleRegisterCallbackInternal( 117 @NonNull final IQosCallback callback, 118 @NonNull final QosFilter filter, final int uid, 119 @NonNull final NetworkAgentInfo networkAgentInfo) { 120 final IBinder binder = callback.asBinder(); 121 if (CollectionUtils.any(mConnections, c -> c.getBinder().equals(binder))) { 122 // A duplicate registration would have only made this far due to a programming error. 123 logwtf("handleRegisterCallback: Callbacks can only be register once."); 124 return null; 125 } 126 127 mPreviousAgentCallbackId = mPreviousAgentCallbackId + 1; 128 final int newCallbackId = mPreviousAgentCallbackId; 129 130 final QosCallbackAgentConnection ac = 131 new QosCallbackAgentConnection(this, newCallbackId, callback, 132 filter, uid, networkAgentInfo); 133 134 final int exceptionType = filter.validate(); 135 if (exceptionType != QosCallbackException.EX_TYPE_FILTER_NONE) { 136 ac.sendEventQosCallbackError(exceptionType); 137 return null; 138 } 139 140 // Only add to the callback maps if the NetworkAgent successfully registered it 141 if (!ac.sendCmdRegisterCallback()) { 142 // There was an issue when registering the agent 143 if (DBG) log("handleRegisterCallback: error sending register callback"); 144 mNetworkRequestCounter.decrementCount(uid); 145 return null; 146 } 147 return ac; 148 } 149 150 /** 151 * Unregisters callback 152 * @param callback callback to unregister 153 */ unregisterCallback(@onNull final IQosCallback callback)154 public void unregisterCallback(@NonNull final IQosCallback callback) { 155 mConnectivityServiceHandler.post(() -> handleUnregisterCallback(callback.asBinder(), true)); 156 } 157 handleUnregisterCallback(@onNull final IBinder binder, final boolean sendToNetworkAgent)158 private void handleUnregisterCallback(@NonNull final IBinder binder, 159 final boolean sendToNetworkAgent) { 160 final int connIndex = 161 CollectionUtils.indexOf(mConnections, c -> c.getBinder().equals(binder)); 162 if (connIndex < 0) { 163 logw("handleUnregisterCallback: no matching agentConnection"); 164 return; 165 } 166 final QosCallbackAgentConnection agentConnection = mConnections.get(connIndex); 167 168 if (DBG) { 169 log("handleUnregisterCallback: unregister " 170 + agentConnection.getAgentCallbackId()); 171 } 172 173 mNetworkRequestCounter.decrementCount(agentConnection.getUid()); 174 mConnections.remove(agentConnection); 175 176 if (sendToNetworkAgent) { 177 agentConnection.sendCmdUnregisterCallback(); 178 } 179 agentConnection.unlinkToDeathRecipient(); 180 } 181 182 /** 183 * Called when the NetworkAgent sends the qos session available event for EPS 184 * 185 * @param qosCallbackId the callback id that the qos session is now available to 186 * @param session the qos session that is now available 187 * @param attributes the qos attributes that are now available on the qos session 188 */ sendEventEpsQosSessionAvailable(final int qosCallbackId, final QosSession session, final EpsBearerQosSessionAttributes attributes)189 public void sendEventEpsQosSessionAvailable(final int qosCallbackId, 190 final QosSession session, 191 final EpsBearerQosSessionAttributes attributes) { 192 runOnAgentConnection(qosCallbackId, "sendEventEpsQosSessionAvailable: ", 193 ac -> ac.sendEventEpsQosSessionAvailable(session, attributes)); 194 } 195 196 /** 197 * Called when the NetworkAgent sends the qos session available event for NR 198 * 199 * @param qosCallbackId the callback id that the qos session is now available to 200 * @param session the qos session that is now available 201 * @param attributes the qos attributes that are now available on the qos session 202 */ sendEventNrQosSessionAvailable(final int qosCallbackId, final QosSession session, final NrQosSessionAttributes attributes)203 public void sendEventNrQosSessionAvailable(final int qosCallbackId, 204 final QosSession session, 205 final NrQosSessionAttributes attributes) { 206 runOnAgentConnection(qosCallbackId, "sendEventNrQosSessionAvailable: ", 207 ac -> ac.sendEventNrQosSessionAvailable(session, attributes)); 208 } 209 210 /** 211 * Called when the NetworkAgent sends the qos session lost event 212 * 213 * @param qosCallbackId the callback id that lost the qos session 214 * @param session the corresponding qos session 215 */ sendEventQosSessionLost(final int qosCallbackId, final QosSession session)216 public void sendEventQosSessionLost(final int qosCallbackId, 217 final QosSession session) { 218 runOnAgentConnection(qosCallbackId, "sendEventQosSessionLost: ", 219 ac -> ac.sendEventQosSessionLost(session)); 220 } 221 222 /** 223 * Called when the NetworkAgent sends the qos session on error event 224 * 225 * @param qosCallbackId the callback id that should receive the exception 226 * @param exceptionType the type of exception that caused the callback to error 227 */ sendEventQosCallbackError(final int qosCallbackId, @QosCallbackException.ExceptionType final int exceptionType)228 public void sendEventQosCallbackError(final int qosCallbackId, 229 @QosCallbackException.ExceptionType final int exceptionType) { 230 runOnAgentConnection(qosCallbackId, "sendEventQosCallbackError: ", 231 ac -> { 232 ac.sendEventQosCallbackError(exceptionType); 233 handleUnregisterCallback(ac.getBinder(), false); 234 }); 235 } 236 237 /** 238 * Unregisters all callbacks associated to this network agent 239 * 240 * Note: Must be called on the connectivity service handler thread 241 * 242 * @param network the network that was released 243 */ handleNetworkReleased(@ullable final Network network)244 public void handleNetworkReleased(@Nullable final Network network) { 245 // Iterate in reverse order as agent connections will be removed when unregistering 246 for (int i = mConnections.size() - 1; i >= 0; i--) { 247 final QosCallbackAgentConnection agentConnection = mConnections.get(i); 248 if (!agentConnection.getNetwork().equals(network)) continue; 249 agentConnection.sendEventQosCallbackError( 250 QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED); 251 252 // Call unregister workflow w\o sending anything to agent since it is disconnected. 253 handleUnregisterCallback(agentConnection.getBinder(), false); 254 } 255 } 256 257 private interface AgentConnectionAction { execute(@onNull QosCallbackAgentConnection agentConnection)258 void execute(@NonNull QosCallbackAgentConnection agentConnection); 259 } 260 261 @Nullable runOnAgentConnection(final int qosCallbackId, @NonNull final String logPrefix, @NonNull final AgentConnectionAction action)262 private void runOnAgentConnection(final int qosCallbackId, 263 @NonNull final String logPrefix, 264 @NonNull final AgentConnectionAction action) { 265 mConnectivityServiceHandler.post(() -> { 266 final int acIndex = CollectionUtils.indexOf(mConnections, 267 c -> c.getAgentCallbackId() == qosCallbackId); 268 if (acIndex == -1) { 269 loge(logPrefix + ": " + qosCallbackId + " missing callback id"); 270 return; 271 } 272 273 action.execute(mConnections.get(acIndex)); 274 }); 275 } 276 log(final String msg)277 private static void log(final String msg) { 278 Log.d(TAG, msg); 279 } 280 logw(final String msg)281 private static void logw(final String msg) { 282 Log.w(TAG, msg); 283 } 284 loge(final String msg)285 private static void loge(final String msg) { 286 Log.e(TAG, msg); 287 } 288 logwtf(final String msg)289 private static void logwtf(final String msg) { 290 Log.wtf(TAG, msg); 291 } 292 } 293