1 /** 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package android.app.usage.cts; 18 19 import android.app.AppOpsManager; 20 import android.app.usage.NetworkStatsManager; 21 import android.app.usage.NetworkStats; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.net.ConnectivityManager; 25 import android.net.Network; 26 import android.net.NetworkCapabilities; 27 import android.net.NetworkInfo; 28 import android.net.NetworkRequest; 29 import android.os.ParcelFileDescriptor; 30 import android.os.Process; 31 import android.os.RemoteException; 32 import android.telephony.TelephonyManager; 33 import android.test.InstrumentationTestCase; 34 import android.util.Log; 35 36 import java.io.FileInputStream; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.io.InputStreamReader; 40 import java.net.URL; 41 import java.text.MessageFormat; 42 import java.util.Scanner; 43 import java.net.HttpURLConnection; 44 45 import libcore.io.IoUtils; 46 import libcore.io.Streams; 47 48 public class NetworkUsageStatsTest extends InstrumentationTestCase { 49 private static final String LOG_TAG = "NetworkUsageStatsTest"; 50 private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} {1} {2}"; 51 private static final String APPOPS_GET_SHELL_COMMAND = "appops get {0} {1}"; 52 53 private static final long MINUTE = 1000 * 60; 54 private static final int TIMEOUT_MILLIS = 15000; 55 56 private interface NetworkInterfaceToTest { getNetworkType()57 int getNetworkType(); getTransportType()58 int getTransportType(); getSystemFeature()59 String getSystemFeature(); getErrorMessage()60 String getErrorMessage(); 61 } 62 63 private static final NetworkInterfaceToTest[] sNetworkInterfacesToTest = 64 new NetworkInterfaceToTest[] { 65 new NetworkInterfaceToTest() { 66 @Override 67 public int getNetworkType() { 68 return ConnectivityManager.TYPE_WIFI; 69 } 70 71 @Override 72 public int getTransportType() { 73 return NetworkCapabilities.TRANSPORT_WIFI; 74 } 75 76 @Override 77 public String getSystemFeature() { 78 return PackageManager.FEATURE_WIFI; 79 } 80 81 @Override 82 public String getErrorMessage() { 83 return " Please make sure you are connected to a WiFi access point."; 84 } 85 }, 86 new NetworkInterfaceToTest() { 87 @Override 88 public int getNetworkType() { 89 return ConnectivityManager.TYPE_MOBILE; 90 } 91 92 @Override 93 public int getTransportType() { 94 return NetworkCapabilities.TRANSPORT_CELLULAR; 95 } 96 97 @Override 98 public String getSystemFeature() { 99 return PackageManager.FEATURE_TELEPHONY; 100 } 101 102 @Override 103 public String getErrorMessage() { 104 return " Please make sure you have added a SIM card with data plan to" + 105 " your phone, have enabled data over cellular and in case of" + 106 " dual SIM devices, have selected the right SIM " + 107 "for data connection."; 108 } 109 } 110 }; 111 112 private NetworkStatsManager mNsm; 113 private ConnectivityManager mCm; 114 private PackageManager mPm; 115 private long mStartTime; 116 private long mEndTime; 117 118 private long mBytesRead; 119 private String mWriteSettingsMode; 120 private String mUsageStatsMode; 121 exerciseRemoteHost(Network network)122 private void exerciseRemoteHost(Network network) throws Exception { 123 NetworkInfo networkInfo = mCm.getNetworkInfo(network); 124 if (networkInfo == null) { 125 Log.w(LOG_TAG, "Network info is null"); 126 } else { 127 Log.w(LOG_TAG, "Network: " + networkInfo.toString()); 128 } 129 InputStreamReader in = null; 130 HttpURLConnection urlc = null; 131 String originalKeepAlive = System.getProperty("http.keepAlive"); 132 System.setProperty("http.keepAlive", "false"); 133 try { 134 urlc = (HttpURLConnection) network.openConnection(new URL( 135 "http://www.265.com/")); 136 urlc.setConnectTimeout(TIMEOUT_MILLIS); 137 urlc.setUseCaches(false); 138 urlc.connect(); 139 boolean ping = urlc.getResponseCode() == 200; 140 if (ping) { 141 in = new InputStreamReader( 142 (InputStream) urlc.getContent()); 143 144 mBytesRead = 0; 145 while (in.read() != -1) ++mBytesRead; 146 } 147 } catch (Exception e) { 148 Log.i(LOG_TAG, "Badness during exercising remote server: " + e); 149 } finally { 150 if (in != null) { 151 try { 152 in.close(); 153 } catch (IOException e) { 154 // don't care 155 } 156 } 157 if (urlc != null) { 158 urlc.disconnect(); 159 } 160 if (originalKeepAlive == null) { 161 System.clearProperty("http.keepAlive"); 162 } else { 163 System.setProperty("http.keepAlive", originalKeepAlive); 164 } 165 } 166 } 167 168 @Override setUp()169 protected void setUp() throws Exception { 170 super.setUp(); 171 mNsm = (NetworkStatsManager) getInstrumentation().getContext() 172 .getSystemService(Context.NETWORK_STATS_SERVICE); 173 174 mCm = (ConnectivityManager) getInstrumentation().getContext() 175 .getSystemService(Context.CONNECTIVITY_SERVICE); 176 177 mPm = getInstrumentation().getContext().getPackageManager(); 178 179 mWriteSettingsMode = getAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS); 180 setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, "allow"); 181 mUsageStatsMode = getAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS); 182 } 183 184 @Override tearDown()185 protected void tearDown() throws Exception { 186 if (mWriteSettingsMode != null) { 187 setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, mWriteSettingsMode); 188 } 189 if (mUsageStatsMode != null) { 190 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, mUsageStatsMode); 191 } 192 super.tearDown(); 193 } 194 setAppOpsMode(String appop, String mode)195 private void setAppOpsMode(String appop, String mode) throws Exception { 196 final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND, 197 getInstrumentation().getContext().getPackageName(), appop, mode); 198 ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() 199 .executeShellCommand(command); 200 try { 201 Streams.readFully(new FileInputStream(pfd.getFileDescriptor())); 202 } finally { 203 IoUtils.closeQuietly(pfd.getFileDescriptor()); 204 } 205 } 206 getAppOpsMode(String appop)207 private String getAppOpsMode(String appop) throws Exception { 208 String result; 209 final String command = MessageFormat.format(APPOPS_GET_SHELL_COMMAND, 210 getInstrumentation().getContext().getPackageName(), appop); 211 ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() 212 .executeShellCommand(command); 213 try { 214 result = convertStreamToString(new FileInputStream(pfd.getFileDescriptor())); 215 } finally { 216 IoUtils.closeQuietly(pfd.getFileDescriptor()); 217 } 218 if (result == null) { 219 Log.w(LOG_TAG, "App op " + appop + " could not be read."); 220 } 221 return result; 222 } 223 convertStreamToString(InputStream is)224 private static String convertStreamToString(InputStream is) { 225 try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) { 226 return scanner.hasNext() ? scanner.next() : null; 227 } 228 } 229 230 private class NetworkCallback extends ConnectivityManager.NetworkCallback { 231 private long mTolerance; 232 public boolean success; 233 NetworkCallback(long tolerance)234 NetworkCallback(long tolerance) { 235 mTolerance = tolerance; 236 success = false; 237 } 238 239 @Override onAvailable(Network network)240 public void onAvailable(Network network) { 241 try { 242 mStartTime = System.currentTimeMillis() - mTolerance; 243 exerciseRemoteHost(network); 244 mEndTime = System.currentTimeMillis() + mTolerance; 245 success = true; 246 synchronized(NetworkUsageStatsTest.this) { 247 NetworkUsageStatsTest.this.notify(); 248 } 249 } catch (Exception e) { 250 Log.w(LOG_TAG, "exercising remote host failed.", e); 251 success = false; 252 } 253 } 254 } 255 shouldTestThisNetworkType(int networkTypeIndex, final long tolerance)256 private boolean shouldTestThisNetworkType(int networkTypeIndex, final long tolerance) 257 throws Exception { 258 boolean hasFeature = mPm.hasSystemFeature( 259 sNetworkInterfacesToTest[networkTypeIndex].getSystemFeature()); 260 if (!hasFeature) { 261 return false; 262 } 263 NetworkCallback callback = new NetworkCallback(tolerance); 264 mCm.requestNetwork(new NetworkRequest.Builder() 265 .addTransportType(sNetworkInterfacesToTest[networkTypeIndex].getTransportType()) 266 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 267 .build(), callback); 268 synchronized(this) { 269 try { 270 wait((int)(TIMEOUT_MILLIS * 1.2)); 271 } catch (InterruptedException e) { 272 } 273 } 274 if (callback.success) { 275 return true; 276 } 277 278 // This will always fail at this point as we know 'hasFeature' is true. 279 assertFalse (sNetworkInterfacesToTest[networkTypeIndex].getSystemFeature() + 280 " is a reported system feature, " + 281 "however no corresponding connected network interface was found or the attempt " + 282 "to connect has timed out (timeout = " + TIMEOUT_MILLIS + "ms)." + 283 sNetworkInterfacesToTest[networkTypeIndex].getErrorMessage(), hasFeature); 284 return false; 285 } 286 getSubscriberId(int networkIndex)287 private String getSubscriberId(int networkIndex) { 288 int networkType = sNetworkInterfacesToTest[networkIndex].getNetworkType(); 289 if (ConnectivityManager.TYPE_MOBILE == networkType) { 290 TelephonyManager tm = (TelephonyManager) getInstrumentation().getContext() 291 .getSystemService(Context.TELEPHONY_SERVICE); 292 return tm.getSubscriberId(); 293 } 294 return ""; 295 } 296 testDeviceSummary()297 public void testDeviceSummary() throws Exception { 298 for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) { 299 if (!shouldTestThisNetworkType(i, MINUTE/2)) { 300 continue; 301 } 302 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 303 NetworkStats.Bucket bucket = null; 304 try { 305 bucket = mNsm.querySummaryForDevice( 306 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 307 mStartTime, mEndTime); 308 } catch (RemoteException | SecurityException e) { 309 fail("testDeviceSummary fails with exception: " + e.toString()); 310 } 311 assertNotNull(bucket); 312 assertTimestamps(bucket); 313 assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL); 314 assertEquals(bucket.getUid(), NetworkStats.Bucket.UID_ALL); 315 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 316 try { 317 bucket = mNsm.querySummaryForDevice( 318 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 319 mStartTime, mEndTime); 320 fail("negative testDeviceSummary fails: no exception thrown."); 321 } catch (RemoteException e) { 322 fail("testDeviceSummary fails with exception: " + e.toString()); 323 } catch (SecurityException e) { 324 // expected outcome 325 } 326 } 327 } 328 testUserSummary()329 public void testUserSummary() throws Exception { 330 for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) { 331 if (!shouldTestThisNetworkType(i, MINUTE/2)) { 332 continue; 333 } 334 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 335 NetworkStats.Bucket bucket = null; 336 try { 337 bucket = mNsm.querySummaryForUser( 338 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 339 mStartTime, mEndTime); 340 } catch (RemoteException | SecurityException e) { 341 fail("testUserSummary fails with exception: " + e.toString()); 342 } 343 assertNotNull(bucket); 344 assertTimestamps(bucket); 345 assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL); 346 assertEquals(bucket.getUid(), NetworkStats.Bucket.UID_ALL); 347 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 348 try { 349 bucket = mNsm.querySummaryForUser( 350 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 351 mStartTime, mEndTime); 352 fail("negative testUserSummary fails: no exception thrown."); 353 } catch (RemoteException e) { 354 fail("testUserSummary fails with exception: " + e.toString()); 355 } catch (SecurityException e) { 356 // expected outcome 357 } 358 } 359 } 360 testAppSummary()361 public void testAppSummary() throws Exception { 362 for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) { 363 if (!shouldTestThisNetworkType(i, MINUTE/2)) { 364 continue; 365 } 366 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 367 NetworkStats result = null; 368 try { 369 result = mNsm.querySummary( 370 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 371 mStartTime, mEndTime); 372 assertTrue(result != null); 373 NetworkStats.Bucket bucket = new NetworkStats.Bucket(); 374 long totalTxPackets = 0; 375 long totalRxPackets = 0; 376 long totalTxBytes = 0; 377 long totalRxBytes = 0; 378 while (result.hasNextBucket()) { 379 assertTrue(result.getNextBucket(bucket)); 380 assertTimestamps(bucket); 381 if (bucket.getUid() == Process.myUid()) { 382 totalTxPackets += bucket.getTxPackets(); 383 totalRxPackets += bucket.getRxPackets(); 384 totalTxBytes += bucket.getTxBytes(); 385 totalRxBytes += bucket.getRxBytes(); 386 } 387 } 388 assertFalse(result.getNextBucket(bucket)); 389 assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0); 390 assertTrue("No Rx packets usage for uid " + Process.myUid(), totalRxPackets > 0); 391 assertTrue("No Tx bytes usage for uid " + Process.myUid(), totalTxBytes > 0); 392 assertTrue("No Tx packets usage for uid " + Process.myUid(), totalTxPackets > 0); 393 } catch (RemoteException | SecurityException e) { 394 fail("testAppSummary fails with exception: " + e.toString()); 395 } finally { 396 if (result != null) { 397 result.close(); 398 } 399 } 400 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 401 try { 402 result = mNsm.querySummary( 403 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 404 mStartTime, mEndTime); 405 fail("negative testAppSummary fails: no exception thrown."); 406 } catch (RemoteException e) { 407 fail("testAppSummary fails with exception: " + e.toString()); 408 } catch (SecurityException e) { 409 // expected outcome 410 } 411 } 412 } 413 testAppDetails()414 public void testAppDetails() throws Exception { 415 for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) { 416 // Relatively large tolerance to accommodate for history bucket size. 417 if (!shouldTestThisNetworkType(i, MINUTE * 120)) { 418 continue; 419 } 420 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 421 NetworkStats result = null; 422 try { 423 result = mNsm.queryDetails( 424 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 425 mStartTime, mEndTime); 426 assertTrue(result != null); 427 NetworkStats.Bucket bucket = new NetworkStats.Bucket(); 428 long totalTxPackets = 0; 429 long totalRxPackets = 0; 430 long totalTxBytes = 0; 431 long totalRxBytes = 0; 432 while (result.hasNextBucket()) { 433 assertTrue(result.getNextBucket(bucket)); 434 assertTimestamps(bucket); 435 assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL); 436 if (bucket.getUid() == Process.myUid()) { 437 totalTxPackets += bucket.getTxPackets(); 438 totalRxPackets += bucket.getRxPackets(); 439 totalTxBytes += bucket.getTxBytes(); 440 totalRxBytes += bucket.getRxBytes(); 441 } 442 } 443 assertFalse(result.getNextBucket(bucket)); 444 assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0); 445 assertTrue("No Rx packets usage for uid " + Process.myUid(), totalRxPackets > 0); 446 assertTrue("No Tx bytes usage for uid " + Process.myUid(), totalTxBytes > 0); 447 assertTrue("No Tx packets usage for uid " + Process.myUid(), totalTxPackets > 0); 448 } catch (RemoteException | SecurityException e) { 449 fail("testAppDetails fails with exception: " + e.toString()); 450 } finally { 451 if (result != null) { 452 result.close(); 453 } 454 } 455 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 456 try { 457 result = mNsm.queryDetails( 458 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 459 mStartTime, mEndTime); 460 fail("negative testAppDetails fails: no exception thrown."); 461 } catch (RemoteException e) { 462 fail("testAppDetails fails with exception: " + e.toString()); 463 } catch (SecurityException e) { 464 // expected outcome 465 } 466 } 467 } 468 testUidDetails()469 public void testUidDetails() throws Exception { 470 for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) { 471 // Relatively large tolerance to accommodate for history bucket size. 472 if (!shouldTestThisNetworkType(i, MINUTE * 120)) { 473 continue; 474 } 475 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 476 NetworkStats result = null; 477 try { 478 result = mNsm.queryDetailsForUid( 479 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 480 mStartTime, mEndTime, Process.myUid()); 481 assertTrue(result != null); 482 NetworkStats.Bucket bucket = new NetworkStats.Bucket(); 483 long totalTxPackets = 0; 484 long totalRxPackets = 0; 485 long totalTxBytes = 0; 486 long totalRxBytes = 0; 487 while (result.hasNextBucket()) { 488 assertTrue(result.getNextBucket(bucket)); 489 assertTimestamps(bucket); 490 assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL); 491 assertEquals(bucket.getUid(), Process.myUid()); 492 totalTxPackets += bucket.getTxPackets(); 493 totalRxPackets += bucket.getRxPackets(); 494 totalTxBytes += bucket.getTxBytes(); 495 totalRxBytes += bucket.getRxBytes(); 496 } 497 assertFalse(result.getNextBucket(bucket)); 498 assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0); 499 assertTrue("No Rx packets usage for uid " + Process.myUid(), totalRxPackets > 0); 500 assertTrue("No Tx bytes usage for uid " + Process.myUid(), totalTxBytes > 0); 501 assertTrue("No Tx packets usage for uid " + Process.myUid(), totalTxPackets > 0); 502 } catch (RemoteException | SecurityException e) { 503 fail("testUidDetails fails with exception: " + e.toString()); 504 } finally { 505 if (result != null) { 506 result.close(); 507 } 508 } 509 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 510 try { 511 result = mNsm.queryDetailsForUid( 512 sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i), 513 mStartTime, mEndTime, Process.myUid()); 514 fail("negative testUidDetails fails: no exception thrown."); 515 } catch (RemoteException e) { 516 fail("testUidDetails fails with exception: " + e.toString()); 517 } catch (SecurityException e) { 518 // expected outcome 519 } 520 } 521 } 522 assertTimestamps(final NetworkStats.Bucket bucket)523 private void assertTimestamps(final NetworkStats.Bucket bucket) { 524 assertTrue("Start timestamp " + bucket.getStartTimeStamp() + " is less than " + 525 mStartTime, bucket.getStartTimeStamp() >= mStartTime); 526 assertTrue("End timestamp " + bucket.getEndTimeStamp() + " is greater than " + 527 mEndTime, bucket.getEndTimeStamp() <= mEndTime); 528 } 529 } 530