Merge remote branch 'goog/gingerbread' into manualmerge

Conflicts:
	tests/tests/os/src/android/os/cts/BuildVersionTest.java

Change-Id: I831f4056d5d1332e90bae1ce69ecaddb5ece728e
diff --git a/tests/jni/Android.mk b/tests/jni/Android.mk
index d58416f..de1d373 100644
--- a/tests/jni/Android.mk
+++ b/tests/jni/Android.mk
@@ -25,7 +25,8 @@
 LOCAL_PRELINK_MODULE := false
 
 LOCAL_SRC_FILES := \
-		CtsJniOnLoad.cpp
+		CtsJniOnLoad.cpp \
+		android_os_cts_FileUtils.cpp
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) 
 
diff --git a/tests/jni/CtsJniOnLoad.cpp b/tests/jni/CtsJniOnLoad.cpp
index b6177a1..d6a1d2b 100644
--- a/tests/jni/CtsJniOnLoad.cpp
+++ b/tests/jni/CtsJniOnLoad.cpp
@@ -21,6 +21,8 @@
 extern int register_android_os_cts_CpuFeatures(JNIEnv*);
 #endif
 
+extern int register_android_os_cts_FileUtils(JNIEnv*);
+
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     JNIEnv *env = NULL;
 
@@ -34,5 +36,9 @@
     }
 #endif
 
+    if (register_android_os_cts_FileUtils(env)) {
+        return JNI_ERR;
+    }
+
     return JNI_VERSION_1_4;
 }
