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

Commit 48a047b3 authored by Kenny Root's avatar Kenny Root Committed by Android Git Automerger
Browse files

am c9535de8: Merge "Add OBB file helper class" into gingerbread

Merge commit 'c9535de8e398d766c95144f9a831f34b2cecfd91' into gingerbread-plus-aosp

* commit 'c9535de8e398d766c95144f9a831f34b2cecfd91':
  Add OBB file helper class
parents 651056ff 47ecb07e
Loading
Loading
Loading
Loading
+87 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 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.
 */

#ifndef OBBFILE_H_
#define OBBFILE_H_

#include <stdint.h>

#include <utils/RefBase.h>
#include <utils/String8.h>

namespace android {

class ObbFile : public RefBase {
protected:
    virtual ~ObbFile();

public:
    ObbFile();

    bool readFrom(const char* filename);
    bool readFrom(int fd);
    bool writeTo(const char* filename);
    bool writeTo(int fd);

    const char* getFileName() const {
        return mFileName;
    }

    const String8 getPackageName() const {
        return mPackageName;
    }

    int32_t getVersion() const {
        return mVersion;
    }

    void setPackageName(String8 packageName) {
        mPackageName = packageName;
    }

    void setVersion(int32_t version) {
        mVersion = version;
    }

    static inline uint32_t get4LE(const unsigned char* buf) {
        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
    }

    static inline void put4LE(unsigned char* buf, uint32_t val) {
        buf[0] = val & 0xFF;
        buf[1] = (val >> 8) & 0xFF;
        buf[2] = (val >> 16) & 0xFF;
        buf[3] = (val >> 24) & 0xFF;
    }

private:
    /* Package name this ObbFile is associated with */
    String8 mPackageName;

    /* Package version this ObbFile is associated with */
    int32_t mVersion;

    const char* mFileName;

    size_t mFileSize;

    unsigned char* mReadBuf;

    bool parseObbFile(int fd);
};

}
#endif /* OBBFILE_H_ */
+6 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ commonSources:= \
	Debug.cpp \
	FileMap.cpp \
	Flattenable.cpp \
	ObbFile.cpp \
	Pool.cpp \
	RefBase.cpp \
	ResourceTypes.cpp \
@@ -65,6 +66,11 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1
endif
endif

ifeq ($(HOST_OS),darwin)
# MacOS doesn't have lseek64. However, off_t is 64-bit anyway.
LOCAL_CFLAGS += -DOFF_T_IS_64_BIT
endif

include $(BUILD_HOST_STATIC_LIBRARY)


libs/utils/ObbFile.cpp

0 → 100644
+284 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 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 <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define LOG_TAG "ObbFile"
#include <utils/Log.h>
#include <utils/ObbFile.h>

//#define DEBUG 1

#define kFooterTagSize 8  /* last two 32-bit integers */

#define kFooterMinSize 21 /* 32-bit signature version
                           * 32-bit package version
                           * 32-bit package name size
                           * 1-character package name
                           * 32-bit footer size
                           * 32-bit footer marker
                           */

#define kMaxBufSize    32768 /* Maximum file read buffer */

#define kSignature     0x01059983U /* ObbFile signature */

#define kSigVersion    1 /* We only know about signature version 1 */

/* offsets in version 1 of the header */
#define kPackageVersionOffset 4
#define kPackageNameLenOffset 8
#define kPackageNameOffset    12

/*
 * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
 * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
 * not already defined, then define it here.
 */
#ifndef TEMP_FAILURE_RETRY
/* Used to retry syscalls that can return EINTR. */
#define TEMP_FAILURE_RETRY(exp) ({         \
    typeof (exp) _rc;                      \
    do {                                   \
        _rc = (exp);                       \
    } while (_rc == -1 && errno == EINTR); \
    _rc; })
#endif

/*
 * Work around situations where off_t is 64-bit and use off64_t in
 * situations where it's 32-bit.
 */
#ifdef OFF_T_IS_64_BIT
#define my_lseek64 lseek
typedef off_t my_off64_t;
#else
#define my_lseek64 lseek64
typedef off64_t my_off64_t;
#endif

namespace android {

ObbFile::ObbFile() :
        mVersion(-1) {
}

ObbFile::~ObbFile() {
}

bool ObbFile::readFrom(const char* filename)
{
    int fd;
    bool success = false;

    fd = ::open(filename, O_RDONLY);
    if (fd < 0) {
        goto out;
    }
    success = readFrom(fd);
    close(fd);

out:
    if (!success) {
        LOGW("failed to read from %s\n", filename);
    }
    return success;
}

bool ObbFile::readFrom(int fd)
{
    if (fd < 0) {
        LOGW("failed to read file\n");
        return false;
    }

    return parseObbFile(fd);
}

bool ObbFile::parseObbFile(int fd)
{
    my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END);

