Merge "Test for improper kernel fusion." into klp-dev
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
index 8a44dfa..40d3cff 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
@@ -16,21 +16,24 @@
 
 package com.android.cts.appaccessdata;
 
+import java.io.BufferedReader;
+import java.io.DataInputStream;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileReader;
 import java.io.IOException;
 
 import android.test.AndroidTestCase;
 
 /**
- * Test that another app's private data cannot be accessed.
+ * Test that another app's private data cannot be accessed, while its public data can.
  *
- * Assumes that {@link APP_WITH_DATA_PKG} has already created the private data.
+ * Assumes that {@link APP_WITH_DATA_PKG} has already created the private and public data.
  */
 public class AccessPrivateDataTest extends AndroidTestCase {
 
     /**
-     * The Android package name of the application that owns the private data
+     * The Android package name of the application that owns the data
      */
     private static final String APP_WITH_DATA_PKG = "com.android.cts.appwithdata";
 
@@ -39,9 +42,15 @@
      * {@link APP_WITH_DATA_PKG}.
      */
     private static final String PRIVATE_FILE_NAME = "private_file.txt";
+    /**
+     * Name of public file to access. This must match the name of the file created by
+     * {@link APP_WITH_DATA_PKG}.
+     */
+    private static final String PUBLIC_FILE_NAME = "public_file.txt";
 
     /**
-     * Tests that another app's private file cannot be accessed
+     * Tests that another app's private data cannot be accessed. It includes file
+     * and detailed traffic stats.
      * @throws IOException
      */
     public void testAccessPrivateData() throws IOException {
@@ -58,5 +67,60 @@
         } catch (SecurityException e) {
             // also valid
         }
+        accessPrivateTrafficStats();
+    }
+
+    /**
+     * Tests that another app's public file can be accessed
+     * @throws IOException
+     */
+    public void testAccessPublicData() throws IOException {
+        try {
+            getOtherAppUid();
+        } catch (FileNotFoundException e) {
+            fail("Was not able to access another app's public file: " + e);
+        } catch (SecurityException e) {
+            fail("Was not able to access another app's public file: " + e);
+        }
+    }
+
+    private int getOtherAppUid() throws IOException, FileNotFoundException, SecurityException {
+        // construct the absolute file path to the other app's public file
+        String publicFilePath = String.format("/data/data/%s/files/%s", APP_WITH_DATA_PKG,
+                PUBLIC_FILE_NAME);
+        DataInputStream inputStream = new DataInputStream(new FileInputStream(publicFilePath));
+        int otherAppUid = (int)inputStream.readInt();
+        inputStream.close();
+        return otherAppUid;
+    }
+
+    private void accessPrivateTrafficStats() throws IOException {
+        int otherAppUid = -1;
+        try {
+            otherAppUid = getOtherAppUid();
+        } catch (FileNotFoundException e) {
+            fail("Was not able to access another app's public file: " + e);
+        } catch (SecurityException e) {
+            fail("Was not able to access another app's public file: " + e);
+        }
+
+        boolean foundOtherStats = false;
+        try {
+            BufferedReader qtaguidReader = new BufferedReader(new FileReader("/proc/net/xt_qtaguid/stats"));
+            String line;
+            while ((line = qtaguidReader.readLine()) != null) {
+                String tokens[] = line.split(" ");
+                if (tokens.length > 3 && tokens[3].equals(String.valueOf(otherAppUid))) {
+                    foundOtherStats = true;
+                    if (!tokens[2].equals("0x0")) {
+                        fail("Other apps detailed traffic stats leaked");
+                    }
+                }
+            }
+            qtaguidReader.close();
+        } catch (FileNotFoundException e) {
+            fail("Was not able to access qtaguid/stats: " + e);
+        }
+        assertTrue("Was expecting to find other apps' traffic stats", foundOtherStats);
     }
 }
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
index 4b10030..9decbcd 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
@@ -22,6 +22,7 @@
     access.
     -->
 