diff --git a/tests/jni/android_os_cts_FileUtils.cpp b/tests/jni/android_os_cts_FileUtils.cpp
new file mode 100644
index 0000000..d78d26c
--- /dev/null
+++ b/tests/jni/android_os_cts_FileUtils.cpp
@@ -0,0 +1,142 @@
+/* 
+ * Copyright (C) 2011 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.
+ */
+
+#include <grp.h>
+#include <jni.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+static jclass gFileStatusClass;
+static jfieldID gFileStatusDevFieldID;
+static jfieldID gFileStatusInoFieldID;
+static jfieldID gFileStatusModeFieldID;
+static jfieldID gFileStatusNlinkFieldID;
+static jfieldID gFileStatusUidFieldID;
+static jfieldID gFileStatusGidFieldID;
+static jfieldID gFileStatusSizeFieldID;
+static jfieldID gFileStatusBlksizeFieldID;
+static jfieldID gFileStatusBlocksFieldID;
+static jfieldID gFileStatusAtimeFieldID;
+static jfieldID gFileStatusMtimeFieldID;
+static jfieldID gFileStatusCtimeFieldID;
+
+/*
+ * Native methods used by
+ * cts/tests/src/android/os/cts/FileUtils.java
+ *
+ * Copied from hidden API: frameworks/base/core/jni/android_os_FileUtils.cpp
+ */
+
+jboolean android_os_cts_FileUtils_getFileStatus(JNIEnv* env, jobject thiz,
+        jstring path, jobject fileStatus, jboolean statLinks)
+{
+    const char* pathStr = env->GetStringUTFChars(path, NULL);
+    jboolean ret = false;
+    struct stat s;
+
+    int res = statLinks == true ? lstat(pathStr, &s) : stat(pathStr, &s);
+
+    if (res == 0) {
+        ret = true;
+        if (fileStatus != NULL) {
+            env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev);
+            env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino);
+            env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode);
+            env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink);
+            env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid);
+            env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid);
+            env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size);
+            env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize);
+            env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks);
+            env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime);
+            env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime);
+            env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
+        }
+    }
+
+    env->ReleaseStringUTFChars(path, pathStr);
+
+    return ret;
+}
+
+jstring android_os_cts_FileUtils_getUserName(JNIEnv* env, jobject thiz,
+        jint uid)
+{
+    struct passwd *pwd = getpwuid(uid);
+    return env->NewStringUTF(pwd->pw_name);
+}
+
+jstring android_os_cts_FileUtils_getGroupName(JNIEnv* env, jobject thiz,
+        jint gid)
+{
+    struct group *grp = getgrgid(gid);
+    return env->NewStringUTF(grp->gr_name);
+}
+
+jint android_os_cts_FileUtils_setPermissions(JNIEnv* env, jobject clazz,
+        jstring file, jint mode)
+{
+    const char *fileStr = env->GetStringUTFChars(file, NULL);
+    if (fileStr == NULL) {
+        return -1;
+    }
+
+    if (strlen(fileStr) <= 0) {
+        env->ReleaseStringUTFChars(file, fileStr);
+        return ENOENT;
+    } 
+
+    jint returnValue = chmod(fileStr, mode) == 0 ? 0 : errno;
+    env->ReleaseStringUTFChars(file, fileStr);
+    return returnValue;
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "getFileStatus", "(Ljava/lang/String;Landroid/os/cts/FileUtils$FileStatus;Z)Z",
+            (void *) android_os_cts_FileUtils_getFileStatus  },
+    {  "getUserName", "(I)Ljava/lang/String;",
+            (void *) android_os_cts_FileUtils_getUserName  },
+    {  "getGroupName", "(I)Ljava/lang/String;",
+            (void *) android_os_cts_FileUtils_getGroupName  },
+    {  "setPermissions", "(Ljava/lang/String;I)I",
+            (void *) android_os_cts_FileUtils_setPermissions },
+};
+
+int register_android_os_cts_FileUtils(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/os/cts/FileUtils");
+
+    gFileStatusClass = env->FindClass("android/os/cts/FileUtils$FileStatus");
+    gFileStatusDevFieldID = env->GetFieldID(gFileStatusClass, "dev", "I");
+    gFileStatusInoFieldID = env->GetFieldID(gFileStatusClass, "ino", "I");
+    gFileStatusModeFieldID = env->GetFieldID(gFileStatusClass, "mode", "I");
+    gFileStatusNlinkFieldID = env->GetFieldID(gFileStatusClass, "nlink", "I");
+    gFileStatusUidFieldID = env->GetFieldID(gFileStatusClass, "uid", "I");
+    gFileStatusGidFieldID = env->GetFieldID(gFileStatusClass, "gid", "I");
+    gFileStatusSizeFieldID = env->GetFieldID(gFileStatusClass, "size", "J");
+    gFileStatusBlksizeFieldID = env->GetFieldID(gFileStatusClass, "blksize", "I");
+    gFileStatusBlocksFieldID = env->GetFieldID(gFileStatusClass, "blocks", "J");
+    gFileStatusAtimeFieldID = env->GetFieldID(gFileStatusClass, "atime", "J");
+    gFileStatusMtimeFieldID = env->GetFieldID(gFileStatusClass, "mtime", "J");
+    gFileStatusCtimeFieldID = env->GetFieldID(gFileStatusClass, "ctime", "J");
+
+    return env->RegisterNatives(clazz, gMethods, 
+            sizeof(gMethods) / sizeof(JNINativeMethod)); 
+}
diff --git a/tests/res/raw/test1.obb b/tests/res/raw/test1.obb
new file mode 100644
index 0000000..a6b3e1c
--- /dev/null
+++ b/tests/res/raw/test1.obb
Binary files differ
diff --git a/tests/res/raw/test1_nosig.obb b/tests/res/raw/test1_nosig.obb
new file mode 100644
index 0000000..5c3573f
--- /dev/null
+++ b/tests/res/raw/test1_nosig.obb
Binary files differ
diff --git a/tests/res/raw/test1_wrongpackage.obb b/tests/res/raw/test1_wrongpackage.obb
new file mode 100644
index 0000000..d0aafe1
--- /dev/null
+++ b/tests/res/raw/test1_wrongpackage.obb
Binary files differ
diff --git a/tests/src/android/os/cts/FileUtils.java b/tests/src/android/os/cts/FileUtils.java
new file mode 100644
index 0000000..feaf7d7
--- /dev/null
+++ b/tests/src/android/os/cts/FileUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2011 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.os.cts;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Bits and pieces copied from hidden API of android.os.FileUtils. */
+public class FileUtils {
+
+    public static final int S_IFSOCK = 0140000;
+    public static final int S_IFLNK = 0120000;
+    public static final int S_IFREG = 0100000;
+    public static final int S_IFBLK = 0060000;
+    public static final int S_IFDIR = 0040000;
+    public static final int S_IFCHR = 0020000;
+    public static final int S_IFIFO = 0010000;
+
+    public static final int S_ISUID = 0004000;
+    public static final int S_ISGID = 0002000;
+    public static final int S_ISVTX = 0001000;
+
+    public static final int S_IRWXU = 00700;
+    public static final int S_IRUSR = 00400;
+    public static final int S_IWUSR = 00200;
+    public static final int S_IXUSR = 00100;
+
+    public static final int S_IRWXG = 00070;
+    public static final int S_IRGRP = 00040;
+    public static final int S_IWGRP = 00020;
+    public static final int S_IXGRP = 00010;
+
+    public static final int S_IRWXO = 00007;
+    public static final int S_IROTH = 00004;
+    public static final int S_IWOTH = 00002;
+    public static final int S_IXOTH = 00001;
+
+    static {
+        System.loadLibrary("cts_jni");
+    }
+
+    public static class FileStatus {
+
+        public int dev;
+        public int ino;
+        public int mode;
+        public int nlink;
+        public int uid;
+        public int gid;
+        public int rdev;
+        public long size;
+        public int blksize;
+        public long blocks;
+        public long atime;
+        public long mtime;
+        public long ctime;
+
+        public boolean hasModeFlag(int flag) {
+            return (mode & flag) == flag;
+        }
+    }
+
+    /**
+     * @param path of the file to stat
+     * @param status object to set the fields on
+     * @param statLinks or don't stat links (lstat vs stat)
+     * @return whether or not we were able to stat the file
+     */
+    public native static boolean getFileStatus(String path, FileStatus status, boolean statLinks);
+
+    public native static String getUserName(int uid);
+
+    public native static String getGroupName(int gid);
+
+    public native static int setPermissions(String file, int mode);
+
+    /**
+     * Copy data from a source stream to destFile.
+     * Return true if succeed, return false if failed.
+     */
+    public static boolean copyToFile(InputStream inputStream, File destFile) {
+        try {
+            if (destFile.exists()) {
+                destFile.delete();
+            }
+            FileOutputStream out = new FileOutputStream(destFile);
+            try {
+                byte[] buffer = new byte[4096];
+                int bytesRead;
+                while ((bytesRead = inputStream.read(buffer)) >= 0) {
+                    out.write(buffer, 0, bytesRead);
+                }
+            } finally {
+                out.flush();
+                try {
+                    out.getFD().sync();
+                } catch (IOException e) {
+                }
+                out.close();
+            }
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+}
diff --git a/tests/src/android/webkit/cts/CtsTestServer.java b/tests/src/android/webkit/cts/CtsTestServer.java
index 91f4f1e..707455b 100644
--- a/tests/src/android/webkit/cts/CtsTestServer.java
+++ b/tests/src/android/webkit/cts/CtsTestServer.java
@@ -79,6 +79,7 @@
 
     public static final String FAVICON_PATH = "/favicon.ico";
     public static final String USERAGENT_PATH = "/useragent.html";
+    public static final String TEST_DOWNLOAD_PATH = "/download.html";
     public static final String ASSET_PREFIX = "/assets/";
     public static final String FAVICON_ASSET_PATH = ASSET_PREFIX + "webkit/favicon.png";
     public static final String APPCACHE_PATH = "/appcache.html";
@@ -350,6 +351,10 @@
         return sb.toString();
     }
 
+    public String getTestDownloadUrl() {
+        return getBaseUri() + TEST_DOWNLOAD_PATH;
+    }
+
     public String getLastRequestUrl() {
         return mLastQuery;
     }
@@ -501,6 +506,10 @@
             }
             response.setEntity(createEntity("<html><head><title>" + agent + "</title></head>" +
                     "<body>" + agent + "</body></html>"));
