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 import java.util.HashMap;
     40 import java.util.Map;
     41 
     42 /**
     43  * Test that downloads files from a test server and reports the bandwidth metrics collected.
     44  */
     45 public 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         mConnectionUtil.wifiTestInit();
     94         assertTrue("Could not connect to wifi!", setDeviceWifiAndAirplaneMode(mSsid));
     95         downloadFile();
     96     }
     97 
     98     /**
     99      * Ensure that downloading on mobile reports reasonable stats.
    100      */
    101     @LargeTest
    102     public void testMobileDownload() throws Exception {
    103         // As part of the setup we disconnected from wifi; make sure we are connected to mobile and
    104         // that we have data.
    105         assertTrue("Do not have mobile data!", hasMobileData());
    106         downloadFile();
    107     }
    108 
    109     /**
    110      * Helper method that downloads a file using http connection from a test server and reports the
    111      * data usage stats to instrumentation out.
    112      */
    113     protected void downloadFile() throws Exception {
    114         NetworkStats pre_test_stats = fetchDataFromProc(mUid);
    115         String ts = Long.toString(System.currentTimeMillis());
    116 
    117         String targetUrl = BandwidthTestUtil.buildDownloadUrl(
    118                 mTestServer, FILE_SIZE, mDeviceId, ts);
    119         TrafficStats.startDataProfiling(mContext);
    120         File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME);
    121         assertTrue(BandwidthTestUtil.DownloadFromUrl(targetUrl, tmpSaveFile));
    122         NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext);
    123         Log.d(LOG_TAG, prof_stats.toString());
    124 
    125         NetworkStats post_test_stats = fetchDataFromProc(mUid);
    126         NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats);
    127 
    128         // Output measurements to instrumentation out, so that it can be compared to that of
    129         // the server.
    130         Bundle results = new Bundle();
    131         results.putString("device_id", mDeviceId);
    132         results.putString("timestamp", ts);
    133         results.putInt("size", FILE_SIZE);
    134         AddStatsToResults(PROF_LABEL, prof_stats, results);
    135         AddStatsToResults(PROC_LABEL, proc_stats, results);
    136         getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
    137 
    138         // Clean up.
    139         assertTrue(cleanUpFile(tmpSaveFile));
    140     }
    141 
    142     /**
    143      * Ensure that uploading on wifi reports reasonable stats.
    144      */
    145     @LargeTest
    146     public void testWifiUpload() throws Exception {
    147         mConnectionUtil.wifiTestInit();
    148         assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
    149         uploadFile();
    150     }
    151 
    152     /**
    153      *  Ensure that uploading on wifi reports reasonable stats.
    154      */
    155     @LargeTest
    156     public void testMobileUpload() throws Exception {
    157         assertTrue(hasMobileData());
    158         uploadFile();
    159     }
    160 
    161     /**
    162      * Helper method that downloads a test file to upload. The stats reported to instrumentation out
    163      * only include upload stats.
    164      */
    165     protected void uploadFile() throws Exception {
    166         // Download a file from the server.
    167         String ts = Long.toString(System.currentTimeMillis());
    168         String targetUrl = BandwidthTestUtil.buildDownloadUrl(
    169                 mTestServer, FILE_SIZE, mDeviceId, ts);
    170         File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME);
    171         assertTrue(BandwidthTestUtil.DownloadFromUrl(targetUrl, tmpSaveFile));
    172 
    173         ts = Long.toString(System.currentTimeMillis());
    174         NetworkStats pre_test_stats = fetchDataFromProc(mUid);
    175         TrafficStats.startDataProfiling(mContext);
    176         assertTrue(BandwidthTestUtil.postFileToServer(mTestServer, mDeviceId, ts, tmpSaveFile));
    177         NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext);
    178         Log.d(LOG_TAG, prof_stats.toString());
    179         NetworkStats post_test_stats = fetchDataFromProc(mUid);
    180         NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats);
    181 
    182         // Output measurements to instrumentation out, so that it can be compared to that of
    183         // the server.
    184         Bundle results = new Bundle();
    185         results.putString("device_id", mDeviceId);
    186         results.putString("timestamp", ts);
    187         results.putInt("size", FILE_SIZE);
    188         AddStatsToResults(PROF_LABEL, prof_stats, results);
    189         AddStatsToResults(PROC_LABEL, proc_stats, results);
    190         getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
    191 
    192         // Clean up.
    193         assertTrue(cleanUpFile(tmpSaveFile));
    194     }
    195 
    196     /**
    197      * We want to make sure that if we use wifi and the  Download Manager to download stuff,
    198      * accounting still goes to the app making the call and that the numbers still make sense.
    199      */
    200     @LargeTest
    201     public void testWifiDownloadWithDownloadManager() throws Exception {
    202         mConnectionUtil.wifiTestInit();
    203         assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
    204         downloadFileUsingDownloadManager();
    205     }
    206 
    207     /**
    208      * We want to make sure that if we use mobile data and the Download Manager to download stuff,
    209      * accounting still goes to the app making the call and that the numbers still make sense.
    210      */
    211     @LargeTest
    212     public void testMobileDownloadWithDownloadManager() throws Exception {
    213         assertTrue(hasMobileData());
    214         downloadFileUsingDownloadManager();
    215     }
    216 
    217     /**
    218      * Helper method that downloads a file from a test server using the download manager and reports
    219      * the stats to instrumentation out.
    220      */
    221     protected void downloadFileUsingDownloadManager() throws Exception {
    222         // If we are using the download manager, then the data that is written to /proc/uid_stat/
    223         // is accounted against download manager's uid, since it uses pre-ICS API.
    224         int downloadManagerUid = mConnectionUtil.downloadManagerUid();
    225         assertTrue(downloadManagerUid >= 0);
    226         NetworkStats pre_test_stats = fetchDataFromProc(downloadManagerUid);
    227         // start profiling
    228         TrafficStats.startDataProfiling(mContext);
    229         String ts = Long.toString(System.currentTimeMillis());
    230         String targetUrl = BandwidthTestUtil.buildDownloadUrl(
    231                 mTestServer, FILE_SIZE, mDeviceId, ts);
    232         Log.v(LOG_TAG, "Download url: " + targetUrl);
    233         File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME);
    234         assertTrue(mConnectionUtil.startDownloadAndWait(targetUrl, 500000));
    235         NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext);
    236         NetworkStats post_test_stats = fetchDataFromProc(downloadManagerUid);
    237         NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats);
    238         Log.d(LOG_TAG, prof_stats.toString());
    239         // Output measurements to instrumentation out, so that it can be compared to that of
    240         // the server.
    241         Bundle results = new Bundle();
    242         results.putString("device_id", mDeviceId);
    243         results.putString("timestamp", ts);
    244         results.putInt("size", FILE_SIZE);
    245         AddStatsToResults(PROF_LABEL, prof_stats, results);
    246         AddStatsToResults(PROC_LABEL, proc_stats, results);
    247         getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
    248 
    249         // Clean up.
    250         assertTrue(cleanUpFile(tmpSaveFile));
    251     }
    252 
    253     /**
    254      * Fetch network data from /proc/uid_stat/uid
    255      *
    256      * @return populated {@link NetworkStats}
    257      */
    258     public NetworkStats fetchDataFromProc(int uid) {
    259         String root_filepath = "/proc/uid_stat/" + uid + "/";
    260         File rcv_stat = new File (root_filepath + "tcp_rcv");
    261         int rx = BandwidthTestUtil.parseIntValueFromFile(rcv_stat);
    262         File snd_stat = new File (root_filepath + "tcp_snd");
    263         int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat);
    264         NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
    265         stats.addValues(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
    266                 NetworkStats.TAG_NONE, rx, 0, tx, 0, 0);
    267         return stats;
    268     }
    269 
    270     /**
    271      * Turn on Airplane mode and connect to the wifi.
    272      *
    273      * @param ssid of the wifi to connect to
    274      * @return true if we successfully connected to a given network.
    275      */
    276     public boolean setDeviceWifiAndAirplaneMode(String ssid) {
    277         mConnectionUtil.setAirplaneMode(mContext, true);
    278         assertTrue(mConnectionUtil.connectToWifi(ssid));
    279         assertTrue(mConnectionUtil.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
    280                 ConnectionUtil.LONG_TIMEOUT));
    281         assertTrue(mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_WIFI,
    282                 State.CONNECTED, ConnectionUtil.LONG_TIMEOUT));
    283         return mConnectionUtil.hasData();
    284     }
    285 
    286     /**
    287      * Helper method to make sure we are connected to mobile data.
    288      *
    289      * @return true if we successfully connect to mobile data.
    290      */
    291     public boolean hasMobileData() {
    292         assertTrue(mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_MOBILE,
    293                 State.CONNECTED, ConnectionUtil.LONG_TIMEOUT));
    294         assertTrue("Not connected to mobile", mConnectionUtil.isConnectedToMobile());
    295         assertFalse("Still connected to wifi.", mConnectionUtil.isConnectedToWifi());
    296         return mConnectionUtil.hasData();
    297     }
    298 
    299     /**
    300      * Output the {@link NetworkStats} to Instrumentation out.
    301      *
    302      * @param label to attach to this given stats.
    303      * @param stats {@link NetworkStats} to add.
    304      * @param results {@link Bundle} to be added to.
    305      */
    306     public void AddStatsToResults(String label, NetworkStats stats, Bundle results){
    307         if (results == null || results.isEmpty()) {
    308             Log.e(LOG_TAG, "Empty bundle provided.");
    309             return;
    310         }
    311         // Merge stats across all sets.
    312         Map<Integer, Entry> totalStats = new HashMap<Integer, Entry>();
    313         for (int i = 0; i < stats.size(); ++i) {
    314             Entry statsEntry = stats.getValues(i, null);
    315             // We are only interested in the all inclusive stats.
    316             if (statsEntry.tag != 0) {
    317                 continue;
    318             }
    319             Entry mapEntry = null;
    320             if (totalStats.containsKey(statsEntry.uid)) {
    321                 mapEntry = totalStats.get(statsEntry.uid);
    322                 switch (statsEntry.set) {
    323                     case NetworkStats.SET_ALL:
    324                         mapEntry.rxBytes = statsEntry.rxBytes;
    325                         mapEntry.txBytes = statsEntry.txBytes;
    326                         break;
    327                     case NetworkStats.SET_DEFAULT:
    328                     case NetworkStats.SET_FOREGROUND:
    329                         mapEntry.rxBytes += statsEntry.rxBytes;
    330                         mapEntry.txBytes += statsEntry.txBytes;
    331                         break;
    332                     default:
    333                         Log.w(LOG_TAG, "Invalid state found in NetworkStats.");
    334                 }
    335             } else {
    336                 totalStats.put(statsEntry.uid, statsEntry);
    337             }
    338         }
    339         // Ouput merged stats to bundle.
    340         for (Entry entry : totalStats.values()) {
    341             results.putInt(label + "uid", entry.uid);
    342             results.putLong(label + "tx", entry.txBytes);
    343             results.putLong(label + "rx", entry.rxBytes);
    344         }
    345     }
    346 
    347     /**
    348      * Remove file if it exists.
    349      * @param file {@link File} to delete.
    350      * @return true if successfully deleted the file.
    351      */
    352     private boolean cleanUpFile(File file) {
    353         if (file.exists()) {
    354             return file.delete();
    355         }
    356         return true;
    357     }
    358 }
    359