+    <uses-permission android:name="android.permission.INTERNET" />
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/CreatePrivateDataTest.java b/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/CreatePrivateDataTest.java
index 1de6464..e11681a 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/CreatePrivateDataTest.java
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/CreatePrivateDataTest.java
@@ -22,10 +22,23 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
+import android.net.TrafficStats;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 
 /**
  * Test that will create private app data.
@@ -36,9 +49,15 @@
 public class CreatePrivateDataTest extends AndroidTestCase {
 
     /**
+     * The Android package name of the application that owns the private data
+     */
+    private static final String APP_WITH_DATA_PKG = "com.android.cts.appwithdata";
+
+    /**
      * Name of private file to create.
      */
     private static final String PRIVATE_FILE_NAME = "private_file.txt";
+    private static final String PUBLIC_FILE_NAME = "public_file.txt";
 
     private static final String PREFERENCES_FILE_NAME = "preferences";
     private static final String PREFERENCE_KEY = "preference_key";
@@ -49,7 +68,8 @@
     static final String DB_VALUE = "test_value";
 
     /**
-     * Creates a file private to this app
+     * Creates the private data for this app, which includes
+     * file, database entries, and traffic stats.
      * @throws IOException if any error occurred when creating the file
      */
     public void testCreatePrivateData() throws IOException {
@@ -59,8 +79,34 @@
         outputStream.close();
         assertTrue(getContext().getFileStreamPath(PRIVATE_FILE_NAME).exists());
 
+        outputStream = getContext().openFileOutput(PUBLIC_FILE_NAME,
+                Context.MODE_WORLD_READABLE);
+        DataOutputStream dataOut = new DataOutputStream(outputStream);
+        dataOut.writeInt(getContext().getApplicationInfo().uid);
+        dataOut.close();
+        outputStream.close();
+        // Ensure that some file will be accessible via the same path that will be used by other app.
+        accessPublicData();
+
         writeToPreferences();
         writeToDatabase();
+        createTrafficStatsWithTags();
+    }
+
+    private void accessPublicData() throws IOException {
+        try {
+            // construct the absolute file path to the app's public's file the same
+            // way as the appaccessdata package will.
+            String publicFilePath = String.format("/data/data/%s/files/%s", APP_WITH_DATA_PKG,
+                    PUBLIC_FILE_NAME);
+            DataInputStream inputStream = new DataInputStream(new FileInputStream(publicFilePath));
+            int otherAppUid = (int)inputStream.readInt();
+            inputStream.close();
+        } catch (FileNotFoundException e) {
+            fail("Was not able to access own public file: " + e);
+        } catch (SecurityException e) {
+            fail("Was not able to access own public file: " + e);
+        }
     }
 
     private void writeToPreferences() {
@@ -127,6 +173,76 @@
         }
     }
 
