diff options
author | Kenny Root <kroot@google.com> | 2012-09-08 22:02:21 -0700 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2012-09-09 15:12:16 -0700 |
commit | 7e921a157d1aca97506302637d4f3843ddc2b982 (patch) | |
tree | 5127912c20609ab661371beb91fef75ca069642f | |
parent | 5455f6826f832ed0f07d28a772f4489d7223acb3 (diff) | |
download | frameworks_base-7e921a157d1aca97506302637d4f3843ddc2b982.zip frameworks_base-7e921a157d1aca97506302637d4f3843ddc2b982.tar.gz frameworks_base-7e921a157d1aca97506302637d4f3843ddc2b982.tar.bz2 |
Add more testing for native libraries
Make sure that native library paths have the correct permissions and
owners. Also make it easier to do a "String#startsWith" test so we can
get better errors when it fails.
Change the observer to have more sane semantics with a CountDownLatch
instead of weird synchronization on a local variable.
Change-Id: I2437e5ea886d6e6cb8b4edeab80d6053b79857d4
-rwxr-xr-x | core/tests/coretests/src/android/content/pm/PackageManagerTests.java | 258 |
1 files changed, 189 insertions, 69 deletions
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index f46478c..1868d1c 100755 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -16,6 +16,8 @@ package android.content.pm; +import static libcore.io.OsConstants.*; + import com.android.frameworks.coretests.R; import com.android.internal.content.PackageHelper; @@ -32,9 +34,11 @@ import android.net.Uri; import android.os.Environment; import android.os.FileUtils; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; +import android.os.SystemClock; import android.os.storage.IMountService; import android.os.storage.StorageListener; import android.os.storage.StorageManager; @@ -52,6 +56,12 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import libcore.io.ErrnoException; +import libcore.io.Libcore; +import libcore.io.StructStat; public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; @@ -404,15 +414,12 @@ public class PackageManagerTests extends AndroidTestCase { if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { assertTrue("The application should be installed forward locked", (info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); - assertTrue("The APK path (" + srcPath + ") should start with " - + SECURE_CONTAINERS_PREFIX, - srcPath.startsWith(SECURE_CONTAINERS_PREFIX)); - assertTrue("The public APK path (" + publicSrcPath + ") should start with " - + SECURE_CONTAINERS_PREFIX, - publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX)); - assertTrue("The native library path (" + info.nativeLibraryDir - + ") should start with " + SECURE_CONTAINERS_PREFIX, - info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX)); + assertStartsWith("The APK path should point to the ASEC", + SECURE_CONTAINERS_PREFIX, srcPath); + assertStartsWith("The public APK path should point to the ASEC", + SECURE_CONTAINERS_PREFIX, publicSrcPath); + assertStartsWith("The native library path should point to the ASEC", + SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir); try { String compatLib = new File(info.dataDir + "/lib").getCanonicalPath(); assertEquals("The compatibility lib directory should be a symbolic link to " @@ -425,7 +432,14 @@ public class PackageManagerTests extends AndroidTestCase { assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); assertEquals(srcPath, appInstallPath); assertEquals(publicSrcPath, appInstallPath); - assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath())); + assertStartsWith("Native library should point to shared lib directory", + dataDir.getPath(), + info.nativeLibraryDir); + assertDirOwnerGroupPerms( + "Native library directory should be owned by system:system and 0755", + Process.SYSTEM_UID, Process.SYSTEM_UID, + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, + info.nativeLibraryDir); } assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); @@ -435,8 +449,7 @@ public class PackageManagerTests extends AndroidTestCase { nativeLibDir.exists()); try { assertEquals("Native library dir should not be a symlink", - info.nativeLibraryDir, - nativeLibDir.getCanonicalPath()); + info.nativeLibraryDir, nativeLibDir.getCanonicalPath()); } catch (IOException e) { fail("Can't read " + nativeLibDir.getPath()); } @@ -453,14 +466,12 @@ public class PackageManagerTests extends AndroidTestCase { (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); // Might need to check: // ((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) - assertTrue("The APK path (" + srcPath + ") should start with " - + SECURE_CONTAINERS_PREFIX, srcPath.startsWith(SECURE_CONTAINERS_PREFIX)); - assertTrue("The public APK path (" + publicSrcPath + ") should start with " - + SECURE_CONTAINERS_PREFIX, - publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX)); - assertTrue("The native library path (" + info.nativeLibraryDir - + ") should start with " + SECURE_CONTAINERS_PREFIX, - info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX)); + assertStartsWith("The APK path should point to the ASEC", + SECURE_CONTAINERS_PREFIX, srcPath); + assertStartsWith("The public APK path should point to the ASEC", + SECURE_CONTAINERS_PREFIX, publicSrcPath); + assertStartsWith("The native library path should point to the ASEC", + SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir); // Make sure the native library in /data/data/<app>/lib is a // symlink to the ASEC @@ -483,6 +494,66 @@ public class PackageManagerTests extends AndroidTestCase { } } + private void assertDirOwnerGroupPerms(String reason, int uid, int gid, int perms, String path) { + final StructStat stat; + + try { + stat = Libcore.os.lstat(path); + } catch (ErrnoException e) { + throw new AssertionError(reason + "\n" + "Got: " + path + " does not exist"); + } + + StringBuilder sb = new StringBuilder(); + + if (!S_ISDIR(stat.st_mode)) { + sb.append("\nExpected type: "); + sb.append(S_IFDIR); + sb.append("\ngot type: "); + sb.append((stat.st_mode & S_IFMT)); + } + + if (stat.st_uid != uid) { + sb.append("\nExpected owner: "); + sb.append(uid); + sb.append("\nGot owner: "); + sb.append(stat.st_uid); + } + + if (stat.st_gid != gid) { + sb.append("\nExpected group: "); + sb.append(gid); + sb.append("\nGot group: "); + sb.append(stat.st_gid); + } + + if ((stat.st_mode & ~S_IFMT) != perms) { + sb.append("\nExpected permissions: "); + sb.append(Integer.toOctalString(perms)); + sb.append("\nGot permissions: "); + sb.append(Integer.toOctalString(stat.st_mode & ~S_IFMT)); + } + + if (sb.length() > 0) { + throw new AssertionError(reason + sb.toString()); + } + } + + private static void assertStartsWith(String prefix, String actual) { + assertStartsWith("", prefix, actual); + } + + private static void assertStartsWith(String description, String prefix, String actual) { + if (!actual.startsWith(prefix)) { + StringBuilder sb = new StringBuilder(description); + sb.append("\nExpected prefix: "); + sb.append(prefix); + sb.append("\n got: "); + sb.append(actual); + sb.append('\n'); + throw new AssertionError(sb.toString()); + } + } + private void assertNotInstalled(String pkgName) { try { ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0); @@ -820,22 +891,51 @@ public class PackageManagerTests extends AndroidTestCase { | PackageManager.INSTALL_EXTERNAL); } - /* -------------- Delete tests ---*/ + /* -------------- Delete tests --- */ private static class DeleteObserver extends IPackageDeleteObserver.Stub { + private CountDownLatch mLatch = new CountDownLatch(1); - public boolean succeeded; - private boolean doneFlag = false; + private int mReturnCode; - public boolean isDone() { - return doneFlag; + private final String mPackageName; + + private String mObservedPackage; + + public DeleteObserver(String packageName) { + mPackageName = packageName; + } + + public boolean isSuccessful() { + return mReturnCode == PackageManager.DELETE_SUCCEEDED; } public void packageDeleted(String packageName, int returnCode) throws RemoteException { - synchronized(this) { - this.succeeded = returnCode == PackageManager.DELETE_SUCCEEDED; - doneFlag = true; - notifyAll(); + mObservedPackage = packageName; + + mReturnCode = returnCode; + + mLatch.countDown(); + } + + public void waitForCompletion(long timeoutMillis) { + final long deadline = SystemClock.uptimeMillis() + timeoutMillis; + + long waitTime = timeoutMillis; + while (waitTime > 0) { + try { + boolean done = mLatch.await(waitTime, TimeUnit.MILLISECONDS); + if (done) { + assertEquals(mPackageName, mObservedPackage); + return; + } + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + waitTime = deadline - SystemClock.uptimeMillis(); } + + throw new AssertionError("Timeout waiting for package deletion"); } } @@ -863,41 +963,40 @@ public class PackageManagerTests extends AndroidTestCase { } } - public boolean invokeDeletePackage(final String pkgName, int flags, - GenericReceiver receiver) throws Exception { - DeleteObserver observer = new DeleteObserver(); - final boolean received = false; + public boolean invokeDeletePackage(final String pkgName, int flags, GenericReceiver receiver) + throws Exception { + ApplicationInfo info = getPm().getApplicationInfo(pkgName, + PackageManager.GET_UNINSTALLED_PACKAGES); + mContext.registerReceiver(receiver, receiver.filter); try { - // Wait on observer - synchronized(observer) { - synchronized (receiver) { - getPm().deletePackage(pkgName, observer, flags); - long waitTime = 0; - while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { - observer.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; - } - if(!observer.isDone()) { - throw new Exception("Timed out waiting for packageInstalled callback"); - } - // Verify we received the broadcast - waitTime = 0; - while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) { - receiver.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; - } - if(!receiver.isDone()) { - throw new Exception("Timed out waiting for PACKAGE_REMOVED notification"); - } - return receiver.received; - } + DeleteObserver observer = new DeleteObserver(pkgName); + + getPm().deletePackage(pkgName, observer, flags); + observer.waitForCompletion(MAX_WAIT_TIME); + + assertUninstalled(info); + + // Verify we received the broadcast + long waitTime = 0; + while ((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME)) { + receiver.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; } + if (!receiver.isDone()) { + throw new Exception("Timed out waiting for PACKAGE_REMOVED notification"); + } + return receiver.received; } finally { mContext.unregisterReceiver(receiver); } } + private static void assertUninstalled(ApplicationInfo info) throws Exception { + File nativeLibraryFile = new File(info.nativeLibraryDir); + assertFalse("Native library directory should be erased", nativeLibraryFile.exists()); + } + public void deleteFromRawResource(int iFlags, int dFlags) throws Exception { InstallParams ip = sampleInstallFromRawResource(iFlags, false); boolean retainData = ((dFlags & PackageManager.DELETE_KEEP_DATA) != 0); @@ -1212,11 +1311,29 @@ public class PackageManagerTests extends AndroidTestCase { return; } Runtime.getRuntime().gc(); - Log.i(TAG, "Deleting package : " + ip.pkg.packageName); - getPm().deletePackage(ip.pkg.packageName, null, 0); - File outFile = new File(ip.pkg.mScanPath); - if (outFile != null && outFile.exists()) { - outFile.delete(); + + final String packageName = ip.pkg.packageName; + Log.i(TAG, "Deleting package : " + packageName); + + ApplicationInfo info = null; + try { + info = getPm().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); + } catch (NameNotFoundException ignored) { + } + + DeleteObserver observer = new DeleteObserver(packageName); + getPm().deletePackage(packageName, observer, 0); + observer.waitForCompletion(MAX_WAIT_TIME); + + try { + if (info != null) { + assertUninstalled(info); + } + } finally { + File outFile = new File(ip.pkg.mScanPath); + if (outFile != null && outFile.exists()) { + outFile.delete(); + } } } @@ -1230,7 +1347,10 @@ public class PackageManagerTests extends AndroidTestCase { PackageManager.GET_UNINSTALLED_PACKAGES); if (info != null) { - getPm().deletePackage(pkgName, null, 0); + DeleteObserver observer = new DeleteObserver(pkgName); + getPm().deletePackage(pkgName, observer, 0); + observer.waitForCompletion(MAX_WAIT_TIME); + assertUninstalled(info); } } catch (NameNotFoundException e) { } @@ -1587,16 +1707,16 @@ public class PackageManagerTests extends AndroidTestCase { if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) { assertTrue("ApplicationInfo.FLAG_EXTERNAL_STORAGE flag should NOT be set", (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0); - assertTrue("ApplicationInfo.nativeLibraryDir should start with " + info.dataDir, - info.nativeLibraryDir.startsWith(info.dataDir)); - } else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0){ + assertStartsWith("Native library dir should be in dataDir", + info.dataDir, info.nativeLibraryDir); + } else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0) { assertTrue("ApplicationInfo.FLAG_EXTERNAL_STORAGE flag should be set", (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); - assertTrue("ApplicationInfo.nativeLibraryDir should start with " + SECURE_CONTAINERS_PREFIX, - info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX)); + assertStartsWith("Native library dir should point to ASEC", + SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir); final File nativeLibSymLink = new File(info.dataDir, "lib"); - assertTrue("The data directory should have a 'lib' symlink that points to the ASEC container", - nativeLibSymLink.getCanonicalPath().startsWith(SECURE_CONTAINERS_PREFIX)); + assertStartsWith("The data directory should have a 'lib' symlink that points to the ASEC container", + SECURE_CONTAINERS_PREFIX, nativeLibSymLink.getCanonicalPath()); } } } catch (NameNotFoundException e) { |