Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c70d4a3 authored by Jeff Brown's avatar Jeff Brown
Browse files

Untangle MediaScanner error handling.

Bug: 5056917

Change-Id: I1a7a73579e3ba4e9709459329fc1901a28b0f4b1
parent cf4cfc6f
Loading
Loading
Loading
Loading
+26 −14
Original line number Diff line number Diff line
@@ -23,23 +23,33 @@
#include <utils/Errors.h>
#include <pthread.h>

struct dirent;

namespace android {

class MediaScannerClient;
class StringArray;

enum MediaScanResult {
    // This file or directory was scanned successfully.
    MEDIA_SCAN_RESULT_OK,
    // This file or directory was skipped because it was not found, could
    // not be opened, was of an unsupported type, or was malfored in some way.
    MEDIA_SCAN_RESULT_SKIPPED,
    // The scan should be aborted due to a fatal error such as out of memory
    // or an exception.
    MEDIA_SCAN_RESULT_ERROR,
};

struct MediaScanner {
    MediaScanner();
    virtual ~MediaScanner();

    virtual status_t processFile(
            const char *path, const char *mimeType,
            MediaScannerClient &client) = 0;
    virtual MediaScanResult processFile(
            const char *path, const char *mimeType, MediaScannerClient &client) = 0;

    typedef bool (*ExceptionCheck)(void* env);
    virtual status_t processDirectory(
            const char *path, MediaScannerClient &client,
            ExceptionCheck exceptionCheck, void *exceptionEnv);
    virtual MediaScanResult processDirectory(
            const char *path, MediaScannerClient &client);

    void setLocale(const char *locale);

@@ -53,9 +63,11 @@ private:
    // current locale (like "ja_JP"), created/destroyed with strdup()/free()
    char *mLocale;

    status_t doProcessDirectory(
            char *path, int pathRemaining, MediaScannerClient &client,
            bool noMedia, ExceptionCheck exceptionCheck, void *exceptionEnv);
    MediaScanResult doProcessDirectory(
            char *path, int pathRemaining, MediaScannerClient &client, bool noMedia);
    MediaScanResult doProcessDirectoryEntry(
            char *path, int pathRemaining, MediaScannerClient &client, bool noMedia,
            struct dirent* entry, char* fileSpot);

    MediaScanner(const MediaScanner &);
    MediaScanner &operator=(const MediaScanner &);
@@ -68,13 +80,13 @@ public:
    virtual ~MediaScannerClient();
    void setLocale(const char* locale);
    void beginFile();
    bool addStringTag(const char* name, const char* value);
    status_t addStringTag(const char* name, const char* value);
    void endFile();

    virtual bool scanFile(const char* path, long long lastModified,
    virtual status_t scanFile(const char* path, long long lastModified,
            long long fileSize, bool isDirectory, bool noMedia) = 0;
    virtual bool handleStringTag(const char* name, const char* value) = 0;
    virtual bool setMimeType(const char* mimeType) = 0;
    virtual status_t handleStringTag(const char* name, const char* value) = 0;
    virtual status_t setMimeType(const char* mimeType) = 0;

protected:
    void convertValues(uint32_t encoding);
+5 −1
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ struct StagefrightMediaScanner : public MediaScanner {
    StagefrightMediaScanner();
    virtual ~StagefrightMediaScanner();

    virtual status_t processFile(
    virtual MediaScanResult processFile(
            const char *path, const char *mimeType,
            MediaScannerClient &client);

@@ -35,6 +35,10 @@ struct StagefrightMediaScanner : public MediaScanner {
private:
    StagefrightMediaScanner(const StagefrightMediaScanner &);
    StagefrightMediaScanner &operator=(const StagefrightMediaScanner &);

    MediaScanResult processFileInternal(
            const char *path, const char *mimeType,
            MediaScannerClient &client);
};

}  // namespace android
+33 −24
Original line number Diff line number Diff line
@@ -46,6 +46,16 @@ struct fields_t {
};
static fields_t fields;

static status_t checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
    if (env->ExceptionCheck()) {
        LOGE("An exception was thrown by callback '%s'.", methodName);
        LOGE_EX(env);
        env->ExceptionClear();
        return UNKNOWN_ERROR;
    }
    return OK;
}

class MyMediaScannerClient : public MediaScannerClient
{
public:
@@ -86,9 +96,7 @@ public:
        mEnv->DeleteGlobalRef(mClient);
    }

    // Returns true if it succeeded, false if an exception occured
    // in the Java code
    virtual bool scanFile(const char* path, long long lastModified,
    virtual status_t scanFile(const char* path, long long lastModified,
            long long fileSize, bool isDirectory, bool noMedia)
    {
        LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
@@ -96,27 +104,29 @@ public:

        jstring pathStr;
        if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
            return false;
            mEnv->ExceptionClear();
            return NO_MEMORY;
        }

        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
                fileSize, isDirectory, noMedia);

        mEnv->DeleteLocalRef(pathStr);
        return (!mEnv->ExceptionCheck());
        return checkAndClearExceptionFromCallback(mEnv, "scanFile");
    }

