| /* |
| * Copyright (C) 2011, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.bandwidthtest; |
| |
| import android.content.Context; |
| import android.net.ConnectivityManager; |
| import android.net.NetworkInfo.State; |
| import android.net.NetworkStats; |
| import android.net.NetworkStats.Entry; |
| import android.net.TrafficStats; |
| import android.net.wifi.WifiManager; |
| import android.os.Bundle; |
| import android.os.Environment; |
| import android.os.Process; |
| import android.os.SystemClock; |
| import android.telephony.TelephonyManager; |
| import android.test.InstrumentationTestCase; |
| import android.test.suitebuilder.annotation.LargeTest; |
| import android.util.Log; |
| |
| import com.android.bandwidthtest.util.BandwidthTestUtil; |
| import com.android.bandwidthtest.util.ConnectionUtil; |
| |
| import java.io.File; |
| |
| /** |
| * Test that downloads files from a test server and reports the bandwidth metrics collected. |
| */ |
| public class BandwidthTest extends InstrumentationTestCase { |
| |
| private static final String LOG_TAG = "BandwidthTest"; |
| private final static String PROF_LABEL = "PROF_"; |
| private final static String PROC_LABEL = "PROC_"; |
| private final static int INSTRUMENTATION_IN_PROGRESS = 2; |
| |
| private final static String BASE_DIR = |
| Environment.getExternalStorageDirectory().getAbsolutePath(); |
| private final static String TMP_FILENAME = "tmp.dat"; |
| // Download 10.486 * 106 bytes (+ headers) from app engine test server. |
| private final int FILE_SIZE = 10485613; |
| private Context mContext; |
| private ConnectionUtil mConnectionUtil; |
| private TelephonyManager mTManager; |
| private int mUid; |
| private String mSsid; |
| private String mTestServer; |
| private String mDeviceId; |
| private BandwidthTestRunner mRunner; |
| |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| mRunner = (BandwidthTestRunner) getInstrumentation(); |
| mSsid = mRunner.mSsid; |
| mTestServer = mRunner.mTestServer; |
| mContext = mRunner.getTargetContext(); |
| mConnectionUtil = new ConnectionUtil(mContext); |
| mConnectionUtil.initialize(); |
| Log.v(LOG_TAG, "Initialized mConnectionUtil"); |
| mUid = Process.myUid(); |
| mTManager = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); |
| mDeviceId = mTManager.getDeviceId(); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| mConnectionUtil.cleanUp(); |
| super.tearDown(); |
| } |
| |
| /** |
| * Ensure that downloading on wifi reports reasonable stats. |
| */ |
| @LargeTest |
| public void testWifiDownload() throws Exception { |
| mConnectionUtil.wifiTestInit(); |
| assertTrue("Could not connect to wifi!", setDeviceWifiAndAirplaneMode(mSsid)); |
| downloadFile(); |
| } |
| |
| /** |
| * Ensure that downloading on mobile reports reasonable stats. |
| */ |
| @LargeTest |
| public void testMobileDownload() throws Exception { |
| // As part of the setup we disconnected from wifi; make sure we are connected to mobile and |
| // that we have data. |
| assertTrue("Do not have mobile data!", hasMobileData()); |
| downloadFile(); |
| } |
| |
| /** |
| * Helper method that downloads a file using http connection from a test server and reports the |
| * data usage stats to instrumentation out. |
| */ |
| protected void downloadFile() throws Exception { |
| NetworkStats pre_test_stats = fetchDataFromProc(mUid); |
| String ts = Long.toString(System.currentTimeMillis()); |
| |
| String targetUrl = BandwidthTestUtil.buildDownloadUrl( |
| mTestServer, FILE_SIZE, mDeviceId, ts); |
| TrafficStats.startDataProfiling(mContext); |
| File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME); |
| assertTrue(BandwidthTestUtil.DownloadFromUrl(targetUrl, tmpSaveFile)); |
| NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext); |
| Log.d(LOG_TAG, prof_stats.toString()); |
| |
| NetworkStats post_test_stats = fetchDataFromProc(mUid); |
| NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats); |
| |
| // Output measurements to instrumentation out, so that it can be compared to that of |
| // the server. |
| Bundle results = new Bundle(); |
| results.putString("device_id", mDeviceId); |
| results.putString("timestamp", ts); |
| results.putInt("size", FILE_SIZE); |
| addStatsToResults(PROF_LABEL, prof_stats, results, mUid); |
| addStatsToResults(PROC_LABEL, proc_stats, results, mUid); |
| getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); |
| |
| // Clean up. |
| assertTrue(cleanUpFile(tmpSaveFile)); |
| } |
| |
| /** |
| * Ensure that uploading on wifi reports reasonable stats. |
| */ |
| @LargeTest |
| public void testWifiUpload() throws Exception { |
| mConnectionUtil.wifiTestInit(); |
| assertTrue(setDeviceWifiAndAirplaneMode(mSsid)); |
| uploadFile(); |
| } |
| |
| /** |
| * Ensure that uploading on wifi reports reasonable stats. |
| */ |
| @LargeTest |
| public void testMobileUpload() throws Exception { |
| assertTrue(hasMobileData()); |
| uploadFile(); |
| } |
| |
| /** |
| * Helper method that downloads a test file to upload. The stats reported to instrumentation out |
| * only include upload stats. |
| */ |
| protected void uploadFile() throws Exception { |
| // Download a file from the server. |
| String ts = Long.toString(System.currentTimeMillis()); |
| String targetUrl = BandwidthTestUtil.buildDownloadUrl( |
| mTestServer, FILE_SIZE, mDeviceId, ts); |
| File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME); |
| assertTrue(BandwidthTestUtil.DownloadFromUrl(targetUrl, tmpSaveFile)); |
| |
| ts = Long.toString(System.currentTimeMillis()); |
| NetworkStats pre_test_stats = fetchDataFromProc(mUid); |
| TrafficStats.startDataProfiling(mContext); |
| assertTrue(BandwidthTestUtil.postFileToServer(mTestServer, mDeviceId, ts, tmpSaveFile)); |
| NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext); |
| Log.d(LOG_TAG, prof_stats.toString()); |
| NetworkStats post_test_stats = fetchDataFromProc(mUid); |
| NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats); |
| |
| // Output measurements to instrumentation out, so that it can be compared to that of |
| // the server. |
| Bundle results = new Bundle(); |
| results.putString("device_id", mDeviceId); |
| results.putString("timestamp", ts); |
| results.putInt("size", FILE_SIZE); |
| addStatsToResults(PROF_LABEL, prof_stats, results, mUid); |
| addStatsToResults(PROC_LABEL, proc_stats, results, mUid); |
| getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); |
| |
| // Clean up. |
| assertTrue(cleanUpFile(tmpSaveFile)); |
| } |
| |
| /** |
| * We want to make sure that if we use wifi and the Download Manager to download stuff, |
| * accounting still goes to the app making the call and that the numbers still make sense. |
| */ |
| @LargeTest |
| public void testWifiDownloadWithDownloadManager() throws Exception { |
| mConnectionUtil.wifiTestInit(); |
| assertTrue(setDeviceWifiAndAirplaneMode(mSsid)); |
| downloadFileUsingDownloadManager(); |
| } |
| |
| /** |
| * We want to make sure that if we use mobile data and the Download Manager to download stuff, |
| * accounting still goes to the app making the call and that the numbers still make sense. |
| */ |
| @LargeTest |
| public void testMobileDownloadWithDownloadManager() throws Exception { |
| assertTrue(hasMobileData()); |
| downloadFileUsingDownloadManager(); |
| } |
| |
| /** |
| * Helper method that downloads a file from a test server using the download manager and reports |
| * the stats to instrumentation out. |
| */ |
| protected void downloadFileUsingDownloadManager() throws Exception { |
| // If we are using the download manager, then the data that is written to /proc/uid_stat/ |
| // is accounted against download manager's uid, since it uses pre-ICS API. |
| int downloadManagerUid = mConnectionUtil.downloadManagerUid(); |
| assertTrue(downloadManagerUid >= 0); |
| NetworkStats pre_test_stats = fetchDataFromProc(downloadManagerUid); |
| // start profiling |
| TrafficStats.startDataProfiling(mContext); |
| String ts = Long.toString(System.currentTimeMillis()); |
| String targetUrl = BandwidthTestUtil.buildDownloadUrl( |
| mTestServer, FILE_SIZE, mDeviceId, ts); |
| Log.v(LOG_TAG, "Download url: " + targetUrl); |
| File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME); |
| assertTrue(mConnectionUtil.startDownloadAndWait(targetUrl, 500000)); |
| NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext); |
| NetworkStats post_test_stats = fetchDataFromProc(downloadManagerUid); |
| NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats); |
| Log.d(LOG_TAG, prof_stats.toString()); |
| // Output measurements to instrumentation out, so that it can be compared to that of |
| // the server. |
| Bundle results = new Bundle(); |
| results.putString("device_id", mDeviceId); |
| results.putString("timestamp", ts); |
| results.putInt("size", FILE_SIZE); |
| addStatsToResults(PROF_LABEL, prof_stats, results, mUid); |
| // remember to use download manager uid for proc stats |
| addStatsToResults(PROC_LABEL, proc_stats, results, downloadManagerUid); |
| getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results); |
| |
| // Clean up. |
| assertTrue(cleanUpFile(tmpSaveFile)); |
| } |
| |
| /** |
| * Fetch network data from /proc/uid_stat/uid |
| * |
| * @return populated {@link NetworkStats} |
| */ |
| public NetworkStats fetchDataFromProc(int uid) { |
| String root_filepath = "/proc/uid_stat/" + uid + "/"; |
| File rcv_stat = new File (root_filepath + "tcp_rcv"); |
| int rx = BandwidthTestUtil.parseIntValueFromFile(rcv_stat); |
| File snd_stat = new File (root_filepath + "tcp_snd"); |
| int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat); |
| NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); |
| stats.addValues(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT, |
| NetworkStats.TAG_NONE, rx, 0, tx, 0, 0); |
| return stats; |
| } |
| |
| /** |
| * Turn on Airplane mode and connect to the wifi. |
| * |
| * @param ssid of the wifi to connect to |
| * @return true if we successfully connected to a given network. |
| */ |
| public boolean setDeviceWifiAndAirplaneMode(String ssid) { |
| mConnectionUtil.setAirplaneMode(mContext, true); |
| assertTrue(mConnectionUtil.connectToWifi(ssid)); |
| assertTrue(mConnectionUtil.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, |
| ConnectionUtil.LONG_TIMEOUT)); |
| assertTrue(mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_WIFI, |
| State.CONNECTED, ConnectionUtil.LONG_TIMEOUT)); |
| return mConnectionUtil.hasData(); |
| } |
| |
| /** |
| * Helper method to make sure we are connected to mobile data. |
| * |
| * @return true if we successfully connect to mobile data. |
| */ |
| public boolean hasMobileData() { |
| assertTrue(mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, |
| State.CONNECTED, ConnectionUtil.LONG_TIMEOUT)); |
| assertTrue("Not connected to mobile", mConnectionUtil.isConnectedToMobile()); |
| assertFalse("Still connected to wifi.", mConnectionUtil.isConnectedToWifi()); |
| return mConnectionUtil.hasData(); |
| } |
| |
| /** |
| * Output the {@link NetworkStats} to Instrumentation out. |
| * |
| * @param label to attach to this given stats. |
| * @param stats {@link NetworkStats} to add. |
| * @param results {@link Bundle} to be added to. |
| * @param uid for which to report the results. |
| */ |
| public void addStatsToResults(String label, NetworkStats stats, Bundle results, int uid){ |
| if (results == null || results.isEmpty()) { |
| Log.e(LOG_TAG, "Empty bundle provided."); |
| return; |
| } |
| Entry totalStats = null; |
| for (int i = 0; i < stats.size(); ++i) { |
| Entry statsEntry = stats.getValues(i, null); |
| // We are only interested in the all inclusive stats. |
| if (statsEntry.tag != 0) { |
| continue; |
| } |
| // skip stats for other uids |
| if (statsEntry.uid != uid) { |
| continue; |
| } |
| if (totalStats == null || statsEntry.set == NetworkStats.SET_ALL) { |
| totalStats = statsEntry; |
| } else { |
| totalStats.rxBytes += statsEntry.rxBytes; |
| totalStats.txBytes += statsEntry.txBytes; |
| } |
| } |
| // Output merged stats to bundle. |
| results.putInt(label + "uid", totalStats.uid); |
| results.putLong(label + "tx", totalStats.txBytes); |
| results.putLong(label + "rx", totalStats.rxBytes); |
| } |
| |
| /** |
| * Remove file if it exists. |
| * @param file {@link File} to delete. |
| * @return true if successfully deleted the file. |
| */ |
| private boolean cleanUpFile(File file) { |
| if (file.exists()) { |
| return file.delete(); |
| } |
| return true; |
| } |
| } |