/*********************************************************************
 *  ____                      _____      _                           *
 * / ___|  ___  _ __  _   _  | ____|_ __(_) ___ ___ ___  ___  _ __   *
 * \___ \ / _ \| '_ \| | | | |  _| | '__| |/ __/ __/ __|/ _ \| '_ \  *
 *  ___) | (_) | | | | |_| | | |___| |  | | (__\__ \__ \ (_) | | | | *
 * |____/ \___/|_| |_|\__, | |_____|_|  |_|\___|___/___/\___/|_| |_| *
 *                    |___/                                          *
 *                                                                   *
 *********************************************************************
 * Copyright 2010 Sony Ericsson Mobile Communications AB.            *
 * All rights, including trade secret rights, reserved.              *
 *********************************************************************/

package com.android.mediaframeworktest.functional;

import java.io.IOException;
import java.io.RandomAccessFile;

import android.content.ContentProviderClient;
import android.content.Context;

import android.database.Cursor;
import android.drm.DrmConvertedStatus;
import android.drm.DrmManagerClient;
import android.drm.DrmRights;
import android.net.Uri;
import android.os.RemoteException;
import android.os.Environment;
import android.provider.MediaStore;
import android.test.ActivityInstrumentationTestCase2;

import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import com.android.mediaframeworktest.MediaFrameworkTest;

/*
 * tests for the Drm Media Scanner functionality.
 *
 * To run this test suite:
 *   make frameworks/base/media/tests/MediaFrameworkTest
 *   make mediaframeworktest
 *
 *   adb install -r out/target/product/dream/data/app/mediaframeworktest.apk
 *
 *   adb shell am instrument -e class \
 *   com.android.mediaframeworktest.functional.MediaScannerDrmTest \
 *   -w com.android.mediaframeworktest/.MediaFrameworkTestRunner
 *   make sure the test files resides in the dirrectory /mnt/sdcard/media_api/music
 *
 */
