Home | History | Annotate | Download | only in security
      1 package android.security;
      2 
      3 import android.app.DownloadManager;
      4 import android.content.BroadcastReceiver;
      5 import android.content.Context;
      6 import android.content.Intent;
      7 import android.content.IntentFilter;
      8 import android.content.pm.ApplicationInfo;
      9 import android.database.Cursor;
     10 import android.media.MediaPlayer;
     11 import android.net.Uri;
     12 import android.net.http.AndroidHttpClient;
     13 import android.test.AndroidTestCase;
     14 import android.webkit.cts.CtsTestServer;
     15 
     16 import org.apache.http.HttpResponse;
     17 import org.apache.http.client.methods.HttpGet;
     18 
     19 import java.io.IOException;
     20 import java.net.HttpURLConnection;
     21 import java.net.URL;
     22 import java.net.UnknownServiceException;
     23 import java.util.concurrent.CancellationException;
     24 import java.util.concurrent.ExecutionException;
     25 import java.util.concurrent.Future;
     26 import java.util.concurrent.TimeUnit;
     27 import java.util.concurrent.TimeoutException;
     28 
     29 abstract class NetworkSecurityPolicyTestBase extends AndroidTestCase {
     30     private CtsTestServer mHttpOnlyWebServer;
     31 
     32     private final boolean mCleartextTrafficExpectedToBePermitted;
     33 
     34     NetworkSecurityPolicyTestBase(boolean cleartextTrafficExpectedToBePermitted) {
     35         mCleartextTrafficExpectedToBePermitted = cleartextTrafficExpectedToBePermitted;
     36     }
     37 
     38     @Override
     39     protected void setUp() throws Exception {
     40         super.setUp();
     41         mHttpOnlyWebServer = new CtsTestServer(mContext, false);
     42     }
     43 
     44     @Override
     45     protected void tearDown() throws Exception {
     46         try {
     47             mHttpOnlyWebServer.shutdown();
     48         } finally {
     49             super.tearDown();
     50         }
     51     }
     52 
     53     public void testNetworkSecurityPolicy() {
     54         assertEquals(mCleartextTrafficExpectedToBePermitted,
     55                 NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
     56     }
     57 
     58     public void testApplicationInfoFlag() {
     59         ApplicationInfo appInfo = getContext().getApplicationInfo();
     60         int expectedValue = (mCleartextTrafficExpectedToBePermitted)
     61                 ? ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC : 0;
     62         assertEquals(expectedValue, appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC);
     63     }
     64 
     65     public void testDefaultHttpURLConnection() throws Exception {
     66         if (mCleartextTrafficExpectedToBePermitted) {
     67             assertCleartextHttpURLConnectionSucceeds();
     68         } else {
     69             assertCleartextHttpURLConnectionBlocked();
     70         }
     71     }
     72 
     73     private void assertCleartextHttpURLConnectionSucceeds() throws Exception {
     74         URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
     75         HttpURLConnection conn = null;
     76         try {
     77             mHttpOnlyWebServer.resetRequestState();
     78             conn = (HttpURLConnection) url.openConnection();
     79             conn.setConnectTimeout(5000);
     80             conn.setReadTimeout(5000);
     81             assertEquals(200, conn.getResponseCode());
     82         } finally {
     83             if (conn != null) {
     84                 conn.disconnect();
     85             }
     86         }
     87         Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
     88         assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
     89     }
     90 
     91     private void assertCleartextHttpURLConnectionBlocked() throws Exception {
     92         URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
     93         HttpURLConnection conn = null;
     94         try {
     95             mHttpOnlyWebServer.resetRequestState();
     96             conn = (HttpURLConnection) url.openConnection();
     97             conn.setConnectTimeout(5000);
     98             conn.setReadTimeout(5000);
     99             conn.getResponseCode();
    100             fail();
    101         } catch (IOException e) {
    102             if ((e.getMessage() == null) || (!e.getMessage().toLowerCase().contains("cleartext"))) {
    103                 fail("Exception with which request failed does not mention cleartext: " + e);
    104             }
    105         } finally {
    106             if (conn != null) {
    107                 conn.disconnect();
    108             }
    109         }
    110         Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
    111         assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
    112     }
    113 
    114     public void testAndroidHttpClient() throws Exception {
    115         if (mCleartextTrafficExpectedToBePermitted) {
    116             assertAndroidHttpClientCleartextRequestSucceeds();
    117         } else {
    118             assertAndroidHttpClientCleartextRequestBlocked();
    119         }
    120     }
    121 
    122     private void assertAndroidHttpClientCleartextRequestSucceeds() throws Exception {
    123         URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
    124         AndroidHttpClient httpClient = AndroidHttpClient.newInstance(null);
    125         try {
    126             HttpResponse response = httpClient.execute(new HttpGet(url.toString()));
    127             assertEquals(200, response.getStatusLine().getStatusCode());
    128         } finally {
    129             httpClient.close();
    130         }
    131         Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
    132         assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
    133     }
    134 
    135     private void assertAndroidHttpClientCleartextRequestBlocked() throws Exception {
    136         URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
    137         AndroidHttpClient httpClient = AndroidHttpClient.newInstance(null);
    138         try {
    139             HttpResponse response = httpClient.execute(new HttpGet(url.toString()));
    140             fail();
    141         } catch (IOException e) {
    142             if ((e.getMessage() == null) || (!e.getMessage().toLowerCase().contains("cleartext"))) {
    143                 fail("Exception with which request failed does not mention cleartext: " + e);
    144             }
    145         } finally {
    146             httpClient.close();
    147         }
    148         Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
    149         assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
    150     }
    151 
    152     public void testMediaPlayer() throws Exception {
    153         if (mCleartextTrafficExpectedToBePermitted) {
    154             assertMediaPlayerCleartextRequestSucceeds();
    155         } else {
    156             assertMediaPlayerCleartextRequestBlocked();
    157         }
    158     }
    159 
    160     private void assertMediaPlayerCleartextRequestSucceeds() throws Exception {
    161         MediaPlayer mediaPlayer = new MediaPlayer();
    162         Uri uri = Uri.parse(mHttpOnlyWebServer.getUserAgentUrl());
    163         mediaPlayer.setDataSource(getContext(), uri);
    164 
    165         try {
    166             mediaPlayer.prepare();
    167         } catch (IOException expected) {
    168         } finally {
    169             try {
    170                 mediaPlayer.stop();
    171             } catch (IllegalStateException ignored) {
    172             }
    173         }
    174         uri = uri.buildUpon().scheme(null).authority(null).build();
    175         assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
    176     }
    177 
    178     private void assertMediaPlayerCleartextRequestBlocked() throws Exception {
    179         MediaPlayer mediaPlayer = new MediaPlayer();
    180         Uri uri = Uri.parse(mHttpOnlyWebServer.getUserAgentUrl());
    181         mediaPlayer.setDataSource(getContext(), uri);
    182 
    183         try {
    184             mediaPlayer.prepare();
    185         } catch (IOException expected) {
    186         } finally {
    187             try {
    188                 mediaPlayer.stop();
    189             } catch (IllegalStateException ignored) {
    190             }
    191         }
    192         uri = uri.buildUpon().scheme(null).authority(null).build();
    193         assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
    194     }
    195 
    196     public void testDownloadManager() throws Exception {
    197         Uri uri = Uri.parse(mHttpOnlyWebServer.getTestDownloadUrl("netsecpolicy", 0));
    198         int[] result = downloadUsingDownloadManager(uri);
    199         int status = result[0];
    200         int reason = result[1];
    201         uri = uri.buildUpon().scheme(null).authority(null).build();
    202         if (mCleartextTrafficExpectedToBePermitted) {
    203             assertEquals(DownloadManager.STATUS_SUCCESSFUL, status);
    204             assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
    205         } else {
    206             assertEquals(DownloadManager.STATUS_FAILED, status);
    207             assertEquals(400, reason);
    208             assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
    209         }
    210     }
    211 
    212 
    213     private int[] downloadUsingDownloadManager(Uri uri) throws Exception {
    214         DownloadManager downloadManager =
    215                 (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
    216         removeAllDownloads(downloadManager);
    217         BroadcastReceiver downloadCompleteReceiver = null;
    218         try {
    219             final SettableFuture<Intent> downloadCompleteIntentFuture = new SettableFuture<Intent>();
    220             downloadCompleteReceiver = new BroadcastReceiver() {
    221                 @Override
    222                 public void onReceive(Context context, Intent intent) {
    223                     downloadCompleteIntentFuture.set(intent);
    224                 }
    225             };
    226             getContext().registerReceiver(
    227                     downloadCompleteReceiver,
    228                     new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    229 
    230             Intent downloadCompleteIntent;
    231 
    232             long downloadId = downloadManager.enqueue(new DownloadManager.Request(uri));
    233             downloadCompleteIntent = downloadCompleteIntentFuture.get(5, TimeUnit.SECONDS);
    234 
    235             assertEquals(downloadId,
    236                     downloadCompleteIntent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
    237             Cursor c = downloadManager.query(
    238                     new DownloadManager.Query().setFilterById(downloadId));
    239             try {
    240                 if (!c.moveToNext()) {
    241                     fail("Download not found");
    242                     return null;
    243                 }
    244                 int status = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
    245                 int reason = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON));
    246                 return new int[] {status, reason};
    247             } finally {
    248                 c.close();
    249             }
    250         } finally {
    251             if (downloadCompleteReceiver != null) {
    252                 getContext().unregisterReceiver(downloadCompleteReceiver);
    253             }
    254             removeAllDownloads(downloadManager);
    255         }
    256     }
    257 
    258     private static void removeAllDownloads(DownloadManager downloadManager) {
    259         Cursor cursor = null;
    260         try {
    261             DownloadManager.Query query = new DownloadManager.Query();
    262             cursor = downloadManager.query(query);
    263             if (cursor.getCount() == 0) {
    264                 return;
    265             }
    266             long[] removeIds = new long[cursor.getCount()];
    267             int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
    268             for (int i = 0; cursor.moveToNext(); i++) {
    269                 removeIds[i] = cursor.getLong(columnIndex);
    270             }
    271             assertEquals(removeIds.length, downloadManager.remove(removeIds));
    272         } finally {
    273             if (cursor != null) {
    274                 cursor.close();
    275             }
    276         }
    277     }
    278 
    279     private static class SettableFuture<T> implements Future<T> {
    280 
    281         private final Object mLock = new Object();
    282         private boolean mDone;
    283         private boolean mCancelled;
    284         private T mValue;
    285         private Throwable mException;
    286 
    287         public void set(T value) {
    288             synchronized (mLock) {
    289                 if (!mDone) {
    290                     mValue = value;
    291                     mDone = true;
    292                     mLock.notifyAll();
    293                 }
    294             }
    295         }
    296 
    297         public void setException(Throwable exception) {
    298             synchronized (mLock) {
    299                 if (!mDone) {
    300                     mException = exception;
    301                     mDone = true;
    302                     mLock.notifyAll();
    303                 }
    304             }
    305         }
    306 
    307         @Override
    308         public boolean cancel(boolean mayInterruptIfRunning) {
    309             synchronized (mLock) {
    310                 if (mDone) {
    311                     return false;
    312                 }
    313                 mCancelled = true;
    314                 mDone = true;
    315                 mLock.notifyAll();
    316                 return true;
    317             }
    318         }
    319 
    320         @Override
    321         public T get() throws InterruptedException, ExecutionException {
    322             synchronized (mLock) {
    323                 while (!mDone) {
    324                     mLock.wait();
    325                 }
    326                 return getValue();
    327             }
    328         }
    329 
    330         @Override
    331         public T get(long timeout, TimeUnit timeUnit)
    332                 throws InterruptedException, ExecutionException, TimeoutException {
    333             synchronized (mLock) {
    334                 if (mDone) {
    335                     return getValue();
    336                 }
    337                 long timeoutMillis = timeUnit.toMillis(timeout);
    338                 long deadlineTimeMillis = System.currentTimeMillis() + timeoutMillis;
    339 
    340                 while (!mDone) {
    341                     long millisTillDeadline = deadlineTimeMillis - System.currentTimeMillis();
    342                     if ((millisTillDeadline <= 0) || (millisTillDeadline > timeoutMillis)) {
    343                         throw new TimeoutException();
    344                     }
    345                     mLock.wait(millisTillDeadline);
    346                 }
    347                 return getValue();
    348             }
    349         }
    350 
    351         private T getValue() throws ExecutionException {
    352             synchronized (mLock) {
    353                 if (!mDone) {
    354                     throw new IllegalStateException("Not yet done");
    355                 }
    356                 if (mCancelled) {
    357                     throw new CancellationException();
    358                 }
    359                 if (mException != null) {
    360                     throw new ExecutionException(mException);
    361                 }
    362                 return mValue;
    363             }
    364         }
    365 
    366         @Override
    367         public boolean isCancelled() {
    368             synchronized (mLock) {
    369                 return mCancelled;
    370             }
    371         }
    372 
    373         @Override
    374         public boolean isDone() {
    375             synchronized (mLock) {
    376                 return mDone;
    377             }
    378         }
    379     }
    380 }
    381