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