    // Returns true if it succeeded, false if an exception occured
    // in the Java code
    virtual bool handleStringTag(const char* name, const char* value)
    virtual status_t handleStringTag(const char* name, const char* value)
    {
        LOGV("handleStringTag: name(%s) and value(%s)", name, value);
        jstring nameStr, valueStr;
        if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
            return false;
            mEnv->ExceptionClear();
            return NO_MEMORY;
        }
        if ((valueStr = mEnv->NewStringUTF(value)) == NULL) {
            return false;
            mEnv->DeleteLocalRef(nameStr);
            mEnv->ExceptionClear();
            return NO_MEMORY;
        }

        mEnv->CallVoidMethod(
@@ -124,23 +134,22 @@ public:

        mEnv->DeleteLocalRef(nameStr);
        mEnv->DeleteLocalRef(valueStr);
        return (!mEnv->ExceptionCheck());
        return checkAndClearExceptionFromCallback(mEnv, "handleStringTag");
    }

    // Returns true if it succeeded, false if an exception occured
    // in the Java code
    virtual bool setMimeType(const char* mimeType)
    virtual status_t setMimeType(const char* mimeType)
    {
        LOGV("setMimeType: %s", mimeType);
        jstring mimeTypeStr;
        if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
            return false;
            mEnv->ExceptionClear();
            return NO_MEMORY;
        }

        mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);

        mEnv->DeleteLocalRef(mimeTypeStr);
        return (!mEnv->ExceptionCheck());
        return checkAndClearExceptionFromCallback(mEnv, "setMimeType");
    }

private:
@@ -152,12 +161,6 @@ private:
};


static bool ExceptionCheck(void* env)
{
    LOGV("ExceptionCheck");
    return ((JNIEnv *)env)->ExceptionCheck();
}

static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz)
{
    return (MediaScanner *) env->GetIntField(thiz, fields.context);
@@ -190,7 +193,10 @@ android_media_MediaScanner_processDirectory(
    }

    MyMediaScannerClient myClient(env, client);
    mp->processDirectory(pathStr, myClient, ExceptionCheck, env);
    MediaScanResult result = mp->processDirectory(pathStr, myClient);
    if (result == MEDIA_SCAN_RESULT_ERROR) {
        LOGE("An error occurred while scanning directory '%s'.", pathStr);
    }
    env->ReleaseStringUTFChars(path, pathStr);
}