public class MediaScannerDrmTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
    private final String TAG = "MediaScannerDrmTest";
    private Context mContext;
    private DrmManagerClient drmManagerClient;

    private final String SD_MIME = "application/vnd.oma.drm.content";
    private final String DM_MIME = "application/vnd.oma.drm.message";
    private final String FL_MIME = "application/vnd.drm.semc.fl";

    private static final String EXT_MEM_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
    private String DM_FILE = "/media_api/music/drmfldm.dm";
    private String FL_FILE = "/media_api/music/drmfldm.fl";
    private String INVALIDFL_FILE = "/media_api/music/invalid.fl";

    private String SD_FILE = "/media_api/music/drmsd.dcf";
    private String RO_FILE = "/media_api/music/drmsdr.dr";
    private String SDNORO_FILE = "/media_api/music/drmsdnoro.dcf";
    private String SDCURROPT_FILE = "/media_api/music/curroptdcf.dcf";
    private String SDCURROPTRO_FILE = "/media_api/music/curroptdr.dr";

    DrmMediaScan drmMediaScan;

    /**
     * Converts a dm file and saves the result as fl. It calculates
     * the output filename itself and expects the extension to be
     * present in the file name.
     *
     * @param path dm file name with path
     */
    private boolean convertFLFile(String path) throws IOException  {

        // Converts a .dm file to the SEMC forward-lock file format
        final int readBuffSize = 8192;
        int returnedId = -1;
        boolean success = false;

        returnedId = drmManagerClient.openConvertSession(DM_MIME);

        if (returnedId < 0) {
            Log.e(TAG, "Convert Session ID is invalid");
            throw new IOException("openConvertSession Failed!!!");
        }

        RandomAccessFile dmFile = null;
        dmFile = new RandomAccessFile(path, "r");

        if (dmFile.length() == 0) {
            dmFile.close();
            return success;
        }

        long lastBufferSize = dmFile.length() % readBuffSize;
        long numberOfIterations = (dmFile.length() / readBuffSize) + 1;
        byte[] byteBuffer = new byte[readBuffSize];

        String newFilePath = path.substring(0, path.length() - 3);
        newFilePath += ".fl";

        RandomAccessFile flFile = new RandomAccessFile(newFilePath, "rw");

        flFile.getChannel().truncate(1);

        long newPositionWriteBuffer = 0;

        success = true;

        for (int i = 0; i < numberOfIterations; ++i) {
            if (i == (numberOfIterations - 1)) {
                if (lastBufferSize == 0) {
                    break;
                }
                byteBuffer = new byte[(int)lastBufferSize];
            }

            if(byteBuffer.length > dmFile.read(byteBuffer)) {
                success = false;
                break;
            }

            DrmConvertedStatus convStatus = drmManagerClient.convertData(returnedId, byteBuffer);

            if (convStatus != null) {
                if (convStatus.statusCode != DrmConvertedStatus.STATUS_OK) {
                    success = false;
                    break;
                }

                flFile.write(convStatus.convertedData);
                newPositionWriteBuffer += convStatus.convertedData.length;
            }
        }

        if(success) {
            success = false;
            DrmConvertedStatus closingStatus = null;
            closingStatus = drmManagerClient.closeConvertSession(returnedId);
            if (closingStatus != null) {
                if (closingStatus.statusCode == DrmConvertedStatus.STATUS_OK) {
                    if (closingStatus.convertedData != null) {
                        // write signature at last.
                        flFile.seek(closingStatus.offset);
                        flFile.write(closingStatus.convertedData);
                        success = true;
                    }
                }
            }
        }

        dmFile.close();
        flFile.close();

        return success;
    }

    /**
     * Extracts the Meta data from Media Store.
     * the output filename itself and expects the extension to be
     * present in the file name.
     *
     * @param uri uri of the content in media store
     * @param projection meta data to return (but it queries media store for all columns)
     */
    private String [] getMediaMetaData(Uri uri, String[] projection) {
        String [] retValues = null;

        ContentProviderClient mMediaProvider = mContext.getContentResolver().acquireContentProviderClient("media");
        Cursor cursor = null;

        //parameter check
        if(null == uri || null == projection) {
            return null;
        }

        // get all the values in the table.
        try {
            cursor = mMediaProvider.query(uri, null, null, null, null);
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        if (cursor != null && cursor.getCount() > 0 && projection != null) {
            int noOfItems = projection.length;
            if(noOfItems > 0) {
                int i = 0;
                retValues = new String[noOfItems];
                cursor.moveToFirst();
                for(i = 0; i < noOfItems; i++) {
                    int index = cursor.getColumnIndex(projection[i]);
                    if(-1 < index && index < cursor.getColumnCount()) {
                        retValues[i] = cursor.getString(index);
                    } else {
                        retValues[i] = null;
                        Log.e(TAG, "failed to get meta data for " + projection[i]);
                    }
                }
            }
        }

        return(retValues);
    }

    private void storeLicense(String path) {
        DrmRights rightToSave = null;

        if (path.endsWith(".dr")) {
            rightToSave = new DrmRights(path, "application/vnd.oma.drm.rights+xml");
        }
        else {
            rightToSave = new DrmRights(path, "application/vnd.oma.drm.rights+wbxml");
        }

        // Not needed to listen to any call back
        try {
            drmManagerClient.saveRights(rightToSave, "", "dummy");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public MediaScannerDrmTest() {
        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
        DM_FILE = EXT_MEM_PATH + DM_FILE;
        FL_FILE = EXT_MEM_PATH + FL_FILE;
        INVALIDFL_FILE = EXT_MEM_PATH + INVALIDFL_FILE;

        SD_FILE = EXT_MEM_PATH + SD_FILE;
        RO_FILE = EXT_MEM_PATH + RO_FILE;
        SDNORO_FILE = EXT_MEM_PATH + SDNORO_FILE;
        SDCURROPT_FILE = EXT_MEM_PATH + SDCURROPT_FILE;
        SDCURROPTRO_FILE = EXT_MEM_PATH + SDCURROPTRO_FILE;
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mContext = getActivity();
        drmMediaScan = new DrmMediaScan(mContext);
        drmManagerClient = new DrmManagerClient(mContext);
        drmManagerClient.loadPlugIns();

        // Checks you have all the test files on your SDCARD.
    }

    @Override
    protected void tearDown() throws Exception {
        drmManagerClient.unloadPlugIns();
        super.tearDown();
    }

    // ----------------------------------------------------------------------
    // AUDIO mime type resolution tests.

    @MediumTest
    // Converts a dm file to fl and scans it and test the data from Media Store
    public void testFLScanning() throws Exception {

        try {
            //Convert Forward Lock File
            if(!convertFLFile(DM_FILE)) {
                // Test Fail
                Log.e(TAG, "convertFLFile failed");
                assertTrue("testFLScanning: convertFLFile Failed", true);
                return;
            }
        }
        catch(IOException e) {
            // Test Fail
            Log.e(TAG, "convertFLFile threw IOException");
            assertTrue("testFLScanning: IOException from converFLFile", true);
            e.printStackTrace();
            return;
        }

        drmMediaScan.scanDrmFile(FL_FILE, FL_MIME);

        String projection [] = new String[] {
            MediaStore.Audio.Media.TITLE,
            MediaStore.Audio.Media.ARTIST
        };

        String metaData[] = getMediaMetaData(drmMediaScan.resultUri, projection);

        if(null == metaData || metaData.length < 2) {
            // Test case failed
            Log.e(TAG, "getMediaMetaData failed");
            assertTrue("testFLScanning: getMediaMetaData failed", true);
        }  else if((!metaData[0].equals("Alerter")) || (!metaData[1].equals("MicroSound"))) {
            // Test case failed
            Log.e(TAG, "Wrong meta Data returned");
            assertTrue("testFLScanning: Wrong Meta Data", true);
        }
    }

      @MediumTest
    // Scans an invalid forward lock file
    public void testFLInvalidScan() throws Exception {
        // Nothing can be obtained from the FL File since whole file cannot be opened.
        // just make sure scanning goes ok.
        drmMediaScan.scanDrmFile(INVALIDFL_FILE, FL_MIME);
    }

    @MediumTest
    // Scans a SD normal File. It stores RO, scans the file and then
    // compare the result with Media Store.
    public void testSDScanning() throws Exception {

        storeLicense(RO_FILE);

        drmMediaScan.scanDrmFile(SD_FILE, SD_MIME);

        // Not all values are verified.
        String projection [] = new String[] {
                MediaStore.Audio.Media.TITLE,
                MediaStore.Audio.Media.ARTIST
        };
        String metaData[] = getMediaMetaData(drmMediaScan.resultUri, projection);

        if(null == metaData || metaData.length < 2) {
            // Test case failed
            Log.e(TAG, "getMediaMetaData failed");
            assertTrue("testSDScanning: getMediaMetaData Failed", true);
        } else if((!metaData[0].equals("Alerter")) || (!metaData[1].equals("MicroSound"))) {
            // Test case failed
            Log.e(TAG, "Wrong meta Data returned");
            assertTrue("testSDScanning: Wrong Meta Data", true);
        }
    }

    @MediumTest
    // Scans a SD file without installing RO
    public void testSDNoRoScanning() throws Exception {
        drmMediaScan.scanDrmFile(SDNORO_FILE, SD_MIME);

        // Not all values are verified.
        String projection [] = new String[] {
                MediaStore.Audio.Media.TITLE,
                MediaStore.Audio.Media.ARTIST
        };

        String metaData[] = getMediaMetaData(drmMediaScan.resultUri, projection);

        if(null == metaData || metaData.length < 2) {
            // Test case failed
            Log.e(TAG, "getMediaMetaData failed");
            assertTrue("testSDNoRoScanning: getMediaMetaData Failed", true);
        } else if((!metaData[0].equals("musicFile")) || (!metaData[1].equals("SEMC"))) {
            // Test case failed
            Log.e(TAG, "Wrong meta Data returned");
            assertTrue("testSDNoRoScanning: Wrong Meta Data", true);
        }
    }

    @MediumTest
    // Scans a curropted SD file with RO installed
    public void testSDCurroptedScan() throws Exception {

        storeLicense(SDCURROPTRO_FILE);

        drmMediaScan.scanDrmFile(SDCURROPT_FILE, SD_MIME);

        // Not all values are verified.
        String projection [] = new String[] {
                MediaStore.Audio.Media.TITLE,
                MediaStore.Audio.Media.ARTIST
        };

        String metaData[] = getMediaMetaData(drmMediaScan.resultUri, projection);

        // No Artis etc. will be found since data part is curropted but dcf header is intact
        if(null == metaData) {
            // Test case failed
            assertTrue("testSDNoRoScanning: getMediaMetaData Failed", true);
        }
    }


}