    if (fileLength < kFooterMinSize) {
        if (fileLength < 0) {
            LOGW("error seeking in ObbFile: %s\n", strerror(errno));
        } else {
            LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize);
        }
        return false;
    }

    ssize_t actual;
    size_t footerSize;

    {
        my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET);

        char *footer = new char[kFooterTagSize];
        actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize));
        if (actual != kFooterTagSize) {
            LOGW("couldn't read footer signature: %s\n", strerror(errno));
            return false;
        }

        unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t));
        if (fileSig != kSignature) {
            LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n",
                    kSignature, fileSig);
            return false;
        }

        footerSize = get4LE((unsigned char*)footer);
        if (footerSize > (size_t)fileLength - kFooterTagSize
                || footerSize > kMaxBufSize) {
            LOGW("claimed footer size is too large (0x%08lx; file size is 0x%08llx)\n",
                    footerSize, fileLength);
            return false;
        }
    }

    my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize;
    if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) {
        LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno));
        return false;
    }

    size_t readAmount = kMaxBufSize;
    if (readAmount > footerSize)
        readAmount = footerSize;

    char* scanBuf = (char*)malloc(readAmount);
    if (scanBuf == NULL) {
        LOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
        return false;
    }

    actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount));
    // readAmount is guaranteed to be less than kMaxBufSize
    if (actual != (ssize_t)readAmount) {
        LOGI("couldn't read ObbFile footer: %s\n", strerror(errno));
        free(scanBuf);
        return false;
    }

#ifdef DEBUG
    for (int i = 0; i < readAmount; ++i) {
        LOGI("char: 0x%02x", scanBuf[i]);
    }
#endif

    uint32_t sigVersion = get4LE((unsigned char*)scanBuf);
    if (sigVersion != kSigVersion) {
        LOGW("Unsupported ObbFile version %d\n", sigVersion);
        free(scanBuf);
        return false;
    }

    mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);

    uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
    if (packageNameLen <= 0
            || packageNameLen > (footerSize - kPackageNameOffset)) {
        LOGW("bad ObbFile package name length (0x%08x)\n", packageNameLen);
        free(scanBuf);
        return false;
    }

    char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset);
    mPackageName = String8(const_cast<char*>(packageName), packageNameLen);

    free(scanBuf);
    return true;
}

bool ObbFile::writeTo(const char* filename)
{
    int fd;
    bool success = false;

    fd = ::open(filename, O_WRONLY);
    if (fd < 0) {
        goto out;
    }
    success = writeTo(fd);
    close(fd);

out:
    if (!success) {
        LOGW("failed to write to %s: %s\n", filename, strerror(errno));
    }
    return success;
}

bool ObbFile::writeTo(int fd)
{
    if (fd < 0) {
        return false;
    }

    if (mPackageName.size() == 0 || mVersion == -1) {
        LOGW("tried to write uninitialized ObbFile data");
        return false;
    }

    unsigned char intBuf[sizeof(uint32_t)+1];
    memset(&intBuf, 0, sizeof(intBuf));

    put4LE(intBuf, kSigVersion);
    if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
        LOGW("couldn't write signature version: %s", strerror(errno));
        return false;
    }

    put4LE(intBuf, mVersion);
    if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
        LOGW("couldn't write package version");
        return false;
    }

    size_t packageNameLen = mPackageName.size();
    put4LE(intBuf, packageNameLen);
    if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
        LOGW("couldn't write package name length: %s", strerror(errno));
        return false;
    }

    if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
        LOGW("couldn't write package name: %s", strerror(errno));
        return false;
    }

    put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen);
    if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
        LOGW("couldn't write footer size: %s", strerror(errno));
        return false;
    }

    put4LE(intBuf, kSignature);
    if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
        LOGW("couldn't write footer magic signature: %s", strerror(errno));
        return false;
    }

    return true;
}

}
+1 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

test_src_files := \
	ObbFile_test.cpp \
	PollLoop_test.cpp

shared_libraries := \
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 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.
 */

#define LOG_TAG "ObbFile_test"
#include <utils/Log.h>
#include <utils/ObbFile.h>
#include <utils/RefBase.h>
#include <utils/String8.h>

#include <gtest/gtest.h>

namespace android {

#define TEST_FILENAME "/test.obb"

class ObbFileTest : public testing::Test {
protected:
    sp<ObbFile> mObbFile;
    char* mExternalStorage;
    char* mFileName;

    virtual void SetUp() {
        mObbFile = new ObbFile();
        mExternalStorage = getenv("EXTERNAL_STORAGE");

        const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1;
        mFileName = new char[totalLen];
        snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME);
    }

    virtual void TearDown() {
    }
};

TEST_F(ObbFileTest, ReadFailure) {
	EXPECT_FALSE(mObbFile->readFrom(-1))
			<< "No failure on invalid file descriptor";
}

TEST_F(ObbFileTest, WriteThenRead) {
    const char* packageName = "com.example.obbfile";
    const int32_t versionNum = 1;

    mObbFile->setPackageName(String8(packageName));
    mObbFile->setVersion(versionNum);

    EXPECT_TRUE(mObbFile->writeTo(mFileName))
            << "couldn't write to fake .obb file";

    mObbFile = new ObbFile();

    EXPECT_TRUE(mObbFile->readFrom(mFileName))
            << "couldn't read from fake .obb file";

    EXPECT_EQ(versionNum, mObbFile->getVersion())
			<< "version didn't come out the same as it went in";
    const char* currentPackageName = mObbFile->getPackageName().string();
    EXPECT_STREQ(packageName, currentPackageName)
			<< "package name didn't come out the same as it went in";
}

}