@@ -227,7 +233,10 @@ android_media_MediaScanner_processFile(
    }

    MyMediaScannerClient myClient(env, client);
    mp->processFile(pathStr, mimeTypeStr, myClient);
    MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
    if (result == MEDIA_SCAN_RESULT_ERROR) {
        LOGE("An error occurred while scanning file '%s'.", pathStr);
    }
    env->ReleaseStringUTFChars(path, pathStr);
    if (mimeType) {
        env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
+78 −70
Original line number Diff line number Diff line
@@ -47,16 +47,15 @@ const char *MediaScanner::locale() const {
    return mLocale;
}

status_t MediaScanner::processDirectory(
        const char *path, MediaScannerClient &client,
        ExceptionCheck exceptionCheck, void *exceptionEnv) {
MediaScanResult MediaScanner::processDirectory(
        const char *path, MediaScannerClient &client) {
    int pathLength = strlen(path);
    if (pathLength >= PATH_MAX) {
        return UNKNOWN_ERROR;
        return MEDIA_SCAN_RESULT_SKIPPED;
    }
    char* pathBuffer = (char *)malloc(PATH_MAX + 1);
    if (!pathBuffer) {
        return UNKNOWN_ERROR;
        return MEDIA_SCAN_RESULT_ERROR;
    }

    int pathRemaining = PATH_MAX - pathLength;
@@ -69,21 +68,18 @@ status_t MediaScanner::processDirectory(

    client.setLocale(locale());

    status_t result =
        doProcessDirectory(pathBuffer, pathRemaining, client, false, exceptionCheck, exceptionEnv);
    MediaScanResult result = doProcessDirectory(pathBuffer, pathRemaining, client, false);

    free(pathBuffer);

    return result;
}

status_t MediaScanner::doProcessDirectory(
        char *path, int pathRemaining, MediaScannerClient &client,
        bool noMedia, ExceptionCheck exceptionCheck, void *exceptionEnv) {
MediaScanResult MediaScanner::doProcessDirectory(
        char *path, int pathRemaining, MediaScannerClient &client, bool noMedia) {
    // place to copy file or directory name
    char* fileSpot = path + strlen(path);
    struct dirent* entry;
    struct stat statbuf;

    // Treat all files as non-media in directories that contain a  ".nomedia" file
    if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
@@ -99,22 +95,37 @@ status_t MediaScanner::doProcessDirectory(

    DIR* dir = opendir(path);
    if (!dir) {
        LOGD("opendir %s failed, errno: %d", path, errno);
        return UNKNOWN_ERROR;
        LOGW("Error opening directory '%s', skipping: %s.", path, strerror(errno));
        return MEDIA_SCAN_RESULT_SKIPPED;
    }

    MediaScanResult result = MEDIA_SCAN_RESULT_OK;
    while ((entry = readdir(dir))) {
        if (doProcessDirectoryEntry(path, pathRemaining, client, noMedia, entry, fileSpot)
                == MEDIA_SCAN_RESULT_ERROR) {
            result = MEDIA_SCAN_RESULT_ERROR;
            break;
        }
    }
    closedir(dir);
    return result;
}

MediaScanResult MediaScanner::doProcessDirectoryEntry(
        char *path, int pathRemaining, MediaScannerClient &client, bool noMedia,
        struct dirent* entry, char* fileSpot) {
    struct stat statbuf;
    const char* name = entry->d_name;

    // ignore "." and ".."
    if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
            continue;
        return MEDIA_SCAN_RESULT_SKIPPED;
    }

    int nameLength = strlen(name);
    if (nameLength + 1 > pathRemaining) {
        // path too long!
            continue;
        return MEDIA_SCAN_RESULT_SKIPPED;
    }
    strcpy(fileSpot, name);

@@ -133,7 +144,6 @@ status_t MediaScanner::doProcessDirectory(
            LOGD("stat() failed for %s: %s", path, strerror(errno) );
        }
    }
        if (type == DT_REG || type == DT_DIR) {
    if (type == DT_DIR) {
        bool childNoMedia = noMedia;
        // set noMedia flag on directories with a name that starts with '.'
@@ -143,32 +153,30 @@ status_t MediaScanner::doProcessDirectory(

        // report the directory to the client
        if (stat(path, &statbuf) == 0) {
                    client.scanFile(path, statbuf.st_mtime, 0, true, childNoMedia);
            status_t status = client.scanFile(path, statbuf.st_mtime, 0,
                    true /*isDirectory*/, childNoMedia);
            if (status) {
                return MEDIA_SCAN_RESULT_ERROR;
            }
        }

        // and now process its contents
        strcat(fileSpot, "/");
                int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client,
                        childNoMedia, exceptionCheck, exceptionEnv);
                if (err) {
                    // pass exceptions up - ignore other errors
                    if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
                    LOGE("Error processing '%s' - skipping\n", path);
                    continue;
        MediaScanResult result = doProcessDirectory(path, pathRemaining - nameLength - 1,
                client, childNoMedia);
        if (result == MEDIA_SCAN_RESULT_ERROR) {
            return MEDIA_SCAN_RESULT_ERROR;
        }
            } else {
    } else if (type == DT_REG) {
        stat(path, &statbuf);
                client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false, noMedia);
                if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
            }
        status_t status = client.scanFile(path, statbuf.st_mtime, statbuf.st_size,
                false /*isDirectory*/, noMedia);
        if (status) {
            return MEDIA_SCAN_RESULT_ERROR;
        }
    }

    closedir(dir);
    return OK;
failure:
    closedir(dir);
    return -1;
    return MEDIA_SCAN_RESULT_OK;
}

}  // namespace android
+4 −2
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ void MediaScannerClient::beginFile()
    mValues = new StringArray;
}

bool MediaScannerClient::addStringTag(const char* name, const char* value)
status_t MediaScannerClient::addStringTag(const char* name, const char* value)
{
    if (mLocaleEncoding != kEncodingNone) {
        // don't bother caching strings that are all ASCII.
@@ -212,10 +212,12 @@ void MediaScannerClient::endFile()

        // finally, push all name/value pairs to the client
        for (int i = 0; i < mNames->size(); i++) {
            if (!handleStringTag(mNames->getEntry(i), mValues->getEntry(i)))
            status_t status = handleStringTag(mNames->getEntry(i), mValues->getEntry(i));
            if (status) {
                break;
            }
        }
    }
    // else addStringTag() has done all the work so we have nothing to do

    delete mNames;
Loading