1 /* 2 * Copyright (C) 2011, 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.bandwidthtest; 18 19 import android.content.Context; 20 import android.net.ConnectivityManager; 21 import android.net.NetworkInfo.State; 22 import android.net.NetworkStats; 23 import android.net.NetworkStats.Entry; 24 import android.net.TrafficStats; 25 import android.net.wifi.WifiManager; 26 import android.os.Bundle; 27 import android.os.Environment; 28 import android.os.Process; 29 import android.os.SystemClock; 30 import android.telephony.TelephonyManager; 31 import android.test.InstrumentationTestCase; 32 import android.test.suitebuilder.annotation.LargeTest; 33 import android.util.Log; 34 35 import com.android.bandwidthtest.util.BandwidthTestUtil; 36 import com.android.bandwidthtest.util.ConnectionUtil; 37 38 import java.io.File; 39 40 /** 41 * Test that downloads files from a test server and reports the bandwidth metrics collected. 42 */ 43 public class BandwidthTest extends InstrumentationTestCase { 44 45 private static final String LOG_TAG = "BandwidthTest"; 46 private final static String PROF_LABEL = "PROF_"; 47 private final static String PROC_LABEL = "PROC_"; 48 private final static int INSTRUMENTATION_IN_PROGRESS = 2; 49 50 private final static String BASE_DIR = 51 Environment.getExternalStorageDirectory().getAbsolutePath(); 52 private final static String TMP_FILENAME = "tmp.dat"; 53 // Download 10.486 * 106 bytes (+ headers) from app engine test server. 54 private final int FILE_SIZE = 10485613; 55 private Context mContext; 56 private ConnectionUtil mConnectionUtil; 57 private TelephonyManager mTManager; 58 private int mUid; 59 private String mSsid; 60 private String mTestServer; 61 private String mDeviceId; 62 private BandwidthTestRunner mRunner; 63 64 65 @Override setUp()66 protected void setUp() throws Exception { 67 super.setUp(); 68 mRunner = (BandwidthTestRunner) getInstrumentation(); 69 mSsid = mRunner.mSsid; 70 mTestServer = mRunner.mTestServer; 71 mContext = mRunner.getTargetContext(); 72 mConnectionUtil = new ConnectionUtil(mContext); 73 mConnectionUtil.initialize(); 74 Log.v(LOG_TAG, "Initialized mConnectionUtil"); 75 mUid = Process.myUid(); 76 mTManager = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); 77 mDeviceId = mTManager.getDeviceId(); 78 } 79 80 @Override tearDown()81 protected void tearDown() throws Exception { 82 mConnectionUtil.cleanUp(); 83 super.tearDown(); 84 } 85 86 /** 87 * Ensure that downloading on wifi reports reasonable stats. 88 */ 89 @LargeTest testWifiDownload()90 public void testWifiDownload() throws Exception { 91 mConnectionUtil.wifiTestInit(); 92 assertTrue("Could not connect to wifi!", setDeviceWifiAndAirplaneMode(mSsid)); 93 downloadFile(); 94 } 95 96 /** 97 * Ensure that downloading on mobile reports reasonable stats. 98 */ 99 @LargeTest testMobileDownload()100 public void testMobileDownload() throws Exception { 101 // As part of the setup we disconnected from wifi; make sure we are connected to mobile and 102 // that we have data. 103 assertTrue("Do not have mobile data!", hasMobileData()); 104 downloadFile(); 105 } 106 107 /** 108 * Helper method that downloads a file using http connection from a test server and reports the 109 * data usage stats to instrumentation out. 110 */ downloadFile()111 protected void downloadFile() throws Exception { 112 NetworkStats pre_test_stats = fetchDataFromProc(mUid); 113 String ts = Long.toString(System.currentTimeMillis()); 114 115 String targetUrl = BandwidthTestUtil.buildDownloadUrl( 116 mTestServer, FILE_SIZE, mDeviceId, ts); 117 TrafficStats.startDataProfiling(mContext); 118 File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME); 119 assertTrue(BandwidthTestUtil.DownloadFromUrl(targetUrl, tmpSaveFile)); 120 NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext); 121 Log.d(LOG_TAG, prof_stats.toString()); 122 123 NetworkStats post_test_stats = fetchDataFromProc(mUid); 124 NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats); 125 126 // Output measurements to instrumentation out, so that it can be compared to that of 127 // the server. 128 Bundle results = new Bundle(); 129 results.putString("device_id", mDeviceId); 130 results.putString("timestamp", ts); 131 results.putInt("size", FILE_SIZE); 132 addStatsToResults(PROF_LABEL, prof_stats, results, mUid); 133 addStatsToResults(PROC_LABEL, proc_stats, results, mUid); 134 getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); 135 136 // Clean up. 137 assertTrue(cleanUpFile(tmpSaveFile)); 138 } 139 140 /** 141 * Ensure that uploading on wifi reports reasonable stats. 142 */ 143 @LargeTest testWifiUpload()144 public void testWifiUpload() throws Exception { 145 mConnectionUtil.wifiTestInit(); 146 assertTrue(setDeviceWifiAndAirplaneMode(mSsid)); 147 uploadFile(); 148 } 149 150 /** 151 * Ensure that uploading on wifi reports reasonable stats. 152 */ 153 @LargeTest testMobileUpload()154 public void testMobileUpload() throws Exception { 155 assertTrue(hasMobileData()); 156 uploadFile(); 157 } 158 159 /** 160 * Helper method that downloads a test file to upload. The stats reported to instrumentation out 161 * only include upload stats. 162 */ uploadFile()163 protected void uploadFile() throws Exception { 164 // Download a file from the server. 165 String ts = Long.toString(System.currentTimeMillis()); 166 String targetUrl = BandwidthTestUtil.buildDownloadUrl( 167 mTestServer, FILE_SIZE, mDeviceId, ts); 168 File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME); 169 assertTrue(BandwidthTestUtil.DownloadFromUrl(targetUrl, tmpSaveFile)); 170 171 ts = Long.toString(System.currentTimeMillis()); 172 NetworkStats pre_test_stats = fetchDataFromProc(mUid); 173 TrafficStats.startDataProfiling(mContext); 174 assertTrue(BandwidthTestUtil.postFileToServer(mTestServer, mDeviceId, ts, tmpSaveFile)); 175 NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext); 176 Log.d(LOG_TAG, prof_stats.toString()); 177 NetworkStats post_test_stats = fetchDataFromProc(mUid); 178 NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats); 179 180 // Output measurements to instrumentation out, so that it can be compared to that of 181 // the server. 182 Bundle results = new Bundle(); 183 results.putString("device_id", mDeviceId); 184 results.putString("timestamp", ts); 185 results.putInt("size", FILE_SIZE); 186 addStatsToResults(PROF_LABEL, prof_stats, results, mUid); 187 addStatsToResults(PROC_LABEL, proc_stats, results, mUid); 188 getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); 189 190 // Clean up. 191 assertTrue(cleanUpFile(tmpSaveFile)); 192 } 193 194 /** 195 * We want to make sure that if we use wifi and the Download Manager to download stuff, 196 * accounting still goes to the app making the call and that the numbers still make sense. 197 */ 198 @LargeTest testWifiDownloadWithDownloadManager()199 public void testWifiDownloadWithDownloadManager() throws Exception { 200 mConnectionUtil.wifiTestInit(); 201 assertTrue(setDeviceWifiAndAirplaneMode(mSsid)); 202 downloadFileUsingDownloadManager(); 203 } 204 205 /** 206 * We want to make sure that if we use mobile data and the Download Manager to download stuff, 207 * accounting still goes to the app making the call and that the numbers still make sense. 208 */ 209 @LargeTest testMobileDownloadWithDownloadManager()210 public void testMobileDownloadWithDownloadManager() throws Exception { 211 assertTrue(hasMobileData()); 212 downloadFileUsingDownloadManager(); 213 } 214 215 /** 216 * Helper method that downloads a file from a test server using the download manager and reports 217 * the stats to instrumentation out. 218 */ downloadFileUsingDownloadManager()219 protected void downloadFileUsingDownloadManager() throws Exception { 220 // If we are using the download manager, then the data that is written to /proc/uid_stat/ 221 // is accounted against download manager's uid, since it uses pre-ICS API. 222 int downloadManagerUid = mConnectionUtil.downloadManagerUid(); 223 assertTrue(downloadManagerUid >= 0); 224 NetworkStats pre_test_stats = fetchDataFromProc(downloadManagerUid); 225 // start profiling 226 TrafficStats.startDataProfiling(mContext); 227 String ts = Long.toString(System.currentTimeMillis()); 228 String targetUrl = BandwidthTestUtil.buildDownloadUrl( 229 mTestServer, FILE_SIZE, mDeviceId, ts); 230 Log.v(LOG_TAG, "Download url: " + targetUrl); 231 File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME); 232 assertTrue(mConnectionUtil.startDownloadAndWait(targetUrl, 500000)); 233 NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext); 234 NetworkStats post_test_stats = fetchDataFromProc(downloadManagerUid); 235 NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats); 236 Log.d(LOG_TAG, prof_stats.toString()); 237 // Output measurements to instrumentation out, so that it can be compared to that of 238 // the server. 239 Bundle results = new Bundle(); 240 results.putString("device_id", mDeviceId); 241 results.putString("timestamp", ts); 242 results.putInt("size", FILE_SIZE); 243 addStatsToResults(PROF_LABEL, prof_stats, results, mUid); 244 // remember to use download manager uid for proc stats 245 addStatsToResults(PROC_LABEL, proc_stats, results, downloadManagerUid); 246 getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); 247 248 // Clean up. 249 assertTrue(cleanUpFile(tmpSaveFile)); 250 } 251 252 /** 253 * Fetch network data from /proc/uid_stat/uid 254 * 255 * @return populated {@link NetworkStats} 256 */ fetchDataFromProc(int uid)257 public NetworkStats fetchDataFromProc(int uid) { 258 String root_filepath = "/proc/uid_stat/" + uid + "/"; 259 File rcv_stat = new File (root_filepath + "tcp_rcv"); 260 int rx = BandwidthTestUtil.parseIntValueFromFile(rcv_stat); 261 File snd_stat = new File (root_filepath + "tcp_snd"); 262 int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat); 263 NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); 264 stats.addValues(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT, 265 NetworkStats.TAG_NONE, rx, 0, tx, 0, 0); 266 return stats; 267 } 268 269 /** 270 * Turn on Airplane mode and connect to the wifi. 271 * 272 * @param ssid of the wifi to connect to 273 * @return true if we successfully connected to a given network. 274 */ setDeviceWifiAndAirplaneMode(String ssid)275 public boolean setDeviceWifiAndAirplaneMode(String ssid) { 276 mConnectionUtil.setAirplaneMode(mContext, true); 277 assertTrue(mConnectionUtil.connectToWifi(ssid)); 278 assertTrue(mConnectionUtil.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, 279 ConnectionUtil.LONG_TIMEOUT)); 280 assertTrue(mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_WIFI, 281 State.CONNECTED, ConnectionUtil.LONG_TIMEOUT)); 282 return mConnectionUtil.hasData(); 283 } 284 285 /** 286 * Helper method to make sure we are connected to mobile data. 287 * 288 * @return true if we successfully connect to mobile data. 289 */ hasMobileData()290 public boolean hasMobileData() { 291 assertTrue(mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, 292 State.CONNECTED, ConnectionUtil.LONG_TIMEOUT)); 293 assertTrue("Not connected to mobile", mConnectionUtil.isConnectedToMobile()); 294 assertFalse("Still connected to wifi.", mConnectionUtil.isConnectedToWifi()); 295 return mConnectionUtil.hasData(); 296 } 297 298 /** 299 * Output the {@link NetworkStats} to Instrumentation out. 300 * 301 * @param label to attach to this given stats. 302 * @param stats {@link NetworkStats} to add. 303 * @param results {@link Bundle} to be added to. 304 * @param uid for which to report the results. 305 */ addStatsToResults(String label, NetworkStats stats, Bundle results, int uid)306 public void addStatsToResults(String label, NetworkStats stats, Bundle results, int uid){ 307 if (results == null || results.isEmpty()) { 308 Log.e(LOG_TAG, "Empty bundle provided."); 309 return; 310 } 311 Entry totalStats = null; 312 for (int i = 0; i < stats.size(); ++i) { 313 Entry statsEntry = stats.getValues(i, null); 314 // We are only interested in the all inclusive stats. 315 if (statsEntry.tag != 0) { 316 continue; 317 } 318 // skip stats for other uids 319 if (statsEntry.uid != uid) { 320 continue; 321 } 322 if (totalStats == null || statsEntry.set == NetworkStats.SET_ALL) { 323 totalStats = statsEntry; 324 } else { 325 totalStats.rxBytes += statsEntry.rxBytes; 326 totalStats.txBytes += statsEntry.txBytes; 327 } 328 } 329 // Output merged stats to bundle. 330 results.putInt(label + "uid", totalStats.uid); 331 results.putLong(label + "tx", totalStats.txBytes); 332 results.putLong(label + "rx", totalStats.rxBytes); 333 } 334 335 /** 336 * Remove file if it exists. 337 * @param file {@link File} to delete. 338 * @return true if successfully deleted the file. 339 */ cleanUpFile(File file)340 private boolean cleanUpFile(File file) { 341 if (file.exists()) { 342 return file.delete(); 343 } 344 return true; 345 } 346 } 347