/*
 * Copyright (C) 2006 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.content.pm;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class AppCacheTest extends AndroidTestCase {
    private static final boolean localLOGV = false;
    public static final String TAG="AppCacheTest";
    public final long MAX_WAIT_TIME=60*1000;
    public final long WAIT_TIME_INCR=10*1000;
    private static final long THRESHOLD=5;
    private static final long ACTUAL_THRESHOLD=10;
    
    @Override
    protected void setUp() throws Exception {
        super.setUp();
        if(localLOGV) Log.i(TAG, "Cleaning up cache directory first");
        cleanUpCacheDirectory();
    }
    
    void cleanUpDirectory(File pDir, String dirName) {
       File testDir = new File(pDir,  dirName);
       if(!testDir.exists()) {
           return;
       }
        String fList[] = testDir.list();
        for(int i = 0; i < fList.length; i++) {
            File file = new File(testDir, fList[i]);
            if(file.isDirectory()) {
                cleanUpDirectory(testDir, fList[i]);
            } else {
                file.delete();
            }
        }
        testDir.delete();
    }
    
    void cleanUpCacheDirectory() {
        File testDir = mContext.getCacheDir();
        if(!testDir.exists()) {
            return;
        }
        
         String fList[] = testDir.list();
         if(fList == null) {
             testDir.delete();
             return;
         }
         for(int i = 0; i < fList.length; i++) {
             File file = new File(testDir, fList[i]);
             if(file.isDirectory()) {
                 cleanUpDirectory(testDir, fList[i]);
             } else {
                 file.delete();
             }
         }
     }
    
    @SmallTest
    public void testDeleteAllCacheFiles() {
        String testName="testDeleteAllCacheFiles";
        cleanUpCacheDirectory();
    }

    void failStr(String errMsg) {
        Log.w(TAG, "errMsg="+errMsg);
        fail(errMsg);
    }

    void failStr(Exception e) {
        Log.w(TAG, "e.getMessage="+e.getMessage());
        Log.w(TAG, "e="+e);
    }

    long getFreeStorageBlks(StatFs st) {
        st.restat("/data");
        return st.getFreeBlocks();
    }

    long getFreeStorageSize(StatFs st) {
        st.restat("/data");
        return (long) st.getFreeBlocks() * (long) st.getBlockSize();
    }

    @LargeTest
    public void testFreeApplicationCacheAllFiles() throws Exception {
        boolean TRACKING = true;
        StatFs st = new StatFs("/data");
        long blks1 = getFreeStorageBlks(st);
        long availableMem = getFreeStorageSize(st);
        File cacheDir = mContext.getCacheDir();
        assertNotNull(cacheDir);
        createTestFiles1(cacheDir, "testtmpdir", 5);
        long blks2 = getFreeStorageBlks(st);
        if(localLOGV || TRACKING) Log.i(TAG, "blk1="+blks1+", blks2="+blks2);
        //this should free up the test files that were created earlier
        if (!invokePMFreeApplicationCache(availableMem)) {
            fail("Could not successfully invoke PackageManager free app cache API");
        }
        long blks3 = getFreeStorageBlks(st);
        if(localLOGV || TRACKING) Log.i(TAG, "blks3="+blks3);
        verifyTestFiles1(cacheDir, "testtmpdir", 5);
    }

    public void testFreeApplicationCacheSomeFiles() throws Exception {
        StatFs st = new StatFs("/data");
        long blks1 = getFreeStorageBlks(st);
        File cacheDir = mContext.getCacheDir();
        assertNotNull(cacheDir);
        createTestFiles1(cacheDir, "testtmpdir", 5);
        long blks2 = getFreeStorageBlks(st);
        Log.i(TAG, "blk1="+blks1+", blks2="+blks2);
        long diff = (blks1-blks2-2);
        if (!invokePMFreeApplicationCache(diff * st.getBlockSize())) {
            fail("Could not successfully invoke PackageManager free app cache API");
        }
        long blks3 = getFreeStorageBlks(st);
        //blks3 should be greater than blks2 and less than blks1
        if(!((blks3 <= blks1) && (blks3 >= blks2))) {
            failStr("Expected "+(blks1-blks2)+" number of blocks to be freed but freed only "
                    +(blks1-blks3));
        }
    }
    
    /**
     * This method opens an output file writes to it, opens the same file as an input 
     * stream, reads the contents and verifies the data that was written earlier can be read
     */
    public void openOutFileInAppFilesDir(File pFile, String pFileOut) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(pFile);
        } catch (FileNotFoundException e1) {
            failStr("Error when opening file "+e1);
            return;
        }
        try {
            fos.write(pFileOut.getBytes());
            fos.close();
        } catch (FileNotFoundException e) {
            failStr(e.getMessage());
        } catch (IOException e) {
            failStr(e.getMessage());
        } 
        int count = pFileOut.getBytes().length;
        byte[] buffer = new byte[count];
        try {
            FileInputStream fis = new FileInputStream(pFile);
            fis.read(buffer, 0, count);
            fis.close();
        } catch (FileNotFoundException e) {
            failStr("Failed when verifing output opening file "+e.getMessage());
        } catch (IOException e) {
            failStr("Failed when verifying output, reading from written file "+e);
        }
        String str = new String(buffer);
        assertEquals(str, pFileOut);
    } 
    
    /*
     * This test case verifies that output written to a file
     * using Context.openFileOutput has executed successfully.
     * The operation is verified by invoking Context.openFileInput
     */
    @MediumTest
    public void testAppFilesCreateFile() {
        String fileName = "testFile1.txt";
        String fileOut = "abcdefghijklmnopqrstuvwxyz";
        Context con = super.getContext();
        try {
            FileOutputStream fos = con.openFileOutput(fileName, Context.MODE_PRIVATE);
            fos.close();
        } catch (FileNotFoundException e) {
            failStr(e);
        } catch (IOException e) {
            failStr(e);
        }
    }
    
    @SmallTest
    public void testAppCacheCreateFile() {
        String fileName = "testFile1.txt";
        String fileOut = "abcdefghijklmnopqrstuvwxyz";
        Context con = super.getContext();
        File file = new File(con.getCacheDir(), fileName);
        openOutFileInAppFilesDir(file, fileOut);
        cleanUpCacheDirectory();
    }
    
    @MediumTest
    public void testAppCreateCacheFiles() {
        File cacheDir = mContext.getCacheDir();
        String testDirName = "testtmp";
        File testTmpDir = new File(cacheDir, testDirName);
        testTmpDir.mkdir();
        int numDirs = 3;
        File fileArr[] = new File[numDirs];
        for(int i = 0; i < numDirs; i++) {
            fileArr[i] = new File(testTmpDir, "dir"+(i+1));
            fileArr[i].mkdir();
        }
        byte buffer[] = getBuffer();
        Log.i(TAG, "Size of bufer="+buffer.length);
        for(int i = 0; i < numDirs; i++) {
            for(int j = 1; j <= (i); j++) {
                File file1 = new File(fileArr[i], "testFile"+j+".txt");
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(file1);
                    for(int k = 1; k < 10; k++) {
                        fos.write(buffer);
                    }
                    Log.i(TAG, "wrote 10K bytes to "+file1);
                    fos.close();
                } catch (FileNotFoundException e) {
                    Log.i(TAG, "Excetion ="+e);
                    fail("Error when creating outputstream "+e);
                } catch(IOException e) {
                    Log.i(TAG, "Excetion ="+e);
                    fail("Error when writing output "+e);
                }
            }
        }
    }
    
    byte[] getBuffer() {
        String sbuffer = "a";
        for(int i = 0; i < 10; i++) {
            sbuffer += sbuffer;
        }
        return sbuffer.getBytes();
    }

    long getFileNumBlocks(long fileSize, long blkSize) {
        long ret = fileSize/blkSize;
        if(ret*blkSize < fileSize) {
            ret++;
        }
        return ret;
    }

    //@LargeTest
    public void testAppCacheClear() {
        String dataDir="/data/data";
        StatFs st = new StatFs(dataDir);
        long blkSize = st.getBlockSize();
        long totBlks = st.getBlockCount();
        long availableBlks = st.getFreeBlocks();
        long thresholdBlks = (totBlks * THRESHOLD) / 100L;
        String testDirName = "testdir";
        //create directory in cache
        File testDir = new File(mContext.getCacheDir(),  testDirName);
        testDir.mkdirs();
        byte[] buffer = getBuffer();
        int i = 1;
        if(localLOGV) Log.i(TAG, "availableBlks="+availableBlks+", thresholdBlks="+thresholdBlks);
        long createdFileBlks = 0;
        int imax = 300;
        while((availableBlks > thresholdBlks) &&(i < imax)) {
            File testFile = new File(testDir, "testFile"+i+".txt");
            if(localLOGV) Log.i(TAG, "Creating "+i+"th test file "+testFile);
            int jmax = i;
            i++;
            FileOutputStream fos;
            try {
                fos = new FileOutputStream(testFile);
            } catch (FileNotFoundException e) {
                Log.i(TAG, "Failed creating test file:"+testFile);
                continue;
            }
            boolean err = false;
            for(int j = 1; j <= jmax;j++) {
                try {
                    fos.write(buffer);
                } catch (IOException e) {
                    Log.i(TAG, "Failed to write to file:"+testFile);
                    err = true;
                }
            }
            try {
                fos.close();
            } catch (IOException e) {
                Log.i(TAG, "Failed closing file:"+testFile);
            }
            if(err) {
                continue;
            }
            createdFileBlks += getFileNumBlocks(testFile.length(), blkSize);
            st.restat(dataDir);
            availableBlks = st.getFreeBlocks();
        }
        st.restat(dataDir);
        long availableBytes = st.getFreeBlocks()*blkSize;
        long shouldFree = (ACTUAL_THRESHOLD-THRESHOLD)*totBlks;
        //would have run out of memory
        //wait for some time and confirm cache is deleted
        try {
            Log.i(TAG, "Sleeping for 2 minutes...");
            Thread.sleep(2*60*1000);
        } catch (InterruptedException e) {
            fail("Exception when sleeping "+e);
        }
        boolean removedFlag = false;
        long existingFileBlks = 0;
        for(int k = 1; k <i; k++) {
            File testFile = new File(testDir, "testFile"+k+".txt");
            if(!testFile.exists()) {
                removedFlag = true;
                if(localLOGV) Log.i(TAG, testFile+" removed");
            }  else {
                existingFileBlks += getFileNumBlocks(testFile.length(), blkSize);
            }
        }
        if(localLOGV) Log.i(TAG, "createdFileBlks="+createdFileBlks+
                ", existingFileBlks="+existingFileBlks);
        long fileSize = createdFileBlks-existingFileBlks;
        //verify fileSize number of bytes have been cleared from cache
        if(localLOGV) Log.i(TAG, "deletedFileBlks="+fileSize+" shouldFreeBlks="+shouldFree);
        if((fileSize > (shouldFree-blkSize) && (fileSize < (shouldFree+blkSize)))) {
            Log.i(TAG, "passed");
        }
        assertTrue("Files should have been removed", removedFlag);
    }
    
    //createTestFiles(new File(super.getContext().getCacheDir(), "testtmp", "dir", 3)
    void createTestFiles1(File cacheDir, String testFilePrefix, int numTestFiles) {
        byte buffer[] = getBuffer();
        for(int i = 0; i < numTestFiles; i++) {
            File file1 = new File(cacheDir, testFilePrefix+i+".txt");
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(file1);
                for(int k = 1; k < 10; k++) {
                    fos.write(buffer);
               }
                fos.close();
            } catch (FileNotFoundException e) {
                Log.i(TAG, "Exception ="+e);
                fail("Error when creating outputstream "+e);
            } catch(IOException e) {
                Log.i(TAG, "Exception ="+e);
                fail("Error when writing output "+e);
            }
            try {
                //introduce sleep for 1 s to avoid common time stamps for files being created
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                fail("Exception when sleeping "+e);
            }
        }
    }

    void verifyTestFiles1(File cacheDir, String testFilePrefix, int numTestFiles) {
        List<String> files = new ArrayList<String>();
        for(int i = 0; i < numTestFiles; i++) {
            File file1 = new File(cacheDir, testFilePrefix+i+".txt");
            if(file1.exists()) {
                files.add(file1.getName());
            }
        }
        if (files.size() > 0) {
            fail("Files should have been deleted: "
                    + Arrays.toString(files.toArray(new String[files.size()])));
        }
    }

    void createTestFiles2(File cacheDir, String rootTestDirName, String subDirPrefix, int numDirs, String testFilePrefix) {
        Context con = super.getContext();
        File testTmpDir = new File(cacheDir, rootTestDirName);
        testTmpDir.mkdir();
        File fileArr[] = new File[numDirs];
        for(int i = 0; i < numDirs; i++) {
            fileArr[i] = new File(testTmpDir, subDirPrefix+(i+1));
            fileArr[i].mkdir();
        }
        byte buffer[] = getBuffer();
        for(int i = 0; i < numDirs; i++) {
            for(int j = 1; j <= (i); j++) {
                File file1 = new File(fileArr[i], testFilePrefix+j+".txt");
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(file1);
                    for(int k = 1; k < 10; k++) {
                        fos.write(buffer);
                    }
                    fos.close();
                } catch (FileNotFoundException e) {
                    Log.i(TAG, "Exception ="+e);
                    fail("Error when creating outputstream "+e);
                } catch(IOException e) {
                    Log.i(TAG, "Exception ="+e);
                    fail("Error when writing output "+e);
                }
                try {
                    //introduce sleep for 10 ms to avoid common time stamps for files being created
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    fail("Exception when sleeping "+e);
                }
            }
        }
    }
    
    class PackageDataObserver extends IPackageDataObserver.Stub {
        public boolean retValue = false;
        private boolean doneFlag = false;
        public void onRemoveCompleted(String packageName, boolean succeeded)
                throws RemoteException {
            synchronized(this) {
                retValue = succeeded;
                doneFlag = true;
                notifyAll();
            }
        }
        public boolean isDone() {
            return doneFlag;
        }
    }
    
    IPackageManager getPm() {
        return  IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    }
    
    boolean invokePMDeleteAppCacheFiles() throws Exception {
        try {
            String packageName = mContext.getPackageName();
            PackageDataObserver observer = new PackageDataObserver();
            //wait on observer
            synchronized(observer) {
                getPm().deleteApplicationCacheFiles(packageName, observer);
                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 PackageDataObserver.onRemoveCompleted");
                }
            }
            return observer.retValue;
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
            return false;
        } catch (InterruptedException e) {
            Log.w(TAG, "InterruptedException :"+e);
            return false;
        }
    }
    
    boolean invokePMFreeApplicationCache(long idealStorageSize) throws Exception {
        try {
            String packageName = mContext.getPackageName();
            PackageDataObserver observer = new PackageDataObserver();
            //wait on observer
            synchronized(observer) {
                getPm().freeStorageAndNotify(null, idealStorageSize, observer);
                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 PackageDataObserver.onRemoveCompleted");
                }
            }
            return observer.retValue;
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
            return false;
        } catch (InterruptedException e) {
            Log.w(TAG, "InterruptedException :"+e);
            return false;
        }
    }

    boolean invokePMFreeStorage(long idealStorageSize, FreeStorageReceiver r, 
            PendingIntent pi) throws Exception {
        try {
            // Spin lock waiting for call back
            synchronized(r) {
                getPm().freeStorage(null, idealStorageSize, pi.getIntentSender());
                long waitTime = 0;
                while(!r.isDone() && (waitTime < MAX_WAIT_TIME)) {
                    r.wait(WAIT_TIME_INCR);
                    waitTime += WAIT_TIME_INCR;
                }
                if(!r.isDone()) {
                    throw new Exception("timed out waiting for call back from PendingIntent");
                }
            }
            return r.getResultCode() == 1;
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
            return false;
        } catch (InterruptedException e) {
            Log.w(TAG, "InterruptedException :"+e);
            return false;
        }
    }
    
    @LargeTest
    public void testDeleteAppCacheFiles() throws Exception {
        String testName="testDeleteAppCacheFiles";
        File cacheDir = mContext.getCacheDir();
        createTestFiles1(cacheDir, "testtmpdir", 5);
        assertTrue(invokePMDeleteAppCacheFiles());
        //confirm files dont exist
        verifyTestFiles1(cacheDir, "testtmpdir", 5);
    }

    class PackageStatsObserver extends IPackageStatsObserver.Stub {
        public boolean retValue = false;
        public PackageStats stats;
        private boolean doneFlag = false;
        
        public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
                throws RemoteException {
            synchronized(this) {
                retValue = succeeded;
                stats = pStats;
                doneFlag = true;
                notifyAll();
            }
        }
        public boolean isDone() {
            return doneFlag;
        }
    }
    
    public PackageStats invokePMGetPackageSizeInfo() throws Exception {
        try {
            String packageName = mContext.getPackageName();
            PackageStatsObserver observer = new PackageStatsObserver();
            //wait on observer
            synchronized(observer) {
                getPm().getPackageSizeInfo(packageName, UserHandle.myUserId(), observer);
                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 PackageStatsObserver.onGetStatsCompleted");
                }
            }
            if(localLOGV) Log.i(TAG, "OBSERVER RET VALUES code="+observer.stats.codeSize+
                    ", data="+observer.stats.dataSize+", cache="+observer.stats.cacheSize);
            return observer.stats;
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
            return null;
        } catch (InterruptedException e) {
            Log.w(TAG, "InterruptedException :"+e);
            return null;
        }
    }
    
    @SmallTest
    public void testGetPackageSizeInfo() throws Exception {
        String testName="testGetPackageSizeInfo";
        PackageStats stats = invokePMGetPackageSizeInfo();
        assertTrue(stats!=null);
        //confirm result
        if(localLOGV) Log.i(TAG, "code="+stats.codeSize+", data="+stats.dataSize+
                ", cache="+stats.cacheSize);
    }
    
    @SmallTest
    public void testGetSystemSharedLibraryNames() throws Exception {
        try {
            String[] sharedLibs = getPm().getSystemSharedLibraryNames();
            if (localLOGV) {
                for (String str : sharedLibs) {
                    Log.i(TAG, str);
                }
            }
        } catch (RemoteException e) {
            fail("Failed invoking getSystemSharedLibraryNames with exception:" + e);
        }   
    }
    
    class FreeStorageReceiver extends BroadcastReceiver {
        public static final String ACTION_FREE = "com.android.unit_tests.testcallback";
        private boolean doneFlag = false;
        
        public boolean isDone() {
            return doneFlag;
        }
        
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equalsIgnoreCase(ACTION_FREE)) {
                if (localLOGV) Log.i(TAG, "Got notification: clear cache succeeded "+getResultCode());
                synchronized (this) {
                    doneFlag = true;
                    notifyAll();
                }
            }
        }
    }
    
    // TODO: flaky test, omit from LargeTest for now
    //@LargeTest
    public void testFreeStorage() throws Exception {
        boolean TRACKING = true;
        StatFs st = new StatFs("/data");
        long blks1 = getFreeStorageBlks(st);
        if(localLOGV || TRACKING) Log.i(TAG, "Available free blocks="+blks1);
        long availableMem = getFreeStorageSize(st);
        File cacheDir = mContext.getCacheDir();
        assertNotNull(cacheDir);
        createTestFiles1(cacheDir, "testtmpdir", 5);
        long blks2 = getFreeStorageBlks(st);
        if(localLOGV || TRACKING) Log.i(TAG, "Available blocks after writing test files in application cache="+blks2);
        // Create receiver and register it
        FreeStorageReceiver receiver = new FreeStorageReceiver();
        mContext.registerReceiver(receiver, new IntentFilter(FreeStorageReceiver.ACTION_FREE));
        PendingIntent pi = PendingIntent.getBroadcast(mContext,
                0,  new Intent(FreeStorageReceiver.ACTION_FREE), 0);
        // Invoke PackageManager api
        if (!invokePMFreeStorage(availableMem, receiver, pi)) {
            fail("Could not invoke PackageManager free storage API");
        }
        long blks3 = getFreeStorageBlks(st);
        if(localLOGV || TRACKING) Log.i(TAG, "Available blocks after freeing cache"+blks3);
        assertEquals(receiver.getResultCode(), 1);
        mContext.unregisterReceiver(receiver);
        // Verify result  
        verifyTestFiles1(cacheDir, "testtmpdir", 5);
    }
    
    /* utility method used to create observer and check async call back from PackageManager.
     * ClearApplicationUserData
     */
    boolean invokePMClearApplicationUserData() throws Exception {
        try {
            String packageName = mContext.getPackageName();
            PackageDataObserver observer = new PackageDataObserver();
            //wait on observer
            synchronized(observer) {
                getPm().clearApplicationUserData(packageName, observer, 0 /* TODO: Other users */);
                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 PackageDataObserver.onRemoveCompleted");
                }
            }
            return observer.retValue;
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
            return false;
        } catch (InterruptedException e) {
            Log.w(TAG, "InterruptedException :"+e);
            return false;
        }
    }
    
    void verifyUserDataCleared(File pDir) {
        if(localLOGV) Log.i(TAG, "Verifying "+pDir);
        if(pDir == null) {
            return;
        }
        String fileList[] = pDir.list();
        if(fileList == null) {
            return;
        }
        int imax = fileList.length;
       //look recursively in user data dir
        for(int i = 0; i < imax; i++) {
            if(localLOGV) Log.i(TAG, "Found entry "+fileList[i]+ "in "+pDir);
            if("lib".equalsIgnoreCase(fileList[i])) {
                if(localLOGV) Log.i(TAG, "Ignoring lib directory");
                continue;
            }
            fail(pDir+" should be empty or contain only lib subdirectory. Found "+fileList[i]);
        }
    }
    
    File getDataDir() {
        try {
            ApplicationInfo appInfo = getPm().getApplicationInfo(mContext.getPackageName(), 0,
                    UserHandle.myUserId());
            return new File(appInfo.dataDir);
        } catch (RemoteException e) {
            throw new RuntimeException("Pacakge manager dead", e);
        }
    }
    
    @LargeTest
    public void testClearApplicationUserDataWithTestData() throws Exception {
        File cacheDir = mContext.getCacheDir();
        createTestFiles1(cacheDir, "testtmpdir", 5);
        if(localLOGV) {
            Log.i(TAG, "Created test data Waiting for 60seconds before continuing");
            Thread.sleep(60*1000);
        }
        assertTrue(invokePMClearApplicationUserData());
        //confirm files dont exist
        verifyUserDataCleared(getDataDir());
    }
    
    @SmallTest
    public void testClearApplicationUserDataWithNoTestData() throws Exception {
        assertTrue(invokePMClearApplicationUserData());
        //confirm files dont exist
        verifyUserDataCleared(getDataDir());
    }
    
    @LargeTest
    public void testClearApplicationUserDataNoObserver() throws Exception {
        getPm().clearApplicationUserData(mContext.getPackageName(), null, UserHandle.myUserId());
        //sleep for 1 minute
        Thread.sleep(60*1000);
        //confirm files dont exist
        verifyUserDataCleared(getDataDir());
    }
    
}