+    private void accessOwnTrafficStats() throws IOException {
+        final int ownAppUid = getContext().getApplicationInfo().uid;
+
+        boolean foundOwnDetailedStats = false;
+        try {
+            BufferedReader qtaguidReader = new BufferedReader(new FileReader("/proc/net/xt_qtaguid/stats"));
+            String line;
+            while ((line = qtaguidReader.readLine()) != null) {
+                String tokens[] = line.split(" ");
+                if (tokens.length > 3 && tokens[3].equals(String.valueOf(ownAppUid))) {
+                    if (!tokens[2].equals("0x0")) {
+                      foundOwnDetailedStats = true;
+                    }
+                }
+            }
+            qtaguidReader.close();
+        } catch (FileNotFoundException e) {
+            fail("Was not able to access qtaguid/stats: " + e);
+        }
+        assertTrue("Was expecting to find own traffic stats", foundOwnDetailedStats);
+    }
+
+    private void createTrafficStatsWithTags() throws IOException {
+
+        // Transfer 1MB of data across an explicitly localhost socket.
+        final int byteCount = 1024;
+        final int packetCount = 1024;
+
+        final ServerSocket server = new ServerSocket(0);
+        new Thread("CreatePrivateDataTest.createTrafficStatsWithTags") {
+            @Override
+            public void run() {
+                try {
+                    Socket socket = new Socket("localhost", server.getLocalPort());
+                    // Make sure that each write()+flush() turns into a packet:
+                    // disable Nagle.
+                    socket.setTcpNoDelay(true);
+                    OutputStream out = socket.getOutputStream();
+                    byte[] buf = new byte[byteCount];
+                    for (int i = 0; i < packetCount; i++) {
+                        TrafficStats.setThreadStatsTag(i % 10);
+                        TrafficStats.tagSocket(socket);
+                        out.write(buf);
+                        out.flush();
+                    }
+                    out.close();
+                    socket.close();
+                } catch (IOException e) {
+                  assertTrue("io exception" + e, false);
+                }
+            }
+        }.start();
+
+        try {
+            Socket socket = server.accept();
+            InputStream in = socket.getInputStream();
+            byte[] buf = new byte[byteCount];
+            int read = 0;
+            while (read < byteCount * packetCount) {
+                int n = in.read(buf);
+                assertTrue("Unexpected EOF", n > 0);
+                read += n;
+            }
+        } finally {
+            server.close();
+        }
+
+        accessOwnTrafficStats();
+    }
+
     static class TestDatabaseOpenHelper extends SQLiteOpenHelper {
 
         static final String _ID = "_id";
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
index d51fc72..c981db3 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
@@ -186,6 +186,36 @@
         }
     }
 
