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.net.ConnectivityManager; 24 import android.net.Network; 25 import android.net.NetworkCapabilities; 26 import android.net.NetworkInfo; 27 import android.net.NetworkRequest; 28 import android.os.ParcelFileDescriptor; 29 import android.os.Process; 30 import android.os.RemoteException; 31 import android.telephony.TelephonyManager; 32 import android.test.InstrumentationTestCase; 33 import android.util.Log; 34 35 import java.io.FileInputStream; 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.io.InputStreamReader; 39 import java.net.URL; 40 import java.text.MessageFormat; 41 import java.util.Scanner; 42 import javax.net.ssl.HttpsURLConnection; 43 44 import libcore.io.IoUtils; 45 import libcore.io.Streams; 46 47 public class NetworkUsageStatsTest extends InstrumentationTestCase { 48 private static final String LOG_TAG = "NetworkUsageStatsTest"; 49 private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} {1} {2}"; 50 private static final String APPOPS_GET_SHELL_COMMAND = "appops get {0} {1}"; 51 52 private static final long MINUTE = 1000 * 60; 53 54 private static final int[] sNetworkTypesToTest = new int[] { 55 ConnectivityManager.TYPE_WIFI, 56 ConnectivityManager.TYPE_MOBILE, 57 }; 58 59 // Order corresponds to sNetworkTypesToTest 60 private static final int[] sTransportTypesToTest = new int[] { 61 NetworkCapabilities.TRANSPORT_WIFI, 62 NetworkCapabilities.TRANSPORT_CELLULAR, 63 }; 64 65 private NetworkStatsManager mNsm; 66 private ConnectivityManager mCm; 67 private long mStartTime; 68 private long mEndTime; 69 70 private long mBytesRead; 71 private String mWriteSettingsMode; 72 private String mUsageStatsMode; 73 exerciseRemoteHost(int transportType)74 private void exerciseRemoteHost(int transportType) throws Exception { 75 final int timeout = 15000; 76 mCm.requestNetwork(new NetworkRequest.Builder() 77 .addTransportType(transportType) 78 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 79 .build(), new ConnectivityManager.NetworkCallback() { 80 @Override 81 public void onAvailable(Network network) { 82 NetworkInfo networkInfo = mCm.getNetworkInfo(network); 83 if (networkInfo == null) { 84 Log.w(LOG_TAG, "Network info is null"); 85 } else { 86 Log.w(LOG_TAG, "Network: " + networkInfo.toString()); 87 } 88 InputStreamReader in = null; 89 HttpsURLConnection urlc = null; 90 String originalKeepAlive = System.getProperty("http.keepAlive"); 91 System.setProperty("http.keepAlive", "false"); 92 try { 93 urlc = (HttpsURLConnection) network.openConnection(new URL( 94 "https://www.google.com")); 95 urlc.setConnectTimeout(timeout); 96 urlc.setUseCaches(false); 97 urlc.connect(); 98 boolean ping = urlc.getResponseCode() == 200; 99 if (ping) { 100 in = new InputStreamReader( 101 (InputStream) urlc.getContent()); 102 103 mBytesRead = 0; 104 while (in.read() != -1) ++mBytesRead; 105 } 106 } catch (Exception e) { 107 Log.i(LOG_TAG, "Badness during exercising remote server: " + e); 108 } finally { 109 if (in != null) { 110 try { 111 in.close(); 112 } catch (IOException e) { 113 // don't care 114 } 115 } 116 if (urlc != null) { 117 urlc.disconnect(); 118 } 119 if (originalKeepAlive == null) { 120 System.clearProperty("http.keepAlive"); 121 } else { 122 System.setProperty("http.keepAlive", originalKeepAlive); 123 } 124 } 125 } 126 }); 127 try { 128 Thread.sleep(timeout); 129 } catch (InterruptedException e) { 130 } 131 } 132 133 @Override setUp()134 protected void setUp() throws Exception { 135 super.setUp(); 136 mNsm = (NetworkStatsManager) getInstrumentation().getContext() 137 .getSystemService(Context.NETWORK_STATS_SERVICE); 138 139 mCm = (ConnectivityManager) getInstrumentation().getContext() 140 .getSystemService(Context.CONNECTIVITY_SERVICE); 141 142 mWriteSettingsMode = getAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS); 143 setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, "allow"); 144 mUsageStatsMode = getAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS); 145 } 146 147 @Override tearDown()148 protected void tearDown() throws Exception { 149 if (mWriteSettingsMode != null) { 150 setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, mWriteSettingsMode); 151 } 152 if (mUsageStatsMode != null) { 153 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, mUsageStatsMode); 154 } 155 super.tearDown(); 156 } 157 setAppOpsMode(String appop, String mode)158 private void setAppOpsMode(String appop, String mode) throws Exception { 159 final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND, 160 getInstrumentation().getContext().getPackageName(), appop, mode); 161 ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() 162 .executeShellCommand(command); 163 try { 164 Streams.readFully(new FileInputStream(pfd.getFileDescriptor())); 165 } finally { 166 IoUtils.closeQuietly(pfd.getFileDescriptor()); 167 } 168 } 169 getAppOpsMode(String appop)170 private String getAppOpsMode(String appop) throws Exception { 171 String result; 172 final String command = MessageFormat.format(APPOPS_GET_SHELL_COMMAND, 173 getInstrumentation().getContext().getPackageName(), appop); 174 ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() 175 .executeShellCommand(command); 176 try { 177 result = convertStreamToString(new FileInputStream(pfd.getFileDescriptor())); 178 } finally { 179 IoUtils.closeQuietly(pfd.getFileDescriptor()); 180 } 181 if (result == null) { 182 Log.w(LOG_TAG, "App op " + appop + " could not be read."); 183 } 184 return result; 185 } 186 convertStreamToString(InputStream is)187 private static String convertStreamToString(InputStream is) { 188 try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) { 189 return scanner.hasNext() ? scanner.next() : null; 190 } 191 } 192 shouldTestThisNetworkType(int networkTypeIndex, long tolerance)193 private boolean shouldTestThisNetworkType(int networkTypeIndex, long tolerance) 194 throws Exception { 195 NetworkInfo networkInfo = mCm.getNetworkInfo(sNetworkTypesToTest[networkTypeIndex]); 196 if (networkInfo == null || !networkInfo.isAvailable()) { 197 return false; 198 } 199 mStartTime = System.currentTimeMillis() - tolerance; 200 exerciseRemoteHost(sTransportTypesToTest[networkTypeIndex]); 201 mEndTime = System.currentTimeMillis() + tolerance; 202 return true; 203 } 204 getSubscriberId(int networkType)205 private String getSubscriberId(int networkType) { 206 if (ConnectivityManager.TYPE_MOBILE == networkType) { 207 TelephonyManager tm = (TelephonyManager) getInstrumentation().getContext() 208 .getSystemService(Context.TELEPHONY_SERVICE); 209 return tm.getSubscriberId(); 210 } 211 return ""; 212 } 213 testDeviceSummary()214 public void testDeviceSummary() throws Exception { 215 for (int i = 0; i < sNetworkTypesToTest.length; ++i) { 216 if (!shouldTestThisNetworkType(i, MINUTE/2)) { 217 continue; 218 } 219 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 220 NetworkStats.Bucket bucket = null; 221 try { 222 bucket = mNsm.querySummaryForDevice( 223 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 224 mStartTime, mEndTime); 225 } catch (RemoteException | SecurityException e) { 226 fail("testDeviceSummary fails with exception: " + e.toString()); 227 } 228 assertNotNull(bucket); 229 assertTimestamps(bucket); 230 assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL); 231 assertEquals(bucket.getUid(), NetworkStats.Bucket.UID_ALL); 232 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 233 try { 234 bucket = mNsm.querySummaryForDevice( 235 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 236 mStartTime, mEndTime); 237 fail("negative testDeviceSummary fails: no exception thrown."); 238 } catch (RemoteException e) { 239 fail("testDeviceSummary fails with exception: " + e.toString()); 240 } catch (SecurityException e) { 241 // expected outcome 242 } 243 } 244 } 245 testUserSummary()246 public void testUserSummary() throws Exception { 247 for (int i = 0; i < sNetworkTypesToTest.length; ++i) { 248 if (!shouldTestThisNetworkType(i, MINUTE/2)) { 249 continue; 250 } 251 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 252 NetworkStats.Bucket bucket = null; 253 try { 254 bucket = mNsm.querySummaryForUser( 255 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 256 mStartTime, mEndTime); 257 } catch (RemoteException | SecurityException e) { 258 fail("testUserSummary fails with exception: " + e.toString()); 259 } 260 assertNotNull(bucket); 261 assertTimestamps(bucket); 262 assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL); 263 assertEquals(bucket.getUid(), NetworkStats.Bucket.UID_ALL); 264 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 265 try { 266 bucket = mNsm.querySummaryForUser( 267 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 268 mStartTime, mEndTime); 269 fail("negative testUserSummary fails: no exception thrown."); 270 } catch (RemoteException e) { 271 fail("testUserSummary fails with exception: " + e.toString()); 272 } catch (SecurityException e) { 273 // expected outcome 274 } 275 } 276 } 277 testAppSummary()278 public void testAppSummary() throws Exception { 279 for (int i = 0; i < sNetworkTypesToTest.length; ++i) { 280 if (!shouldTestThisNetworkType(i, MINUTE/2)) { 281 continue; 282 } 283 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 284 NetworkStats result = null; 285 try { 286 result = mNsm.querySummary( 287 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 288 mStartTime, mEndTime); 289 assertTrue(result != null); 290 NetworkStats.Bucket bucket = new NetworkStats.Bucket(); 291 long totalTxPackets = 0; 292 long totalRxPackets = 0; 293 long totalTxBytes = 0; 294 long totalRxBytes = 0; 295 while (result.hasNextBucket()) { 296 assertTrue(result.getNextBucket(bucket)); 297 assertTimestamps(bucket); 298 if (bucket.getUid() == Process.myUid()) { 299 totalTxPackets += bucket.getTxPackets(); 300 totalRxPackets += bucket.getRxPackets(); 301 totalTxBytes += bucket.getTxBytes(); 302 totalRxBytes += bucket.getRxBytes(); 303 } 304 } 305 assertFalse(result.getNextBucket(bucket)); 306 assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0); 307 assertTrue("No Rx packets usage for uid " + Process.myUid(), totalRxPackets > 0); 308 assertTrue("No Tx bytes usage for uid " + Process.myUid(), totalTxBytes > 0); 309 assertTrue("No Tx packets usage for uid " + Process.myUid(), totalTxPackets > 0); 310 } catch (RemoteException | SecurityException e) { 311 fail("testAppSummary fails with exception: " + e.toString()); 312 } finally { 313 if (result != null) { 314 result.close(); 315 } 316 } 317 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 318 try { 319 result = mNsm.querySummary( 320 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 321 mStartTime, mEndTime); 322 fail("negative testAppSummary fails: no exception thrown."); 323 } catch (RemoteException e) { 324 fail("testAppSummary fails with exception: " + e.toString()); 325 } catch (SecurityException e) { 326 // expected outcome 327 } 328 } 329 } 330 testAppDetails()331 public void testAppDetails() throws Exception { 332 for (int i = 0; i < sNetworkTypesToTest.length; ++i) { 333 // Relatively large tolerance to accommodate for history bucket size. 334 if (!shouldTestThisNetworkType(i, MINUTE * 120)) { 335 continue; 336 } 337 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 338 NetworkStats result = null; 339 try { 340 result = mNsm.queryDetails( 341 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 342 mStartTime, mEndTime); 343 assertTrue(result != null); 344 NetworkStats.Bucket bucket = new NetworkStats.Bucket(); 345 long totalTxPackets = 0; 346 long totalRxPackets = 0; 347 long totalTxBytes = 0; 348 long totalRxBytes = 0; 349 while (result.hasNextBucket()) { 350 assertTrue(result.getNextBucket(bucket)); 351 assertTimestamps(bucket); 352 assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL); 353 if (bucket.getUid() == Process.myUid()) { 354 totalTxPackets += bucket.getTxPackets(); 355 totalRxPackets += bucket.getRxPackets(); 356 totalTxBytes += bucket.getTxBytes(); 357 totalRxBytes += bucket.getRxBytes(); 358 } 359 } 360 assertFalse(result.getNextBucket(bucket)); 361 assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0); 362 assertTrue("No Rx packets usage for uid " + Process.myUid(), totalRxPackets > 0); 363 assertTrue("No Tx bytes usage for uid " + Process.myUid(), totalTxBytes > 0); 364 assertTrue("No Tx packets usage for uid " + Process.myUid(), totalTxPackets > 0); 365 } catch (RemoteException | SecurityException e) { 366 fail("testAppDetails fails with exception: " + e.toString()); 367 } finally { 368 if (result != null) { 369 result.close(); 370 } 371 } 372 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 373 try { 374 result = mNsm.queryDetails( 375 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 376 mStartTime, mEndTime); 377 fail("negative testAppDetails fails: no exception thrown."); 378 } catch (RemoteException e) { 379 fail("testAppDetails fails with exception: " + e.toString()); 380 } catch (SecurityException e) { 381 // expected outcome 382 } 383 } 384 } 385 testUidDetails()386 public void testUidDetails() throws Exception { 387 for (int i = 0; i < sNetworkTypesToTest.length; ++i) { 388 // Relatively large tolerance to accommodate for history bucket size. 389 if (!shouldTestThisNetworkType(i, MINUTE * 120)) { 390 continue; 391 } 392 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow"); 393 NetworkStats result = null; 394 try { 395 result = mNsm.queryDetailsForUid( 396 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 397 mStartTime, mEndTime, Process.myUid()); 398 assertTrue(result != null); 399 NetworkStats.Bucket bucket = new NetworkStats.Bucket(); 400 long totalTxPackets = 0; 401 long totalRxPackets = 0; 402 long totalTxBytes = 0; 403 long totalRxBytes = 0; 404 while (result.hasNextBucket()) { 405 assertTrue(result.getNextBucket(bucket)); 406 assertTimestamps(bucket); 407 assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL); 408 assertEquals(bucket.getUid(), Process.myUid()); 409 totalTxPackets += bucket.getTxPackets(); 410 totalRxPackets += bucket.getRxPackets(); 411 totalTxBytes += bucket.getTxBytes(); 412 totalRxBytes += bucket.getRxBytes(); 413 } 414 assertFalse(result.getNextBucket(bucket)); 415 assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0); 416 assertTrue("No Rx packets usage for uid " + Process.myUid(), totalRxPackets > 0); 417 assertTrue("No Tx bytes usage for uid " + Process.myUid(), totalTxBytes > 0); 418 assertTrue("No Tx packets usage for uid " + Process.myUid(), totalTxPackets > 0); 419 } catch (RemoteException | SecurityException e) { 420 fail("testUidDetails fails with exception: " + e.toString()); 421 } finally { 422 if (result != null) { 423 result.close(); 424 } 425 } 426 setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny"); 427 try { 428 result = mNsm.queryDetailsForUid( 429 sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]), 430 mStartTime, mEndTime, Process.myUid()); 431 fail("negative testUidDetails fails: no exception thrown."); 432 } catch (RemoteException e) { 433 fail("testUidDetails fails with exception: " + e.toString()); 434 } catch (SecurityException e) { 435 // expected outcome 436 } 437 } 438 } 439 assertTimestamps(final NetworkStats.Bucket bucket)440 private void assertTimestamps(final NetworkStats.Bucket bucket) { 441 assertTrue("Start timestamp " + bucket.getStartTimeStamp() + " is less than " + 442 mStartTime, bucket.getStartTimeStamp() >= mStartTime); 443 assertTrue("End timestamp " + bucket.getEndTimeStamp() + " is greater than " + 444 mEndTime, bucket.getEndTimeStamp() <= mEndTime); 445 } 446 } 447