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