+        } else if (path.equals(TEST_DOWNLOAD_PATH)) {
+            response = createResponse(HttpStatus.SC_OK);
+            response.setHeader("Content-Length", "0");
+            response.setEntity(createEntity(""));
         } else if (path.equals(SHUTDOWN_PREFIX)) {
             response = createResponse(HttpStatus.SC_OK);
             // We cannot close the socket here, because we need to respond.
diff --git a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
new file mode 100644
index 0000000..fbe57c4
--- /dev/null
+++ b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2011 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.cts;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.view.animation.cts.DelayedCheck;
+import android.webkit.cts.CtsTestServer;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class DownloadManagerTest extends AndroidTestCase {
+
+    private DownloadManager mDownloadManager;
+
+    private CtsTestServer mWebServer;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+        mWebServer = new CtsTestServer(mContext);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mWebServer.shutdown();
+    }
+
+    public void testDownloadManager() throws Exception {
+        DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            removeAllDownloads();
+
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            long goodId = mDownloadManager.enqueue(new Request(getGoodUrl()));
+            long badId = mDownloadManager.enqueue(new Request(getBadUrl()));
+
+            int allDownloads = getTotalNumberDownloads();
+            assertEquals(2, allDownloads);
+
+            assertDownloadQueryableById(goodId);
+            assertDownloadQueryableById(badId);
+
+            receiver.waitForDownloadComplete();
+
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_FAILED);
+
+            assertRemoveDownload(goodId, allDownloads - 1);
+            assertRemoveDownload(badId, allDownloads - 2);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    private class DownloadCompleteReceiver extends BroadcastReceiver {
+
+        private final CountDownLatch mReceiveLatch = new CountDownLatch(2);
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mReceiveLatch.countDown();
+        }
+
+        public void waitForDownloadComplete() throws InterruptedException {
+            assertTrue("Make sure you have WiFi or some other connectivity for this test.",
+                    mReceiveLatch.await(3, TimeUnit.SECONDS));
+        }
+    }
+
+    private void removeAllDownloads() {
+        if (getTotalNumberDownloads() > 0) {
+            Cursor cursor = null;
+            try {
+                Query query = new Query();
+                cursor = mDownloadManager.query(query);
+                int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+                long[] removeIds = new long[cursor.getCount()];
+                for (int i = 0; cursor.moveToNext(); i++) {
+                    removeIds[i] = cursor.getLong(columnIndex);
+                }
+                assertEquals(removeIds.length, mDownloadManager.remove(removeIds));
+                assertEquals(0, getTotalNumberDownloads());
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+    }
+
+    private Uri getGoodUrl() {
+        return Uri.parse(mWebServer.getTestDownloadUrl());
+    }
+
+    private Uri getBadUrl() {
+        return Uri.parse(mWebServer.getBaseUri() + "/nosuchurl");
+    }
+
+    private int getTotalNumberDownloads() {
+        Cursor cursor = null;
+        try {
+            Query query = new Query();
+            cursor = mDownloadManager.query(query);
+            return cursor.getCount();
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertDownloadQueryableById(long downloadId) {
+        Cursor cursor = null;
+        try {
+            Query query = new Query().setFilterById(downloadId);
+            cursor = mDownloadManager.query(query);
+            assertEquals(1, cursor.getCount());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertDownloadQueryableByStatus(final int status) {
+        new DelayedCheck() {
+            @Override
+            protected boolean check() {
+                Cursor cursor= null;
+                try {
+                    Query query = new Query().setFilterByStatus(status);
+                    cursor = mDownloadManager.query(query);
+                    return 1 == cursor.getCount();
+                } finally {
+                    if (cursor != null) {
+                        cursor.close();
+                    }
+                }
+            }
+        }.run();
+    }
+
+    private void assertRemoveDownload(long removeId, int expectedNumDownloads) {
+        Cursor cursor = null;
+        try {
+            assertEquals(1, mDownloadManager.remove(removeId));
+            Query query = new Query();
+            cursor = mDownloadManager.query(query);
+            assertEquals(expectedNumDownloads, cursor.getCount());
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index 12a828d..d9ea59e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -332,11 +332,9 @@
         assertTrue(mShutterCallbackResult);
         assertTrue(mJpegPictureCallbackResult);
         assertNotNull(mJpegData);
-        BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
-        bmpOptions.inJustDecodeBounds = true;
-        BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length, bmpOptions);
-        assertEquals(pictureSize.width, bmpOptions.outWidth);
-        assertEquals(pictureSize.height, bmpOptions.outHeight);
+        Bitmap b = BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length);
+        assertEquals(pictureSize.width, b.getWidth());
+        assertEquals(pictureSize.height, b.getHeight());
     }
 
     @TestTargets({
@@ -807,11 +805,9 @@
         ExifInterface exif = new ExifInterface(JPEG_PATH);
         assertTrue(exif.hasThumbnail());
         byte[] thumb = exif.getThumbnail();
-        BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
-        bmpOptions.inJustDecodeBounds = true;
-        BitmapFactory.decodeByteArray(thumb, 0, thumb.length, bmpOptions);
-        assertEquals(size.width, bmpOptions.outWidth);
-        assertEquals(size.height, bmpOptions.outHeight);
+        Bitmap b = BitmapFactory.decodeByteArray(thumb, 0, thumb.length);
+        assertEquals(size.width, b.getWidth());
+        assertEquals(size.height, b.getHeight());
 
         // Test no thumbnail case.
         p.setJpegThumbnailSize(0, 0);
@@ -1532,11 +1528,11 @@
                 waitForSnapshotDone();
                 assertTrue(mJpegPictureCallbackResult);
                 assertNotNull(mJpegData);
-                BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
-                bmpOptions.inJustDecodeBounds = true;
-                BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length, bmpOptions);
-                assertEquals(pictureSize.width, bmpOptions.outWidth);
-                assertEquals(pictureSize.height, bmpOptions.outHeight);
+                Bitmap b = BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length);
+                assertEquals(pictureSize.width, b.getWidth());
+                assertEquals(pictureSize.height, b.getHeight());
+                b.recycle();
+                b = null;
             }
         }
         terminateMessageLooper();
diff --git a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
new file mode 100644
index 0000000..b229d03
--- /dev/null
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 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.os.storage.cts;
+
+import com.android.cts.stub.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.os.cts.FileUtils;
+import android.os.storage.OnObbStateChangeListener;
+import android.os.storage.StorageManager;
+import android.test.AndroidTestCase;
+import android.test.ComparisonFailure;
+import android.util.Log;
+
+import java.io.File;
+import java.io.InputStream;
+
+public class StorageManagerTest extends AndroidTestCase {
+
+    private static final String TAG = StorageManager.class.getSimpleName();
+
+    private static final long MAX_WAIT_TIME = 25*1000;
+    private static final long WAIT_TIME_INCR = 5*1000;
+
+    private static final String OBB_MOUNT_PREFIX = "/mnt/obb/";
+
+    private StorageManager mStorageManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
+    }
+
+    public void testMountAndUnmountObbNormal() {
+        final File outFile = getFilePath("test1.obb");
+
+        mountObb(R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED);
+
+        mountObb(R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
+
+        final String mountPath = checkMountedPath(outFile);
+        final File mountDir = new File(mountPath);
+
+        assertTrue("OBB mounted path should be a directory", mountDir.isDirectory());
+
+        unmountObb(outFile, OnObbStateChangeListener.UNMOUNTED);
+    }
+
+    public void testAttemptMountNonObb() {
+        final File outFile = getFilePath("test1_nosig.obb");
+
+        mountObb(R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
+
+        assertFalse("OBB should not be mounted",
+                mStorageManager.isObbMounted(outFile.getPath()));
+
+        assertNull("OBB's mounted path should be null",
+                mStorageManager.getMountedObbPath(outFile.getPath()));
+    }
+
+    public void testAttemptMountObbWrongPackage() {
+        final File outFile = getFilePath("test1_wrongpackage.obb");
+
+        mountObb(R.raw.test1_wrongpackage, outFile,
+                OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+
+        assertFalse("OBB should not be mounted",
+                mStorageManager.isObbMounted(outFile.getPath()));
+
+        assertNull("OBB's mounted path should be null",
+                mStorageManager.getMountedObbPath(outFile.getPath()));
+    }
+
+    public void testMountAndUnmountTwoObbs() {
+        final File file1 = getFilePath("test1.obb");
+        final File file2 = getFilePath("test2.obb");
+
+        ObbObserver oo1 = mountObbWithoutWait(R.raw.test1, file1);
+        ObbObserver oo2 = mountObbWithoutWait(R.raw.test1, file2);
+
+        Log.d(TAG, "Waiting for OBB #1 to complete mount");
+        waitForObbActionCompletion(file1, oo1, OnObbStateChangeListener.MOUNTED, false);
+        Log.d(TAG, "Waiting for OBB #2 to complete mount");
+        waitForObbActionCompletion(file2, oo2, OnObbStateChangeListener.MOUNTED, false);
+
+        final String mountPath1 = checkMountedPath(file1);
+        final File mountDir1 = new File(mountPath1);
+        assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
+
+        final String mountPath2 = checkMountedPath(file2);
+        final File mountDir2 = new File(mountPath2);
+        assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
+
+        unmountObb(file1, OnObbStateChangeListener.UNMOUNTED);
+        unmountObb(file2, OnObbStateChangeListener.UNMOUNTED);
+    }
+
+    private static void assertStartsWith(String message, String prefix, String actual) {
+        if (!actual.startsWith(prefix)) {
+            throw new ComparisonFailure(message, prefix, actual);
+        }
+    }
+
+    private static class ObbObserver extends OnObbStateChangeListener {
+        private String path;
+
+        public int state = -1;
+        boolean done = false;
+
+        @Override
+        public void onObbStateChange(String path, int state) {
+            Log.d(TAG, "Received message.  path=" + path + ", state=" + state);
+            synchronized (this) {
+                this.path = path;
+                this.state = state;
+                done = true;
+                notifyAll();
+            }
+        }
+
+        public String getPath() {
+            assertTrue("Expected ObbObserver to have received a state change.", done);
+            return path;
+        }
+
+        public int getState() {
+            assertTrue("Expected ObbObserver to have received a state change.", done);
+            return state;
+        }
+
+        public boolean isDone() {
+            return done;
+        }
+
+        public boolean waitForCompletion() {
+            long waitTime = 0;
+            synchronized (this) {
+                while (!isDone() && waitTime < MAX_WAIT_TIME) {
+                    try {
+                        wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    } catch (InterruptedException e) {
+                        Log.i(TAG, "Interrupted during sleep", e);
+                    }
+                }
+            }
+
+            return isDone();
+        }
+    }
+
+    private File getFilePath(String name) {
+        final File filesDir = mContext.getFilesDir();
+        final File outFile = new File(filesDir, name);
+        return outFile;
+    }
+
+    private void copyRawToFile(int rawResId, File outFile) {
+        Resources res = mContext.getResources();
+        InputStream is = null;
+        try {
+            is = res.openRawResource(rawResId);
+        } catch (NotFoundException e) {
+            fail("Failed to load resource with id: " + rawResId);
+        }
+        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                | FileUtils.S_IRWXO);
+        assertTrue(FileUtils.copyToFile(is, outFile));
+        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                | FileUtils.S_IRWXO);
+    }
+
+    private void mountObb(final int resource, final File file, int expectedState) {
+        copyRawToFile(resource, file);
+
+        final ObbObserver observer = new ObbObserver();
+        assertTrue("mountObb call on " + file.getPath() + " should succeed",
+                mStorageManager.mountObb(file.getPath(), null, observer));
+
+        assertTrue("Mount should have completed",
+                observer.waitForCompletion());
+
+        if (expectedState == OnObbStateChangeListener.MOUNTED) {
+            assertTrue("OBB should be mounted", mStorageManager.isObbMounted(file.getPath()));
+        }
+
+        assertEquals("Actual file and resolved file should be the same",
+                file.getPath(), observer.getPath());
+
+        assertEquals(expectedState, observer.getState());
+    }
+
+    private ObbObserver mountObbWithoutWait(final int resource, final File file) {
+        copyRawToFile(resource, file);
+
+        final ObbObserver observer = new ObbObserver();
+        assertTrue("mountObb call on " + file.getPath() + " should succeed",
+                mStorageManager.mountObb(file.getPath(), null, observer));
+
+        return observer;
+    }
+
+    private void waitForObbActionCompletion(final File file,
+            final ObbObserver observer, int expectedState, boolean checkPath) {
+        assertTrue("Mount should have completed", observer.waitForCompletion());
+
+        assertTrue("OBB should be mounted", mStorageManager.isObbMounted(file.getPath()));
+
+        if (checkPath) {
+            assertEquals("Actual file and resolved file should be the same", file.getPath(),
+                    observer.getPath());
+        }
+
+        assertEquals(expectedState, observer.getState());
+    }
+
+    private String checkMountedPath(final File file) {
+        final String mountPath = mStorageManager.getMountedObbPath(file.getPath());
+        assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX,
+                OBB_MOUNT_PREFIX,
+                mountPath);
+        return mountPath;
+    }
+
+    private void unmountObb(final File file, int expectedState) {
+        final ObbObserver observer = new ObbObserver();
+
+        assertTrue("unmountObb call on test1.obb should succeed",
+                mStorageManager.unmountObb(file.getPath(), false, observer));
+
+        assertTrue("Unmount should have completed",
+                observer.waitForCompletion());
+
+        assertEquals(expectedState, observer.getState());
+
+        if (expectedState == OnObbStateChangeListener.UNMOUNTED) {
+            assertFalse("OBB should not be mounted", mStorageManager.isObbMounted(file.getPath()));
+        }
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 80d44f7..d7233d5 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -64,7 +64,8 @@
                     "45002",    // SKT Mobility
                     "45008",    // KT Mobility
                     "45006",    // LGT
-                    "311660"    // MetroPCS
+                    "311660",   // MetroPCS
+                    "310120"    // Sprint
             );
 
     // List of network operators that doesn't support Data(binary) SMS message