Home | History | Annotate | Download | only in bandwidthtest
      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
     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
     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
     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
    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      */
    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
    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
    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, 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
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    340     private boolean cleanUpFile(File file) {
    341         if (file.exists()) {
    342             return file.delete();
    343         }
    344         return true;
    345     }
    346 }
    347