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.net; 18 19 import static android.net.TrafficStats.MB_IN_BYTES; 20 21 import static com.android.internal.util.Preconditions.checkArgument; 22 23 import android.app.usage.NetworkStatsManager; 24 import android.net.DataUsageRequest; 25 import android.net.NetworkStats; 26 import android.net.NetworkStatsHistory; 27 import android.net.NetworkTemplate; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.HandlerThread; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.Messenger; 35 import android.os.Process; 36 import android.os.RemoteException; 37 import android.util.ArrayMap; 38 import android.util.Slog; 39 import android.util.SparseArray; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.net.VpnInfo; 43 44 import java.util.concurrent.atomic.AtomicInteger; 45 46 /** 47 * Manages observers of {@link NetworkStats}. Allows observers to be notified when 48 * data usage has been reported in {@link NetworkStatsService}. An observer can set 49 * a threshold of how much data it cares about to be notified. 50 */ 51 class NetworkStatsObservers { 52 private static final String TAG = "NetworkStatsObservers"; 53 private static final boolean LOGV = false; 54 55 private static final long MIN_THRESHOLD_BYTES = 2 * MB_IN_BYTES; 56 57 private static final int MSG_REGISTER = 1; 58 private static final int MSG_UNREGISTER = 2; 59 private static final int MSG_UPDATE_STATS = 3; 60 61 // All access to this map must be done from the handler thread. 62 // indexed by DataUsageRequest#requestId 63 private final SparseArray<RequestInfo> mDataUsageRequests = new SparseArray<>(); 64 65 // Sequence number of DataUsageRequests 66 private final AtomicInteger mNextDataUsageRequestId = new AtomicInteger(); 67 68 // Lazily instantiated when an observer is registered. 69 private volatile Handler mHandler; 70 71 /** 72 * Creates a wrapper that contains the caller context and a normalized request. 73 * The request should be returned to the caller app, and the wrapper should be sent to this 74 * object through #addObserver by the service handler. 75 * 76 * <p>It will register the observer asynchronously, so it is safe to call from any thread. 77 * 78 * @return the normalized request wrapped within {@link RequestInfo}. 79 */ 80 public DataUsageRequest register(DataUsageRequest inputRequest, Messenger messenger, 81 IBinder binder, int callingUid, @NetworkStatsAccess.Level int accessLevel) { 82 DataUsageRequest request = buildRequest(inputRequest); 83 RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid, 84 accessLevel); 85 86 if (LOGV) Slog.v(TAG, "Registering observer for " + request); 87 getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo)); 88 return request; 89 } 90 91 /** 92 * Unregister a data usage observer. 93 * 94 * <p>It will unregister the observer asynchronously, so it is safe to call from any thread. 95 */ 96 public void unregister(DataUsageRequest request, int callingUid) { 97 getHandler().sendMessage(mHandler.obtainMessage(MSG_UNREGISTER, callingUid, 0 /* ignore */, 98 request)); 99 } 100 101 /** 102 * Updates data usage statistics of registered observers and notifies if limits are reached. 103 * 104 * <p>It will update stats asynchronously, so it is safe to call from any thread. 105 */ 106 public void updateStats(NetworkStats xtSnapshot, NetworkStats uidSnapshot, 107 ArrayMap<String, NetworkIdentitySet> activeIfaces, 108 ArrayMap<String, NetworkIdentitySet> activeUidIfaces, 109 VpnInfo[] vpnArray, long currentTime) { 110 StatsContext statsContext = new StatsContext(xtSnapshot, uidSnapshot, activeIfaces, 111 activeUidIfaces, vpnArray, currentTime); 112 getHandler().sendMessage(mHandler.obtainMessage(MSG_UPDATE_STATS, statsContext)); 113 } 114 115 private Handler getHandler() { 116 if (mHandler == null) { 117 synchronized (this) { 118 if (mHandler == null) { 119 if (LOGV) Slog.v(TAG, "Creating handler"); 120 mHandler = new Handler(getHandlerLooperLocked(), mHandlerCallback); 121 } 122 } 123 } 124 return mHandler; 125 } 126 127 @VisibleForTesting 128 protected Looper getHandlerLooperLocked() { 129 HandlerThread handlerThread = new HandlerThread(TAG); 130 handlerThread.start(); 131 return handlerThread.getLooper(); 132 } 133 134 private Handler.Callback mHandlerCallback = new Handler.Callback() { 135 @Override 136 public boolean handleMessage(Message msg) { 137 switch (msg.what) { 138 case MSG_REGISTER: { 139 handleRegister((RequestInfo) msg.obj); 140 return true; 141 } 142 case MSG_UNREGISTER: { 143 handleUnregister((DataUsageRequest) msg.obj, msg.arg1 /* callingUid */); 144 return true; 145 } 146 case MSG_UPDATE_STATS: { 147 handleUpdateStats((StatsContext) msg.obj); 148 return true; 149 } 150 default: { 151 return false; 152 } 153 } 154 } 155 }; 156 157 /** 158 * Adds a {@link RequestInfo} as an observer. 159 * Should only be called from the handler thread otherwise there will be a race condition 160 * on mDataUsageRequests. 161 */ 162 private void handleRegister(RequestInfo requestInfo) { 163 mDataUsageRequests.put(requestInfo.mRequest.requestId, requestInfo); 164 } 165 166 /** 167 * Removes a {@link DataUsageRequest} if the calling uid is authorized. 168 * Should only be called from the handler thread otherwise there will be a race condition 169 * on mDataUsageRequests. 170 */ 171 private void handleUnregister(DataUsageRequest request, int callingUid) { 172 RequestInfo requestInfo; 173 requestInfo = mDataUsageRequests.get(request.requestId); 174 if (requestInfo == null) { 175 if (LOGV) Slog.v(TAG, "Trying to unregister unknown request " + request); 176 return; 177 } 178 if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) { 179 Slog.w(TAG, "Caller uid " + callingUid + " is not owner of " + request); 180 return; 181 } 182 183 if (LOGV) Slog.v(TAG, "Unregistering " + request); 184 mDataUsageRequests.remove(request.requestId); 185 requestInfo.unlinkDeathRecipient(); 186 requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED); 187 } 188 189 private void handleUpdateStats(StatsContext statsContext) { 190 if (mDataUsageRequests.size() == 0) { 191 return; 192 } 193 194 for (int i = 0; i < mDataUsageRequests.size(); i++) { 195 RequestInfo requestInfo = mDataUsageRequests.valueAt(i); 196 requestInfo.updateStats(statsContext); 197 } 198 } 199 200 private DataUsageRequest buildRequest(DataUsageRequest request) { 201 // Cap the minimum threshold to a safe default to avoid too many callbacks 202 long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes); 203 if (thresholdInBytes < request.thresholdInBytes) { 204 Slog.w(TAG, "Threshold was too low for " + request 205 + ". Overriding to a safer default of " + thresholdInBytes + " bytes"); 206 } 207 return new DataUsageRequest(mNextDataUsageRequestId.incrementAndGet(), 208 request.template, thresholdInBytes); 209 } 210 211 private RequestInfo buildRequestInfo(DataUsageRequest request, 212 Messenger messenger, IBinder binder, int callingUid, 213 @NetworkStatsAccess.Level int accessLevel) { 214 if (accessLevel <= NetworkStatsAccess.Level.USER) { 215 return new UserUsageRequestInfo(this, request, messenger, binder, callingUid, 216 accessLevel); 217 } else { 218 // Safety check in case a new access level is added and we forgot to update this 219 checkArgument(accessLevel >= NetworkStatsAccess.Level.DEVICESUMMARY); 220 return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid, 221 accessLevel); 222 } 223 } 224 225 /** 226 * Tracks information relevant to a data usage observer. 227 * It will notice when the calling process dies so we can self-expire. 228 */ 229 private abstract static class RequestInfo implements IBinder.DeathRecipient { 230 private final NetworkStatsObservers mStatsObserver; 231 protected final DataUsageRequest mRequest; 232 private final Messenger mMessenger; 233 private final IBinder mBinder; 234 protected final int mCallingUid; 235 protected final @NetworkStatsAccess.Level int mAccessLevel; 236 protected NetworkStatsRecorder mRecorder; 237 protected NetworkStatsCollection mCollection; 238 239 RequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request, 240 Messenger messenger, IBinder binder, int callingUid, 241 @NetworkStatsAccess.Level int accessLevel) { 242 mStatsObserver = statsObserver; 243 mRequest = request; 244 mMessenger = messenger; 245 mBinder = binder; 246 mCallingUid = callingUid; 247 mAccessLevel = accessLevel; 248 249 try { 250 mBinder.linkToDeath(this, 0); 251 } catch (RemoteException e) { 252 binderDied(); 253 } 254 } 255 256 @Override 257 public void binderDied() { 258 if (LOGV) Slog.v(TAG, "RequestInfo binderDied(" 259 + mRequest + ", " + mBinder + ")"); 260 mStatsObserver.unregister(mRequest, Process.SYSTEM_UID); 261 callCallback(NetworkStatsManager.CALLBACK_RELEASED); 262 } 263 264 @Override 265 public String toString() { 266 return "RequestInfo from uid:" + mCallingUid 267 + " for " + mRequest + " accessLevel:" + mAccessLevel; 268 } 269 270 private void unlinkDeathRecipient() { 271 if (mBinder != null) { 272 mBinder.unlinkToDeath(this, 0); 273 } 274 } 275 276 /** 277 * Update stats given the samples and interface to identity mappings. 278 */ 279 private void updateStats(StatsContext statsContext) { 280 if (mRecorder == null) { 281 // First run; establish baseline stats 282 resetRecorder(); 283 recordSample(statsContext); 284 return; 285 } 286 recordSample(statsContext); 287 288 if (checkStats()) { 289 resetRecorder(); 290 callCallback(NetworkStatsManager.CALLBACK_LIMIT_REACHED); 291 } 292 } 293 294 private void callCallback(int callbackType) { 295 Bundle bundle = new Bundle(); 296 bundle.putParcelable(DataUsageRequest.PARCELABLE_KEY, mRequest); 297 Message msg = Message.obtain(); 298 msg.what = callbackType; 299 msg.setData(bundle); 300 try { 301 if (LOGV) { 302 Slog.v(TAG, "sending notification " + callbackTypeToName(callbackType) 303 + " for " + mRequest); 304 } 305 mMessenger.send(msg); 306 } catch (RemoteException e) { 307 // May occur naturally in the race of binder death. 308 Slog.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest); 309 } 310 } 311 312 private void resetRecorder() { 313 mRecorder = new NetworkStatsRecorder(); 314 mCollection = mRecorder.getSinceBoot(); 315 } 316 317 protected abstract boolean checkStats(); 318 319 protected abstract void recordSample(StatsContext statsContext); 320 321 private String callbackTypeToName(int callbackType) { 322 switch (callbackType) { 323 case NetworkStatsManager.CALLBACK_LIMIT_REACHED: 324 return "LIMIT_REACHED"; 325 case NetworkStatsManager.CALLBACK_RELEASED: 326 return "RELEASED"; 327 default: 328 return "UNKNOWN"; 329 } 330 } 331 } 332 333 private static class NetworkUsageRequestInfo extends RequestInfo { 334 NetworkUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request, 335 Messenger messenger, IBinder binder, int callingUid, 336 @NetworkStatsAccess.Level int accessLevel) { 337 super(statsObserver, request, messenger, binder, callingUid, accessLevel); 338 } 339 340 @Override 341 protected boolean checkStats() { 342 long bytesSoFar = getTotalBytesForNetwork(mRequest.template); 343 if (LOGV) { 344 Slog.v(TAG, bytesSoFar + " bytes so far since notification for " 345 + mRequest.template); 346 } 347 if (bytesSoFar > mRequest.thresholdInBytes) { 348 return true; 349 } 350 return false; 351 } 352 353 @Override 354 protected void recordSample(StatsContext statsContext) { 355 // Recorder does not need to be locked in this context since only the handler 356 // thread will update it. We pass a null VPN array because usage is aggregated by uid 357 // for this snapshot, so VPN traffic can't be reattributed to responsible apps. 358 mRecorder.recordSnapshotLocked(statsContext.mXtSnapshot, statsContext.mActiveIfaces, 359 null /* vpnArray */, statsContext.mCurrentTime); 360 } 361 362 /** 363 * Reads stats matching the given template. {@link NetworkStatsCollection} will aggregate 364 * over all buckets, which in this case should be only one since we built it big enough 365 * that it will outlive the caller. If it doesn't, then there will be multiple buckets. 366 */ 367 private long getTotalBytesForNetwork(NetworkTemplate template) { 368 NetworkStats stats = mCollection.getSummary(template, 369 Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */, 370 mAccessLevel, mCallingUid); 371 return stats.getTotalBytes(); 372 } 373 } 374 375 private static class UserUsageRequestInfo extends RequestInfo { 376 UserUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request, 377 Messenger messenger, IBinder binder, int callingUid, 378 @NetworkStatsAccess.Level int accessLevel) { 379 super(statsObserver, request, messenger, binder, callingUid, accessLevel); 380 } 381 382 @Override 383 protected boolean checkStats() { 384 int[] uidsToMonitor = mCollection.getRelevantUids(mAccessLevel, mCallingUid); 385 386 for (int i = 0; i < uidsToMonitor.length; i++) { 387 long bytesSoFar = getTotalBytesForNetworkUid(mRequest.template, uidsToMonitor[i]); 388 if (bytesSoFar > mRequest.thresholdInBytes) { 389 return true; 390 } 391 } 392 return false; 393 } 394 395 @Override 396 protected void recordSample(StatsContext statsContext) { 397 // Recorder does not need to be locked in this context since only the handler 398 // thread will update it. We pass the VPN info so VPN traffic is reattributed to 399 // responsible apps. 400 mRecorder.recordSnapshotLocked(statsContext.mUidSnapshot, statsContext.mActiveUidIfaces, 401 statsContext.mVpnArray, statsContext.mCurrentTime); 402 } 403 404 /** 405 * Reads all stats matching the given template and uid. Ther history will likely only 406 * contain one bucket per ident since we build it big enough that it will outlive the 407 * caller lifetime. 408 */ 409 private long getTotalBytesForNetworkUid(NetworkTemplate template, int uid) { 410 try { 411 NetworkStatsHistory history = mCollection.getHistory(template, null, uid, 412 NetworkStats.SET_ALL, NetworkStats.TAG_NONE, 413 NetworkStatsHistory.FIELD_ALL, 414 Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */, 415 mAccessLevel, mCallingUid); 416 return history.getTotalBytes(); 417 } catch (SecurityException e) { 418 if (LOGV) { 419 Slog.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid " 420 + uid); 421 } 422 return 0; 423 } 424 } 425 } 426 427 private static class StatsContext { 428 NetworkStats mXtSnapshot; 429 NetworkStats mUidSnapshot; 430 ArrayMap<String, NetworkIdentitySet> mActiveIfaces; 431 ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces; 432 VpnInfo[] mVpnArray; 433 long mCurrentTime; 434 435 StatsContext(NetworkStats xtSnapshot, NetworkStats uidSnapshot, 436 ArrayMap<String, NetworkIdentitySet> activeIfaces, 437 ArrayMap<String, NetworkIdentitySet> activeUidIfaces, 438 VpnInfo[] vpnArray, long currentTime) { 439 mXtSnapshot = xtSnapshot; 440 mUidSnapshot = uidSnapshot; 441 mActiveIfaces = activeIfaces; 442 mActiveUidIfaces = activeUidIfaces; 443 mVpnArray = vpnArray; 444 mCurrentTime = currentTime; 445 } 446 } 447 } 448