+    public void testDecodeRegionInputStreamInBitmap() throws IOException {
+        Options opts = new BitmapFactory.Options();
+        for (int i = 0; i < NUM_TEST_IMAGES; ++i) {
+            for (int j = 0; j < SAMPLESIZES.length; ++j) {
+                for (int k = 0; k < COLOR_CONFIGS.length; ++k) {
+                    opts.inSampleSize = SAMPLESIZES[j];
+                    opts.inPreferredConfig = COLOR_CONFIGS[k];
+                    opts.inBitmap = null;
+
+                    InputStream is1 = obtainInputStream(RES_IDS[i]);
+                    BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is1, false);
+                    InputStream is2 = obtainInputStream(RES_IDS[i]);
+                    Bitmap wholeImage = BitmapFactory.decodeStream(is2, null, opts);
+
+                    // setting inBitmap enables several checks within compareRegionByRegion
+                    opts.inBitmap = Bitmap.createBitmap(
+                            wholeImage.getWidth(), wholeImage.getHeight(), opts.inPreferredConfig);
+
+                    if (RES_IDS[i] == R.drawable.webp_test && COLOR_CONFIGS[k] == Config.RGB_565) {
+                        compareRegionByRegion(decoder, opts, mMseMarginWebPConfigRgb565,
+                                              wholeImage);
+                    } else {
+                        compareRegionByRegion(decoder, opts, mMseMargin, wholeImage);
+                    }
+                    wholeImage.recycle();
+                }
+            }
+        }
+    }
+
     public void testDecodeRegionByteArray() throws IOException {
         Options opts = new BitmapFactory.Options();
         for (int i = 0; i < NUM_TEST_IMAGES; ++i) {
@@ -292,9 +322,18 @@
                 actual = decoder.decodeRegion(rect1, opts);
                 int left = rect1.left / opts.inSampleSize;
                 int top = rect1.top / opts.inSampleSize;
-                Rect rect2 = new Rect(left, top, left + actual.getWidth(),
+                if (opts.inBitmap != null) {
+                    // bitmap reuse path - ensure reuse worked
+                    assertSame(opts.inBitmap, actual);
+                    int currentWidth = rect1.width() / opts.inSampleSize;
+                    int currentHeight = rect1.height() / opts.inSampleSize;
+                    Rect actualRect = new Rect(0, 0, currentWidth, currentHeight);
+                    // crop 'actual' to the size to be tested (and avoid recycling inBitmap)
+                    actual = cropBitmap(actual, actualRect);
+                }
+                Rect expectedRect = new Rect(left, top, left + actual.getWidth(),
                         top + actual.getHeight());
-                expected = cropBitmap(wholeImage, rect2);
+                expected = cropBitmap(wholeImage, expectedRect);
                 compareBitmaps(expected, actual, mseMargin, true);
                 actual.recycle();
                 expected.recycle();
diff --git a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
index 42ae7a7..9cad011 100644
--- a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
@@ -38,6 +38,11 @@
     private static final String TAG = "MediaMuxerTest";
     private static final boolean VERBOSE = false;
     private static final int MAX_SAMPLE_SIZE = 256 * 1024;
+    private static final float LATITUDE = 0.0000f;
+    private static final float LONGITUDE  = -180.0f;
+    private static final float BAD_LATITUDE = 91.0f;
+    private static final float BAD_LONGITUDE = -181.0f;
+    private static final float TOLERANCE = 0.0002f;
     private Resources mResources;
 
     @Override
@@ -189,6 +194,25 @@
         if (degrees >= 0) {
             muxer.setOrientationHint(degrees);
         }
+
+        // Test setLocation out of bound cases
+        try {
+            muxer.setLocation(BAD_LATITUDE, LONGITUDE);
+            fail("setLocation succeeded with bad argument: [" + BAD_LATITUDE + "," + LONGITUDE
+                    + "]");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+        try {
+            muxer.setLocation(LATITUDE, BAD_LONGITUDE);
+            fail("setLocation succeeded with bad argument: [" + LATITUDE + "," + BAD_LONGITUDE
+                    + "]");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+
+        muxer.setLocation(LATITUDE, LONGITUDE);
+
         muxer.start();
         while (!sawEOS) {
             bufferInfo.offset = offset;
@@ -235,6 +259,7 @@
         try {
             cloneMediaUsingMuxer(srcMedia, outputMediaFile, expectedTrackCount, degrees);
             verifyAttributesMatch(srcMedia, outputMediaFile, degrees);
+            verifyLocationInFile(outputMediaFile);
             // Check the sample on 1s and 0.5s.
             verifySamplesMatch(srcMedia, outputMediaFile, 1000000);
             verifySamplesMatch(srcMedia, outputMediaFile, 500000);
@@ -337,5 +362,32 @@
             fail("byteBuffer didn't match");
         }
     }
+
+    private void verifyLocationInFile(String fileName) {
+        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+        retriever.setDataSource(fileName);
+        String location = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
+        assertNotNull("No location information found in file " + fileName, location);
+
+        // parsing String location and recover the location inforamtion in floats
+        // Make sure the tolerance is very small - due to rounding errors.
+
+        // Get the position of the -/+ sign in location String, which indicates
+        // the beginning of the longtitude.
+        int index = location.lastIndexOf('-');
+        if (index == -1) {
+            index = location.lastIndexOf('+');
+        }
+        assertTrue("+ or - is not found", index != -1);
+        assertTrue("+ or - is only found at the beginning", index != 0);
+        float latitude = Float.parseFloat(location.substring(0, index - 1));
+        float longitude = Float.parseFloat(location.substring(index));
+        assertTrue("Incorrect latitude: " + latitude,
+                Math.abs(latitude - LATITUDE) <= TOLERANCE);
+        assertTrue("Incorrect longitude: " + longitude,
+                Math.abs(longitude - LONGITUDE) <= TOLERANCE);
+        retriever.release();
+    }
+
 }
 
diff --git a/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java b/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
index ce3a9c4..dfaabb8 100644
--- a/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/RingtoneManagerTest.java
@@ -83,11 +83,6 @@
         Cursor c = mRingtoneManager.getCursor();
         assertTrue("Must have at least one ring tone available", c.getCount() > 0);
 
-        mRingtoneManager.setIncludeDrm(true);
-        assertTrue(mRingtoneManager.getIncludeDrm());
-        mRingtoneManager.setIncludeDrm(false);
-        assertFalse(mRingtoneManager.getIncludeDrm());
-
         assertNotNull(mRingtoneManager.getRingtone(0));
         assertNotNull(RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_RINGTONE_URI));
         int expectedPosition = 0;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index 3353d50..efd3aef 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -192,11 +192,19 @@
     public void testOnScaleChanged() throws Throwable {
         final MockWebViewClient webViewClient = new MockWebViewClient();
         mOnUiThread.setWebViewClient(webViewClient);
+        mWebServer = new CtsTestServer(getActivity());
 
         assertFalse(webViewClient.hasOnScaleChangedCalled());
+        String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url1);
+
         mOnUiThread.zoomIn();
-        getInstrumentation().waitForIdleSync();
-        assertTrue(webViewClient.hasOnScaleChangedCalled());
+        new PollingCheck(TEST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return webViewClient.hasOnScaleChangedCalled();
+            }
+        }.run();
     }
 
     private void requireLoadedPage() throws Throwable {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 1df71e4..827bf27 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -854,6 +854,10 @@
         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
                 HTML_HEADER + "<title>Hello World%21</title></html>", "text/html", "UTF-8", null);
         assertEquals("Hello World!", mOnUiThread.getTitle());
+
+        // Check the method is null input safe.
+        mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(null, null, null, null, null);
+        assertEquals("about:blank", mOnUiThread.getUrl());
     }
 
     private static class WaitForFindResultsListener extends FutureTask<Integer>
