Add NetworkUsageStatsTest to CTS suite

Bug: 19572685

Change-Id: Ie390464ca27ace87f625ec7c5e884c5b0b333fa2
diff --git a/tests/tests/app.usage/AndroidManifest.xml b/tests/tests/app.usage/AndroidManifest.xml
index 11065d4..3bd795a 100644
--- a/tests/tests/app.usage/AndroidManifest.xml
+++ b/tests/tests/app.usage/AndroidManifest.xml
@@ -21,6 +21,9 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.SET_TIME" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
new file mode 100644
index 0000000..c3ba834
--- /dev/null
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -0,0 +1,339 @@
+/**
+ * Copyright (C) 2015 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 android.app.usage.cts;
+
+import android.app.AppOpsManager;
+import android.app.usage.NetworkStatsManager;
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import dalvik.system.SocketTagger;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.net.URL;
+import java.text.MessageFormat;
+import javax.net.ssl.HttpsURLConnection;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+public class NetworkUsageStatsTest extends InstrumentationTestCase {
+    private static final String LOG_TAG = "NetworkUsageStatsTest";
+    private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} " +
+            AppOpsManager.OPSTR_GET_USAGE_STATS + " {1}";
+
+    private static final long MINUTE = 1000 * 60;
+
+    private static final int[] sNetworkTypesToTest = new int[] {
+        ConnectivityManager.TYPE_WIFI,
+        ConnectivityManager.TYPE_MOBILE,
+    };
+
+    // Order corresponds to sNetworkTypesToTest
+    private static final int[] sTransportTypesToTest = new int[] {
+        NetworkCapabilities.TRANSPORT_WIFI,
+        NetworkCapabilities.TRANSPORT_CELLULAR,
+    };
+
+    private NetworkStatsManager mNsm;
+    private PackageManager mPm;
+    private ConnectivityManager mCm;
+    private long mStartTime;
+    private long mEndTime;
+
+    private long mBytesRead;
+
+    private void exerciseRemoteHost(int transportType) throws Exception {
+        final int timeout = 60000;
+        mCm.requestNetwork(new NetworkRequest.Builder()
+            .addTransportType(transportType)
+            .build(), new ConnectivityManager.NetworkCallback() {
+                @Override
+                public void onAvailable(Network network) {
+                    NetworkInfo networkInfo = mCm.getNetworkInfo(network);
+                    if (networkInfo == null) {
+                        Log.w(LOG_TAG, "Network info is null");
+                    } else {
+                        Log.w(LOG_TAG, "Network: " + networkInfo.toString());
+                    }
+                    InputStreamReader in = null;
+                    HttpsURLConnection urlc = null;
+                    String originalKeepAlive = System.getProperty("http.keepAlive");
+                    System.setProperty("http.keepAlive", "false");
+                    try {
+                        urlc = (HttpsURLConnection) network.openConnection(new URL(
+                                "https://www.google.com"));
+                        urlc.setConnectTimeout(timeout);
+                        urlc.setUseCaches(false);
+                        urlc.connect();
+                        boolean ping = urlc.getResponseCode() == 200;
+                        if (ping) {
+                            in = new InputStreamReader(
+                                    (InputStream) urlc.getContent());
+
+                            mBytesRead = 0;
+                            while (in.read() != -1) ++mBytesRead;
+                        }
+                    } catch (Exception e) {
+                        Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
+                    } finally {
+                        if (in != null) {
+                            try {
+                                in.close();
+                            } catch (IOException e) {
+                                // don't care
+                            }
+                        }
+                        if (urlc != null) {
+                            urlc.disconnect();
+                        }
+                        if (originalKeepAlive == null) {
+                            System.clearProperty("http.keepAlive");
+                        } else {
+                            System.setProperty("http.keepAlive", originalKeepAlive);
+                        }
+                    }
+                }
+            });
+        try {
+            Thread.sleep(timeout);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mNsm = (NetworkStatsManager) getInstrumentation().getContext()
+                .getSystemService(Context.NETWORK_STATS_SERVICE);
+
+        mPm = getInstrumentation().getContext().getPackageManager();
+        mCm = (ConnectivityManager) getInstrumentation().getContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private void setAppOpsMode(String mode) throws Exception {
+        final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND,
+                getInstrumentation().getContext().getPackageName(), mode);
+        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+                .executeShellCommand(command);
+        try {
+            Streams.readFully(new FileInputStream(pfd.getFileDescriptor()));
+        } finally {
+            IoUtils.closeQuietly(pfd.getFileDescriptor());
+        }
+    }
+
+    private boolean shouldTestThisNetworkType(int networkTypeIndex) throws Exception {
+        NetworkInfo networkInfo = mCm.getNetworkInfo(sNetworkTypesToTest[networkTypeIndex]);
+        if (networkInfo == null || !networkInfo.isAvailable()) {
+            return false;
+        }
+        mStartTime = System.currentTimeMillis() - MINUTE/2;
+        exerciseRemoteHost(sTransportTypesToTest[networkTypeIndex]);
+        mEndTime = System.currentTimeMillis() + MINUTE/2;
+        return true;
+    }
+
+    public void testDeviceSummary() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats.Bucket bucket = null;
+            try {
+                bucket = mNsm.querySummaryForDevice(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime);
+            } catch (RemoteException | SecurityException e) {
+                fail("testDeviceSummary fails with exception: " + e.toString());
+            }
+            assertTrue(bucket != null);
+            setAppOpsMode("deny");
+            try {
+                bucket = mNsm.querySummaryForDevice(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime);
+                fail("negative testDeviceSummary fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testDeviceSummary fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testUserSummary() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats.Bucket bucket = null;
+            try {
+                bucket = mNsm.querySummaryForUser(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime);
+            } catch (RemoteException | SecurityException e) {
+                fail("testUserSummary fails with exception: " + e.toString());
+            }
+            assertTrue(bucket != null);
+            setAppOpsMode("deny");
+            try {
+                bucket = mNsm.querySummaryForUser(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime);
+                fail("negative testUserSummary fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testUserSummary fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testAppSummary() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats result = null;
+            try {
+                result = mNsm.querySummary(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime);
+                assertTrue(result != null);
+                NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+                long totalTxPackets = 0;
+                long totalRxPackets = 0;
+                long totalTxBytes = 0;
+                long totalRxBytes = 0;
+                while (result.getNextBucket(bucket)) {
+                    if (bucket.getUid() == Process.myUid()) {
+                        totalTxPackets += bucket.getTxPackets();
+                        totalRxPackets += bucket.getRxPackets();
+                        totalTxBytes += bucket.getTxBytes();
+                        totalRxBytes += bucket.getRxBytes();
+                    }
+                }
+                assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0);
+                assertTrue("No Rx packets usage for uid " + Process.myUid(), totalRxPackets > 0);
+                assertTrue("No Tx bytes usage for uid " + Process.myUid(), totalTxBytes > 0);
+                assertTrue("No Tx packets usage for uid " + Process.myUid(), totalTxPackets > 0);
+            } catch (RemoteException | SecurityException e) {
+                fail("testAppSummary fails with exception: " + e.toString());
+            } finally {
+                if (result != null) {
+                    result.close();
+                }
+            }
+            setAppOpsMode("deny");
+            try {
+                result = mNsm.querySummary(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime);
+                fail("negative testAppSummary fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testAppSummary fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testAppDetails() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats result = null;
+            try {
+                result = mNsm.queryDetails(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime);
+                assertTrue(result != null);
+            } catch (RemoteException | SecurityException e) {
+                fail("testAppDetails fails with exception: " + e.toString());
+            } finally {
+                if (result != null) {
+                    result.close();
+                }
+            }
+            setAppOpsMode("deny");
+            try {
+                result = mNsm.queryDetails(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime);
+                fail("negative testAppDetails fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testAppDetails fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testUidDetails() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats result = null;
+            try {
+                result = mNsm.queryDetailsForUid(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime, Process.myUid());
+                assertTrue(result != null);
+            } catch (RemoteException | SecurityException e) {
+                fail("testUidDetails fails with exception: " + e.toString());
+            } finally {
+                if (result != null) {
+                    result.close();
+                }
+            }
+            setAppOpsMode("deny");
+            try {
+                result = mNsm.queryDetailsForUid(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime, Process.myUid());
+                fail("negative testUidDetails fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testUidDetails fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+}