@@ -1108,27 +1112,18 @@
         }.run();
         getInstrumentation().waitForIdleSync();
 
-        int previousScrollX = mOnUiThread.getScrollX();
-        int previousScrollY = mOnUiThread.getScrollY();
+        final int previousScrollX = mOnUiThread.getScrollX();
+        final int previousScrollY = mOnUiThread.getScrollY();
 
         mOnUiThread.flingScroll(100, 100);
 
-        int timeSlice = 500;
-        Thread.sleep(timeSlice);
-        assertTrue(mOnUiThread.getScrollX() > previousScrollX);
-        assertTrue(mOnUiThread.getScrollY() > previousScrollY);
-
-        previousScrollY = mOnUiThread.getScrollY();
-        previousScrollX = mOnUiThread.getScrollX();
-        Thread.sleep(timeSlice);
-        assertTrue(mOnUiThread.getScrollX() >= previousScrollX);
-        assertTrue(mOnUiThread.getScrollY() >= previousScrollY);
-
-        previousScrollY = mOnUiThread.getScrollY();
-        previousScrollX = mOnUiThread.getScrollX();
-        Thread.sleep(timeSlice);
-        assertTrue(mOnUiThread.getScrollX() >= previousScrollX);
-        assertTrue(mOnUiThread.getScrollY() >= previousScrollY);
+        new PollingCheck() {
+            @Override
+            protected boolean check() {
+                return mOnUiThread.getScrollX() > previousScrollX &&
+                        mOnUiThread.getScrollY() > previousScrollY;
+            }
+        }.run();
     }
 
     public void testRequestFocusNodeHref() throws Throwable {
@@ -1487,12 +1482,19 @@
 
         final MockWebViewClient webViewClient = new MockWebViewClient();
         mOnUiThread.setWebViewClient(webViewClient);
-        getInstrumentation().waitForIdleSync();
+        startWebServer(false);
+
         assertFalse(webViewClient.onScaleChangedCalled());
+        String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url1);
 
         mOnUiThread.zoomIn();
-        getInstrumentation().waitForIdleSync();
-        assertTrue(webViewClient.onScaleChangedCalled());
+        new PollingCheck() {
+            @Override
+            protected boolean check() {
+                return webViewClient.onScaleChangedCalled();
+            }
+        }.run();
     }
 
     @UiThreadTest