Moved androidfw back to frameworks/base
Change-Id: Idd57bbbc86c936cbfd60e02f8ac8d4f8fbc94592
This commit is contained in:
parent
a9d5701b03
commit
809af3aa03
@ -1,322 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Class providing access to a read-only asset. Asset objects are NOT
|
||||
// thread-safe, and should not be shared across threads.
|
||||
//
|
||||
#ifndef __LIBS_ASSET_H
|
||||
#define __LIBS_ASSET_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utils/Compat.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/FileMap.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Instances of this class provide read-only operations on a byte stream.
|
||||
*
|
||||
* Access may be optimized for streaming, random, or whole buffer modes. All
|
||||
* operations are supported regardless of how the file was opened, but some
|
||||
* things will be less efficient. [pass that in??]
|
||||
*
|
||||
* "Asset" is the base class for all types of assets. The classes below
|
||||
* provide most of the implementation. The AssetManager uses one of the
|
||||
* static "create" functions defined here to create a new instance.
|
||||
*/
|
||||
class Asset {
|
||||
public:
|
||||
virtual ~Asset(void);
|
||||
|
||||
static int32_t getGlobalCount();
|
||||
static String8 getAssetAllocations();
|
||||
|
||||
/* used when opening an asset */
|
||||
typedef enum AccessMode {
|
||||
ACCESS_UNKNOWN = 0,
|
||||
|
||||
/* read chunks, and seek forward and backward */
|
||||
ACCESS_RANDOM,
|
||||
|
||||
/* read sequentially, with an occasional forward seek */
|
||||
ACCESS_STREAMING,
|
||||
|
||||
/* caller plans to ask for a read-only buffer with all data */
|
||||
ACCESS_BUFFER,
|
||||
} AccessMode;
|
||||
|
||||
/*
|
||||
* Read data from the current offset. Returns the actual number of
|
||||
* bytes read, 0 on EOF, or -1 on error.
|
||||
*/
|
||||
virtual ssize_t read(void* buf, size_t count) = 0;
|
||||
|
||||
/*
|
||||
* Seek to the specified offset. "whence" uses the same values as
|
||||
* lseek/fseek. Returns the new position on success, or (off64_t) -1
|
||||
* on failure.
|
||||
*/
|
||||
virtual off64_t seek(off64_t offset, int whence) = 0;
|
||||
|
||||
/*
|
||||
* Close the asset, freeing all associated resources.
|
||||
*/
|
||||
virtual void close(void) = 0;
|
||||
|
||||
/*
|
||||
* Get a pointer to a buffer with the entire contents of the file.
|
||||
*/
|
||||
virtual const void* getBuffer(bool wordAligned) = 0;
|
||||
|
||||
/*
|
||||
* Get the total amount of data that can be read.
|
||||
*/
|
||||
virtual off64_t getLength(void) const = 0;
|
||||
|
||||
/*
|
||||
* Get the total amount of data that can be read from the current position.
|
||||
*/
|
||||
virtual off64_t getRemainingLength(void) const = 0;
|
||||
|
||||
/*
|
||||
* Open a new file descriptor that can be used to read this asset.
|
||||
* Returns -1 if you can not use the file descriptor (for example if the
|
||||
* asset is compressed).
|
||||
*/
|
||||
virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const = 0;
|
||||
|
||||
/*
|
||||
* Return whether this asset's buffer is allocated in RAM (not mmapped).
|
||||
* Note: not virtual so it is safe to call even when being destroyed.
|
||||
*/
|
||||
virtual bool isAllocated(void) const { return false; }
|
||||
|
||||
/*
|
||||
* Get a string identifying the asset's source. This might be a full
|
||||
* path, it might be a colon-separated list of identifiers.
|
||||
*
|
||||
* This is NOT intended to be used for anything except debug output.
|
||||
* DO NOT try to parse this or use it to open a file.
|
||||
*/
|
||||
const char* getAssetSource(void) const { return mAssetSource.string(); }
|
||||
|
||||
protected:
|
||||
Asset(void); // constructor; only invoked indirectly
|
||||
|
||||
/* handle common seek() housekeeping */
|
||||
off64_t handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn);
|
||||
|
||||
/* set the asset source string */
|
||||
void setAssetSource(const String8& path) { mAssetSource = path; }
|
||||
|
||||
AccessMode getAccessMode(void) const { return mAccessMode; }
|
||||
|
||||
private:
|
||||
/* these operations are not implemented */
|
||||
Asset(const Asset& src);
|
||||
Asset& operator=(const Asset& src);
|
||||
|
||||
/* AssetManager needs access to our "create" functions */
|
||||
friend class AssetManager;
|
||||
|
||||
/*
|
||||
* Create the asset from a named file on disk.
|
||||
*/
|
||||
static Asset* createFromFile(const char* fileName, AccessMode mode);
|
||||
|
||||
/*
|
||||
* Create the asset from a named, compressed file on disk (e.g. ".gz").
|
||||
*/
|
||||
static Asset* createFromCompressedFile(const char* fileName,
|
||||
AccessMode mode);
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Create the asset from a segment of an open file. This will fail
|
||||
* if "offset" and "length" don't fit within the bounds of the file.
|
||||
*
|
||||
* The asset takes ownership of the file descriptor.
|
||||
*/
|
||||
static Asset* createFromFileSegment(int fd, off64_t offset, size_t length,
|
||||
AccessMode mode);
|
||||
|
||||
/*
|
||||
* Create from compressed data. "fd" should be seeked to the start of
|
||||
* the compressed data. This could be inside a gzip file or part of a
|
||||
* Zip archive.
|
||||
*
|
||||
* The asset takes ownership of the file descriptor.
|
||||
*
|
||||
* This may not verify the validity of the compressed data until first
|
||||
* use.
|
||||
*/
|
||||
static Asset* createFromCompressedData(int fd, off64_t offset,
|
||||
int compressionMethod, size_t compressedLength,
|
||||
size_t uncompressedLength, AccessMode mode);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create the asset from a memory-mapped file segment.
|
||||
*
|
||||
* The asset takes ownership of the FileMap.
|
||||
*/
|
||||
static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode);
|
||||
|
||||
/*
|
||||
* Create the asset from a memory-mapped file segment with compressed
|
||||
* data. "method" is a Zip archive compression method constant.
|
||||
*
|
||||
* The asset takes ownership of the FileMap.
|
||||
*/
|
||||
static Asset* createFromCompressedMap(FileMap* dataMap, int method,
|
||||
size_t uncompressedLen, AccessMode mode);
|
||||
|
||||
|
||||
/*
|
||||
* Create from a reference-counted chunk of shared memory.
|
||||
*/
|
||||
// TODO
|
||||
|
||||
AccessMode mAccessMode; // how the asset was opened
|
||||
String8 mAssetSource; // debug string
|
||||
|
||||
Asset* mNext; // linked list.
|
||||
Asset* mPrev;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
*
|
||||
* Innards follow. Do not use these classes directly.
|
||||
*/
|
||||
|
||||
/*
|
||||
* An asset based on an uncompressed file on disk. It may encompass the
|
||||
* entire file or just a piece of it. Access is through fread/fseek.
|
||||
*/
|
||||
class _FileAsset : public Asset {
|
||||
public:
|
||||
_FileAsset(void);
|
||||
virtual ~_FileAsset(void);
|
||||
|
||||
/*
|
||||
* Use a piece of an already-open file.
|
||||
*
|
||||
* On success, the object takes ownership of "fd".
|
||||
*/
|
||||
status_t openChunk(const char* fileName, int fd, off64_t offset, size_t length);
|
||||
|
||||
/*
|
||||
* Use a memory-mapped region.
|
||||
*
|
||||
* On success, the object takes ownership of "dataMap".
|
||||
*/
|
||||
status_t openChunk(FileMap* dataMap);
|
||||
|
||||
/*
|
||||
* Standard Asset interfaces.
|
||||
*/
|
||||
virtual ssize_t read(void* buf, size_t count);
|
||||
virtual off64_t seek(off64_t offset, int whence);
|
||||
virtual void close(void);
|
||||
virtual const void* getBuffer(bool wordAligned);
|
||||
virtual off64_t getLength(void) const { return mLength; }
|
||||
virtual off64_t getRemainingLength(void) const { return mLength-mOffset; }
|
||||
virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const;
|
||||
virtual bool isAllocated(void) const { return mBuf != NULL; }
|
||||
|
||||
private:
|
||||
off64_t mStart; // absolute file offset of start of chunk
|
||||
off64_t mLength; // length of the chunk
|
||||
off64_t mOffset; // current local offset, 0 == mStart
|
||||
FILE* mFp; // for read/seek
|
||||
char* mFileName; // for opening
|
||||
|
||||
/*
|
||||
* To support getBuffer() we either need to read the entire thing into
|
||||
* a buffer or memory-map it. For small files it's probably best to
|
||||
* just read them in.
|
||||
*/
|
||||
enum { kReadVsMapThreshold = 4096 };
|
||||
|
||||
FileMap* mMap; // for memory map
|
||||
unsigned char* mBuf; // for read
|
||||
|
||||
const void* ensureAlignment(FileMap* map);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* An asset based on compressed data in a file.
|
||||
*/
|
||||
class _CompressedAsset : public Asset {
|
||||
public:
|
||||
_CompressedAsset(void);
|
||||
virtual ~_CompressedAsset(void);
|
||||
|
||||
/*
|
||||
* Use a piece of an already-open file.
|
||||
*
|
||||
* On success, the object takes ownership of "fd".
|
||||
*/
|
||||
status_t openChunk(int fd, off64_t offset, int compressionMethod,
|
||||
size_t uncompressedLen, size_t compressedLen);
|
||||
|
||||
/*
|
||||
* Use a memory-mapped region.
|
||||
*
|
||||
* On success, the object takes ownership of "fd".
|
||||
*/
|
||||
status_t openChunk(FileMap* dataMap, int compressionMethod,
|
||||
size_t uncompressedLen);
|
||||
|
||||
/*
|
||||
* Standard Asset interfaces.
|
||||
*/
|
||||
virtual ssize_t read(void* buf, size_t count);
|
||||
virtual off64_t seek(off64_t offset, int whence);
|
||||
virtual void close(void);
|
||||
virtual const void* getBuffer(bool wordAligned);
|
||||
virtual off64_t getLength(void) const { return mUncompressedLen; }
|
||||
virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; }
|
||||
virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const { return -1; }
|
||||
virtual bool isAllocated(void) const { return mBuf != NULL; }
|
||||
|
||||
private:
|
||||
off64_t mStart; // offset to start of compressed data
|
||||
off64_t mCompressedLen; // length of the compressed data
|
||||
off64_t mUncompressedLen; // length of the uncompressed data
|
||||
off64_t mOffset; // current offset, 0 == start of uncomp data
|
||||
|
||||
FileMap* mMap; // for memory-mapped input
|
||||
int mFd; // for file input
|
||||
|
||||
class StreamingZipInflater* mZipInflater; // for streaming large compressed assets
|
||||
|
||||
unsigned char* mBuf; // for getBuffer()
|
||||
};
|
||||
|
||||
// need: shared mmap version?
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // __LIBS_ASSET_H
|
@ -1,145 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Access a chunk of the asset hierarchy as if it were a single directory.
|
||||
//
|
||||
#ifndef __LIBS_ASSETDIR_H
|
||||
#define __LIBS_ASSETDIR_H
|
||||
|
||||
#include <androidfw/misc.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/SortedVector.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* This provides vector-style access to a directory. We do this rather
|
||||
* than modeling opendir/readdir access because it's simpler and the
|
||||
* nature of the operation requires us to have all data on hand anyway.
|
||||
*
|
||||
* The list of files will be sorted in ascending order by ASCII value.
|
||||
*
|
||||
* The contents are populated by our friend, the AssetManager.
|
||||
*/
|
||||
class AssetDir {
|
||||
public:
|
||||
AssetDir(void)
|
||||
: mFileInfo(NULL)
|
||||
{}
|
||||
virtual ~AssetDir(void) {
|
||||
delete mFileInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Vector-style access.
|
||||
*/
|
||||
size_t getFileCount(void) { return mFileInfo->size(); }
|
||||
const String8& getFileName(int idx) {
|
||||
return mFileInfo->itemAt(idx).getFileName();
|
||||
}
|
||||
const String8& getSourceName(int idx) {
|
||||
return mFileInfo->itemAt(idx).getSourceName();
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the type of a file (usually regular or directory).
|
||||
*/
|
||||
FileType getFileType(int idx) {
|
||||
return mFileInfo->itemAt(idx).getFileType();
|
||||
}
|
||||
|
||||
private:
|
||||
/* these operations are not implemented */
|
||||
AssetDir(const AssetDir& src);
|
||||
const AssetDir& operator=(const AssetDir& src);
|
||||
|
||||
friend class AssetManager;
|
||||
|
||||
/*
|
||||
* This holds information about files in the asset hierarchy.
|
||||
*/
|
||||
class FileInfo {
|
||||
public:
|
||||
FileInfo(void) {}
|
||||
FileInfo(const String8& path) // useful for e.g. svect.indexOf
|
||||
: mFileName(path), mFileType(kFileTypeUnknown)
|
||||
{}
|
||||
~FileInfo(void) {}
|
||||
FileInfo(const FileInfo& src) {
|
||||
copyMembers(src);
|
||||
}
|
||||
const FileInfo& operator= (const FileInfo& src) {
|
||||
if (this != &src)
|
||||
copyMembers(src);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void copyMembers(const FileInfo& src) {
|
||||
mFileName = src.mFileName;
|
||||
mFileType = src.mFileType;
|
||||
mSourceName = src.mSourceName;
|
||||
}
|
||||
|
||||
/* need this for SortedVector; must compare only on file name */
|
||||
bool operator< (const FileInfo& rhs) const {
|
||||
return mFileName < rhs.mFileName;
|
||||
}
|
||||
|
||||
/* used by AssetManager */
|
||||
bool operator== (const FileInfo& rhs) const {
|
||||
return mFileName == rhs.mFileName;
|
||||
}
|
||||
|
||||
void set(const String8& path, FileType type) {
|
||||
mFileName = path;
|
||||
mFileType = type;
|
||||
}
|
||||
|
||||
const String8& getFileName(void) const { return mFileName; }
|
||||
void setFileName(const String8& path) { mFileName = path; }
|
||||
|
||||
FileType getFileType(void) const { return mFileType; }
|
||||
void setFileType(FileType type) { mFileType = type; }
|
||||
|
||||
const String8& getSourceName(void) const { return mSourceName; }
|
||||
void setSourceName(const String8& path) { mSourceName = path; }
|
||||
|
||||
/*
|
||||
* Handy utility for finding an entry in a sorted vector of FileInfo.
|
||||
* Returns the index of the matching entry, or -1 if none found.
|
||||
*/
|
||||
static int findEntry(const SortedVector<FileInfo>* pVector,
|
||||
const String8& fileName);
|
||||
|
||||
private:
|
||||
String8 mFileName; // filename only
|
||||
FileType mFileType; // regular, directory, etc
|
||||
|
||||
String8 mSourceName; // currently debug-only
|
||||
};
|
||||
|
||||
/* AssetManager uses this to initialize us */
|
||||
void setFileList(SortedVector<FileInfo>* list) { mFileInfo = list; }
|
||||
|
||||
SortedVector<FileInfo>* mFileInfo;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // __LIBS_ASSETDIR_H
|
@ -1,374 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Asset management class. AssetManager objects are thread-safe.
|
||||
//
|
||||
#ifndef __LIBS_ASSETMANAGER_H
|
||||
#define __LIBS_ASSETMANAGER_H
|
||||
|
||||
#include <androidfw/Asset.h>
|
||||
#include <androidfw/AssetDir.h>
|
||||
#include <androidfw/ZipFileRO.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/SortedVector.h>
|
||||
#include <utils/String16.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/threads.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
/*
|
||||
* Native-app access is via the opaque typedef struct AAssetManager in the C namespace.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct AAssetManager { };
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Now the proper C++ android-namespace definitions
|
||||
*/
|
||||
|
||||
namespace android {
|
||||
|
||||
class Asset; // fwd decl for things that include Asset.h first
|
||||
class ResTable;
|
||||
struct ResTable_config;
|
||||
|
||||
/*
|
||||
* Every application that uses assets needs one instance of this. A
|
||||
* single instance may be shared across multiple threads, and a single
|
||||
* thread may have more than one instance (the latter is discouraged).
|
||||
*
|
||||
* The purpose of the AssetManager is to create Asset objects. To do
|
||||
* this efficiently it may cache information about the locations of
|
||||
* files it has seen. This can be controlled with the "cacheMode"
|
||||
* argument.
|
||||
*
|
||||
* The asset hierarchy may be examined like a filesystem, using
|
||||
* AssetDir objects to peruse a single directory.
|
||||
*/
|
||||
class AssetManager : public AAssetManager {
|
||||
public:
|
||||
typedef enum CacheMode {
|
||||
CACHE_UNKNOWN = 0,
|
||||
CACHE_OFF, // don't try to cache file locations
|
||||
CACHE_DEFER, // construct cache as pieces are needed
|
||||
//CACHE_SCAN, // scan full(!) asset hierarchy at init() time
|
||||
} CacheMode;
|
||||
|
||||
AssetManager(CacheMode cacheMode = CACHE_OFF);
|
||||
virtual ~AssetManager(void);
|
||||
|
||||
static int32_t getGlobalCount();
|
||||
|
||||
/*
|
||||
* Add a new source for assets. This can be called multiple times to
|
||||
* look in multiple places for assets. It can be either a directory (for
|
||||
* finding assets as raw files on the disk) or a ZIP file. This newly
|
||||
* added asset path will be examined first when searching for assets,
|
||||
* before any that were previously added.
|
||||
*
|
||||
* Returns "true" on success, "false" on failure. If 'cookie' is non-NULL,
|
||||
* then on success, *cookie is set to the value corresponding to the
|
||||
* newly-added asset source.
|
||||
*/
|
||||
bool addAssetPath(const String8& path, int32_t* cookie);
|
||||
|
||||
/*
|
||||
* Convenience for adding the standard system assets. Uses the
|
||||
* ANDROID_ROOT environment variable to find them.
|
||||
*/
|
||||
bool addDefaultAssets();
|
||||
|
||||
/*
|
||||
* Iterate over the asset paths in this manager. (Previously
|
||||
* added via addAssetPath() and addDefaultAssets().) On first call,
|
||||
* 'cookie' must be 0, resulting in the first cookie being returned.
|
||||
* Each next cookie will be returned there-after, until -1 indicating
|
||||
* the end has been reached.
|
||||
*/
|
||||
int32_t nextAssetPath(const int32_t cookie) const;
|
||||
|
||||
/*
|
||||
* Return an asset path in the manager. 'which' must be between 0 and
|
||||
* countAssetPaths().
|
||||
*/
|
||||
String8 getAssetPath(const int32_t cookie) const;
|
||||
|
||||
/*
|
||||
* Set the current locale and vendor. The locale can change during
|
||||
* the lifetime of an AssetManager if the user updates the device's
|
||||
* language setting. The vendor is less likely to change.
|
||||
*
|
||||
* Pass in NULL to indicate no preference.
|
||||
*/
|
||||
void setLocale(const char* locale);
|
||||
void setVendor(const char* vendor);
|
||||
|
||||
/*
|
||||
* Choose screen orientation for resources values returned.
|
||||
*/
|
||||
void setConfiguration(const ResTable_config& config, const char* locale = NULL);
|
||||
|
||||
void getConfiguration(ResTable_config* outConfig) const;
|
||||
|
||||
typedef Asset::AccessMode AccessMode; // typing shortcut
|
||||
|
||||
/*
|
||||
* Open an asset.
|
||||
*
|
||||
* This will search through locale-specific and vendor-specific
|
||||
* directories and packages to find the file.
|
||||
*
|
||||
* The object returned does not depend on the AssetManager. It should
|
||||
* be freed by calling Asset::close().
|
||||
*/
|
||||
Asset* open(const char* fileName, AccessMode mode);
|
||||
|
||||
/*
|
||||
* Open a non-asset file as an asset.
|
||||
*
|
||||
* This is for opening files that are included in an asset package
|
||||
* but aren't assets. These sit outside the usual "locale/vendor"
|
||||
* path hierarchy, and will not be seen by "AssetDir" or included
|
||||
* in our filename cache.
|
||||
*/
|
||||
Asset* openNonAsset(const char* fileName, AccessMode mode);
|
||||
|
||||
/*
|
||||
* Explicit non-asset file. The file explicitly named by the cookie (the
|
||||
* resource set to look in) and fileName will be opened and returned.
|
||||
*/
|
||||
Asset* openNonAsset(const int32_t cookie, const char* fileName, AccessMode mode);
|
||||
|
||||
/*
|
||||
* Open a directory within the asset hierarchy.
|
||||
*
|
||||
* The contents of the directory are an amalgam of vendor-specific,
|
||||
* locale-specific, and generic assets stored loosely or in asset
|
||||
* packages. Depending on the cache setting and previous accesses,
|
||||
* this call may incur significant disk overhead.
|
||||
*
|
||||
* To open the top-level directory, pass in "".
|
||||
*/
|
||||
AssetDir* openDir(const char* dirName);
|
||||
|
||||
/*
|
||||
* Open a directory within a particular path of the asset manager.
|
||||
*
|
||||
* The contents of the directory are an amalgam of vendor-specific,
|
||||
* locale-specific, and generic assets stored loosely or in asset
|
||||
* packages. Depending on the cache setting and previous accesses,
|
||||
* this call may incur significant disk overhead.
|
||||
*
|
||||
* To open the top-level directory, pass in "".
|
||||
*/
|
||||
AssetDir* openNonAssetDir(const int32_t cookie, const char* dirName);
|
||||
|
||||
/*
|
||||
* Get the type of a file in the asset hierarchy. They will either
|
||||
* be "regular" or "directory". [Currently only works for "regular".]
|
||||
*
|
||||
* Can also be used as a quick test for existence of a file.
|
||||
*/
|
||||
FileType getFileType(const char* fileName);
|
||||
|
||||
/*
|
||||
* Return the complete resource table to find things in the package.
|
||||
*/
|
||||
const ResTable& getResources(bool required = true) const;
|
||||
|
||||
/*
|
||||
* Discard cached filename information. This only needs to be called
|
||||
* if somebody has updated the set of "loose" files, and we want to
|
||||
* discard our cached notion of what's where.
|
||||
*/
|
||||
void purge(void) { purgeFileNameCacheLocked(); }
|
||||
|
||||
/*
|
||||
* Return true if the files this AssetManager references are all
|
||||
* up-to-date (have not been changed since it was created). If false
|
||||
* is returned, you will need to create a new AssetManager to get
|
||||
* the current data.
|
||||
*/
|
||||
bool isUpToDate();
|
||||
|
||||
/**
|
||||
* Get the known locales for this asset manager object.
|
||||
*/
|
||||
void getLocales(Vector<String8>* locales) const;
|
||||
|
||||
private:
|
||||
struct asset_path
|
||||
{
|
||||
String8 path;
|
||||
FileType type;
|
||||
String8 idmap;
|
||||
};
|
||||
|
||||
Asset* openInPathLocked(const char* fileName, AccessMode mode,
|
||||
const asset_path& path);
|
||||
Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode,
|
||||
const asset_path& path);
|
||||
Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode,
|
||||
const asset_path& path, const char* locale, const char* vendor);
|
||||
String8 createPathNameLocked(const asset_path& path, const char* locale,
|
||||
const char* vendor);
|
||||
String8 createPathNameLocked(const asset_path& path, const char* rootDir);
|
||||
String8 createZipSourceNameLocked(const String8& zipFileName,
|
||||
const String8& dirName, const String8& fileName);
|
||||
|
||||
ZipFileRO* getZipFileLocked(const asset_path& path);
|
||||
Asset* openAssetFromFileLocked(const String8& fileName, AccessMode mode);
|
||||
Asset* openAssetFromZipLocked(const ZipFileRO* pZipFile,
|
||||
const ZipEntryRO entry, AccessMode mode, const String8& entryName);
|
||||
|
||||
bool scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
|
||||
const asset_path& path, const char* rootDir, const char* dirName);
|
||||
SortedVector<AssetDir::FileInfo>* scanDirLocked(const String8& path);
|
||||
bool scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
|
||||
const asset_path& path, const char* rootDir, const char* dirName);
|
||||
void mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
|
||||
const SortedVector<AssetDir::FileInfo>* pContents);
|
||||
|
||||
void loadFileNameCacheLocked(void);
|
||||
void fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
|
||||
const char* dirName);
|
||||
bool fncScanAndMergeDirLocked(
|
||||
SortedVector<AssetDir::FileInfo>* pMergedInfo,
|
||||
const asset_path& path, const char* locale, const char* vendor,
|
||||
const char* dirName);
|
||||
void purgeFileNameCacheLocked(void);
|
||||
|
||||
const ResTable* getResTable(bool required = true) const;
|
||||
void setLocaleLocked(const char* locale);
|
||||
void updateResourceParamsLocked() const;
|
||||
|
||||
bool createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
|
||||
const String8& idmapPath);
|
||||
|
||||
bool isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
|
||||
const String8& idmapPath);
|
||||
|
||||
Asset* openIdmapLocked(const struct asset_path& ap) const;
|
||||
|
||||
bool getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename, uint32_t* pCrc);
|
||||
|
||||
class SharedZip : public RefBase {
|
||||
public:
|
||||
static sp<SharedZip> get(const String8& path);
|
||||
|
||||
ZipFileRO* getZip();
|
||||
|
||||
Asset* getResourceTableAsset();
|
||||
Asset* setResourceTableAsset(Asset* asset);
|
||||
|
||||
ResTable* getResourceTable();
|
||||
ResTable* setResourceTable(ResTable* res);
|
||||
|
||||
bool isUpToDate();
|
||||
|
||||
protected:
|
||||
~SharedZip();
|
||||
|
||||
private:
|
||||
SharedZip(const String8& path, time_t modWhen);
|
||||
SharedZip(); // <-- not implemented
|
||||
|
||||
String8 mPath;
|
||||
ZipFileRO* mZipFile;
|
||||
time_t mModWhen;
|
||||
|
||||
Asset* mResourceTableAsset;
|
||||
ResTable* mResourceTable;
|
||||
|
||||
static Mutex gLock;
|
||||
static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
|
||||
};
|
||||
|
||||
/*
|
||||
* Manage a set of Zip files. For each file we need a pointer to the
|
||||
* ZipFile and a time_t with the file's modification date.
|
||||
*
|
||||
* We currently only have two zip files (current app, "common" app).
|
||||
* (This was originally written for 8, based on app/locale/vendor.)
|
||||
*/
|
||||
class ZipSet {
|
||||
public:
|
||||
ZipSet(void);
|
||||
~ZipSet(void);
|
||||
|
||||
/*
|
||||
* Return a ZipFileRO structure for a ZipFileRO with the specified
|
||||
* parameters.
|
||||
*/
|
||||
ZipFileRO* getZip(const String8& path);
|
||||
|
||||
Asset* getZipResourceTableAsset(const String8& path);
|
||||
Asset* setZipResourceTableAsset(const String8& path, Asset* asset);
|
||||
|
||||
ResTable* getZipResourceTable(const String8& path);
|
||||
ResTable* setZipResourceTable(const String8& path, ResTable* res);
|
||||
|
||||
// generate path, e.g. "common/en-US-noogle.zip"
|
||||
static String8 getPathName(const char* path);
|
||||
|
||||
bool isUpToDate();
|
||||
|
||||
private:
|
||||
void closeZip(int idx);
|
||||
|
||||
int getIndex(const String8& zip) const;
|
||||
mutable Vector<String8> mZipPath;
|
||||
mutable Vector<sp<SharedZip> > mZipFile;
|
||||
};
|
||||
|
||||
// Protect all internal state.
|
||||
mutable Mutex mLock;
|
||||
|
||||
ZipSet mZipSet;
|
||||
|
||||
Vector<asset_path> mAssetPaths;
|
||||
char* mLocale;
|
||||
char* mVendor;
|
||||
|
||||
mutable ResTable* mResources;
|
||||
ResTable_config* mConfig;
|
||||
|
||||
/*
|
||||
* Cached data for "loose" files. This lets us avoid poking at the
|
||||
* filesystem when searching for loose assets. Each entry is the
|
||||
* "extended partial" path, e.g. "default/default/foo/bar.txt". The
|
||||
* full set of files is present, including ".EXCLUDE" entries.
|
||||
*
|
||||
* We do not cache directory names. We don't retain the ".gz",
|
||||
* because to our clients "foo" and "foo.gz" both look like "foo".
|
||||
*/
|
||||
CacheMode mCacheMode; // is the cache enabled?
|
||||
bool mCacheValid; // clear when locale or vendor changes
|
||||
SortedVector<AssetDir::FileInfo> mCache;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // __LIBS_ASSETMANAGER_H
|
@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 _UTILS_BACKUP_HELPERS_H
|
||||
#define _UTILS_BACKUP_HELPERS_H
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
BACKUP_HEADER_ENTITY_V1 = 0x61746144, // Data (little endian)
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int type; // BACKUP_HEADER_ENTITY_V1
|
||||
int keyLen; // length of the key name, not including the null terminator
|
||||
int dataSize; // size of the data, not including the padding, -1 means delete
|
||||
} entity_header_v1;
|
||||
|
||||
struct SnapshotHeader {
|
||||
int magic0;
|
||||
int fileCount;
|
||||
int magic1;
|
||||
int totalSize;
|
||||
};
|
||||
|
||||
struct FileState {
|
||||
int modTime_sec;
|
||||
int modTime_nsec;
|
||||
int mode;
|
||||
int size;
|
||||
int crc32;
|
||||
int nameLen;
|
||||
};
|
||||
|
||||
struct FileRec {
|
||||
String8 file;
|
||||
bool deleted;
|
||||
FileState s;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes the data.
|
||||
*
|
||||
* If an error occurs, it poisons this object and all write calls will fail
|
||||
* with the error that occurred.
|
||||
*/
|
||||
class BackupDataWriter
|
||||
{
|
||||
public:
|
||||
BackupDataWriter(int fd);
|
||||
// does not close fd
|
||||
~BackupDataWriter();
|
||||
|
||||
status_t WriteEntityHeader(const String8& key, size_t dataSize);
|
||||
|
||||
/* Note: WriteEntityData will write arbitrary data into the file without
|
||||
* validation or a previously-supplied header. The full backup implementation
|
||||
* uses it this way to generate a controlled binary stream that is not
|
||||
* entity-structured. If the implementation here is changed, either this
|
||||
* use case must remain valid, or the full backup implementation should be
|
||||
* adjusted to use some other appropriate mechanism.
|
||||
*/
|
||||
status_t WriteEntityData(const void* data, size_t size);
|
||||
|
||||
void SetKeyPrefix(const String8& keyPrefix);
|
||||
|
||||
private:
|
||||
explicit BackupDataWriter();
|
||||
status_t write_padding_for(int n);
|
||||
|
||||
int m_fd;
|
||||
status_t m_status;
|
||||
ssize_t m_pos;
|
||||
int m_entityCount;
|
||||
String8 m_keyPrefix;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads the data.
|
||||
*
|
||||
* If an error occurs, it poisons this object and all write calls will fail
|
||||
* with the error that occurred.
|
||||
*/
|
||||
class BackupDataReader
|
||||
{
|
||||
public:
|
||||
BackupDataReader(int fd);
|
||||
// does not close fd
|
||||
~BackupDataReader();
|
||||
|
||||
status_t Status();
|
||||
status_t ReadNextHeader(bool* done, int* type);
|
||||
|
||||
bool HasEntities();
|
||||
status_t ReadEntityHeader(String8* key, size_t* dataSize);
|
||||
status_t SkipEntityData(); // must be called with the pointer at the beginning of the data.
|
||||
ssize_t ReadEntityData(void* data, size_t size);
|
||||
|
||||
private:
|
||||
explicit BackupDataReader();
|
||||
status_t skip_padding();
|
||||
|
||||
int m_fd;
|
||||
bool m_done;
|
||||
status_t m_status;
|
||||
ssize_t m_pos;
|
||||
ssize_t m_dataEndPos;
|
||||
int m_entityCount;
|
||||
union {
|
||||
int type;
|
||||
entity_header_v1 entity;
|
||||
} m_header;
|
||||
String8 m_key;
|
||||
};
|
||||
|
||||
int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
|
||||
char const* const* files, char const* const *keys, int fileCount);
|
||||
|
||||
int write_tarfile(const String8& packageName, const String8& domain,
|
||||
const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream);
|
||||
|
||||
class RestoreHelperBase
|
||||
{
|
||||
public:
|
||||
RestoreHelperBase();
|
||||
~RestoreHelperBase();
|
||||
|
||||
status_t WriteFile(const String8& filename, BackupDataReader* in);
|
||||
status_t WriteSnapshot(int fd);
|
||||
|
||||
private:
|
||||
void* m_buf;
|
||||
bool m_loggedUnknownMetadata;
|
||||
KeyedVector<String8,FileRec> m_files;
|
||||
};
|
||||
|
||||
#define TEST_BACKUP_HELPERS 1
|
||||
|
||||
#if TEST_BACKUP_HELPERS
|
||||
int backup_helper_test_empty();
|
||||
int backup_helper_test_four();
|
||||
int backup_helper_test_files();
|
||||
int backup_helper_test_null_base();
|
||||
int backup_helper_test_missing_file();
|
||||
int backup_helper_test_data_writer();
|
||||
int backup_helper_test_data_reader();
|
||||
#endif
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _UTILS_BACKUP_HELPERS_H
|
@ -1,193 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID__DATABASE_WINDOW_H
|
||||
#define _ANDROID__DATABASE_WINDOW_H
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#if LOG_NDEBUG
|
||||
|
||||
#define IF_LOG_WINDOW() if (false)
|
||||
#define LOG_WINDOW(...)
|
||||
|
||||
#else
|
||||
|
||||
#define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow")
|
||||
#define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* This class stores a set of rows from a database in a buffer. The begining of the
|
||||
* window has first chunk of RowSlots, which are offsets to the row directory, followed by
|
||||
* an offset to the next chunk in a linked-list of additional chunk of RowSlots in case
|
||||
* the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
|
||||
* FieldSlot per column, which has the size, offset, and type of the data for that field.
|
||||
* Note that the data types come from sqlite3.h.
|
||||
*
|
||||
* Strings are stored in UTF-8.
|
||||
*/
|
||||
class CursorWindow {
|
||||
CursorWindow(const String8& name, int ashmemFd,
|
||||
void* data, size_t size, bool readOnly);
|
||||
|
||||
public:
|
||||
/* Field types. */
|
||||
enum {
|
||||
FIELD_TYPE_NULL = 0,
|
||||
FIELD_TYPE_INTEGER = 1,
|
||||
FIELD_TYPE_FLOAT = 2,
|
||||
FIELD_TYPE_STRING = 3,
|
||||
FIELD_TYPE_BLOB = 4,
|
||||
};
|
||||
|
||||
/* Opaque type that describes a field slot. */
|
||||
struct FieldSlot {
|
||||
private:
|
||||
int32_t type;
|
||||
union {
|
||||
double d;
|
||||
int64_t l;
|
||||
struct {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} buffer;
|
||||
} data;
|
||||
|
||||
friend class CursorWindow;
|
||||
} __attribute((packed));
|
||||
|
||||
~CursorWindow();
|
||||
|
||||
static status_t create(const String8& name, size_t size, CursorWindow** outCursorWindow);
|
||||
static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow);
|
||||
|
||||
status_t writeToParcel(Parcel* parcel);
|
||||
|
||||
inline String8 name() { return mName; }
|
||||
inline size_t size() { return mSize; }
|
||||
inline size_t freeSpace() { return mSize - mHeader->freeOffset; }
|
||||
inline uint32_t getNumRows() { return mHeader->numRows; }
|
||||
inline uint32_t getNumColumns() { return mHeader->numColumns; }
|
||||
|
||||
status_t clear();
|
||||
status_t setNumColumns(uint32_t numColumns);
|
||||
|
||||
/**
|
||||
* Allocate a row slot and its directory.
|
||||
* The row is initialized will null entries for each field.
|
||||
*/
|
||||
status_t allocRow();
|
||||
status_t freeLastRow();
|
||||
|
||||
status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size);
|
||||
status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull);
|
||||
status_t putLong(uint32_t row, uint32_t column, int64_t value);
|
||||
status_t putDouble(uint32_t row, uint32_t column, double value);
|
||||
status_t putNull(uint32_t row, uint32_t column);
|
||||
|
||||
/**
|
||||
* Gets the field slot at the specified row and column.
|
||||
* Returns null if the requested row or column is not in the window.
|
||||
*/
|
||||
FieldSlot* getFieldSlot(uint32_t row, uint32_t column);
|
||||
|
||||
inline int32_t getFieldSlotType(FieldSlot* fieldSlot) {
|
||||
return fieldSlot->type;
|
||||
}
|
||||
|
||||
inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) {
|
||||
return fieldSlot->data.l;
|
||||
}
|
||||
|
||||
inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) {
|
||||
return fieldSlot->data.d;
|
||||
}
|
||||
|
||||
inline const char* getFieldSlotValueString(FieldSlot* fieldSlot,
|
||||
size_t* outSizeIncludingNull) {
|
||||
*outSizeIncludingNull = fieldSlot->data.buffer.size;
|
||||
return static_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset));
|
||||
}
|
||||
|
||||
inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) {
|
||||
*outSize = fieldSlot->data.buffer.size;
|
||||
return offsetToPtr(fieldSlot->data.buffer.offset);
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100;
|
||||
|
||||
struct Header {
|
||||
// Offset of the lowest unused byte in the window.
|
||||
uint32_t freeOffset;
|
||||
|
||||
// Offset of the first row slot chunk.
|
||||
uint32_t firstChunkOffset;
|
||||
|
||||
uint32_t numRows;
|
||||
uint32_t numColumns;
|
||||
};
|
||||
|
||||
struct RowSlot {
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct RowSlotChunk {
|
||||
RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS];
|
||||
uint32_t nextChunkOffset;
|
||||
};
|
||||
|
||||
String8 mName;
|
||||
int mAshmemFd;
|
||||
void* mData;
|
||||
size_t mSize;
|
||||
bool mReadOnly;
|
||||
Header* mHeader;
|
||||
|
||||
inline void* offsetToPtr(uint32_t offset) {
|
||||
return static_cast<uint8_t*>(mData) + offset;
|
||||
}
|
||||
|
||||
inline uint32_t offsetFromPtr(void* ptr) {
|
||||
return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a portion of the window. Returns the offset
|
||||
* of the allocation, or 0 if there isn't enough space.
|
||||
* If aligned is true, the allocation gets 4 byte alignment.
|
||||
*/
|
||||
uint32_t alloc(size_t size, bool aligned = false);
|
||||
|
||||
RowSlot* getRowSlot(uint32_t row);
|
||||
RowSlot* allocRowSlot();
|
||||
|
||||
status_t putBlobOrString(uint32_t row, uint32_t column,
|
||||
const void* value, size_t size, int32_t type);
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif
|
@ -1,145 +0,0 @@
|
||||
/*
|
||||
* 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 <strings.h>
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// OBB flags (bit 0)
|
||||
#define OBB_OVERLAY (1 << 0)
|
||||
#define OBB_SALTED (1 << 1)
|
||||
|
||||
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);
|
||||
bool removeFrom(const char* filename);
|
||||
bool removeFrom(int fd);
|
||||
|
||||
const char* getFileName() const {
|
||||
return mFileName;
|
||||
}
|
||||
|
||||
const String8 getPackageName() const {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
void setPackageName(String8 packageName) {
|
||||
mPackageName = packageName;
|
||||
}
|
||||
|
||||
int32_t getVersion() const {
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
void setVersion(int32_t version) {
|
||||
mVersion = version;
|
||||
}
|
||||
|
||||
int32_t getFlags() const {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
void setFlags(int32_t flags) {
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
const unsigned char* getSalt(size_t* length) const {
|
||||
if ((mFlags & OBB_SALTED) == 0) {
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*length = sizeof(mSalt);
|
||||
return mSalt;
|
||||
}
|
||||
|
||||
bool setSalt(const unsigned char* salt, size_t length) {
|
||||
if (length != sizeof(mSalt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(mSalt, salt, sizeof(mSalt));
|
||||
mFlags |= OBB_SALTED;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isOverlay() {
|
||||
return (mFlags & OBB_OVERLAY) == OBB_OVERLAY;
|
||||
}
|
||||
|
||||
void setOverlay(bool overlay) {
|
||||
if (overlay) {
|
||||
mFlags |= OBB_OVERLAY;
|
||||
} else {
|
||||
mFlags &= ~OBB_OVERLAY;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Flags for this OBB type. */
|
||||
int32_t mFlags;
|
||||
|
||||
/* Whether the file is salted. */
|
||||
bool mSalted;
|
||||
|
||||
/* The encryption salt. */
|
||||
unsigned char mSalt[8];
|
||||
|
||||
const char* mFileName;
|
||||
|
||||
size_t mFileSize;
|
||||
|
||||
size_t mFooterStart;
|
||||
|
||||
unsigned char* mReadBuf;
|
||||
|
||||
bool parseObbFile(int fd);
|
||||
};
|
||||
|
||||
}
|
||||
#endif /* OBBFILE_H_ */
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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 _ANDROIDFW_POWER_MANAGER_H
|
||||
#define _ANDROIDFW_POWER_MANAGER_H
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
enum {
|
||||
USER_ACTIVITY_EVENT_OTHER = 0,
|
||||
USER_ACTIVITY_EVENT_BUTTON = 1,
|
||||
USER_ACTIVITY_EVENT_TOUCH = 2,
|
||||
|
||||
USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_TOUCH, // Last valid event code.
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif // _ANDROIDFW_POWER_MANAGER_H
|
File diff suppressed because it is too large
Load Diff
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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 __LIBS_STREAMINGZIPINFLATER_H
|
||||
#define __LIBS_STREAMINGZIPINFLATER_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <utils/Compat.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class StreamingZipInflater {
|
||||
public:
|
||||
static const size_t INPUT_CHUNK_SIZE = 64 * 1024;
|
||||
static const size_t OUTPUT_CHUNK_SIZE = 64 * 1024;
|
||||
|
||||
// Flavor that pages in the compressed data from a fd
|
||||
StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize);
|
||||
|
||||
// Flavor that gets the compressed data from an in-memory buffer
|
||||
StreamingZipInflater(class FileMap* dataMap, size_t uncompSize);
|
||||
|
||||
~StreamingZipInflater();
|
||||
|
||||
// read 'count' bytes of uncompressed data from the current position. outBuf may
|
||||
// be NULL, in which case the data is consumed and discarded.
|
||||
ssize_t read(void* outBuf, size_t count);
|
||||
|
||||
// seeking backwards requires uncompressing fom the beginning, so is very
|
||||
// expensive. seeking forwards only requires uncompressing from the current
|
||||
// position to the destination.
|
||||
off64_t seekAbsolute(off64_t absoluteInputPosition);
|
||||
|
||||
private:
|
||||
void initInflateState();
|
||||
int readNextChunk();
|
||||
|
||||
// where to find the uncompressed data
|
||||
int mFd;
|
||||
off64_t mInFileStart; // where the compressed data lives in the file
|
||||
class FileMap* mDataMap;
|
||||
|
||||
z_stream mInflateState;
|
||||
bool mStreamNeedsInit;
|
||||
|
||||
// output invariants for this asset
|
||||
uint8_t* mOutBuf; // output buf for decompressed bytes
|
||||
size_t mOutBufSize; // allocated size of mOutBuf
|
||||
size_t mOutTotalSize; // total uncompressed size of the blob
|
||||
|
||||
// current output state bookkeeping
|
||||
off64_t mOutCurPosition; // current position in total offset
|
||||
size_t mOutLastDecoded; // last decoded byte + 1 in mOutbuf
|
||||
size_t mOutDeliverable; // next undelivered byte of decoded output in mOutBuf
|
||||
|
||||
// input invariants
|
||||
uint8_t* mInBuf;
|
||||
size_t mInBufSize; // allocated size of mInBuf;
|
||||
size_t mInTotalSize; // total size of compressed data for this blob
|
||||
|
||||
// input state bookkeeping
|
||||
size_t mInNextChunkOffset; // offset from start of blob at which the next input chunk lies
|
||||
// the z_stream contains state about input block consumption
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Read-only access to Zip archives, with minimal heap allocation.
|
||||
*
|
||||
* This is similar to the more-complete ZipFile class, but no attempt
|
||||
* has been made to make them interchangeable. This class operates under
|
||||
* a very different set of assumptions and constraints.
|
||||
*
|
||||
* One such assumption is that if you're getting file descriptors for
|
||||
* use with this class as a child of a fork() operation, you must be on
|
||||
* a pread() to guarantee correct operation. This is because pread() can
|
||||
* atomically read at a file offset without worrying about a lock around an
|
||||
* lseek() + read() pair.
|
||||
*/
|
||||
#ifndef __LIBS_ZIPFILERO_H
|
||||
#define __LIBS_ZIPFILERO_H
|
||||
|
||||
#include <utils/Compat.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/FileMap.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef void* ZipArchiveHandle;
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Trivial typedef to ensure that ZipEntryRO is not treated as a simple
|
||||
* integer. We use NULL to indicate an invalid value.
|
||||
*/
|
||||
typedef void* ZipEntryRO;
|
||||
|
||||
/*
|
||||
* Open a Zip archive for reading.
|
||||
*
|
||||
* Implemented as a thin wrapper over system/core/libziparchive.
|
||||
*
|
||||
* "open" and "find entry by name" are fast operations and use as little
|
||||
* memory as possible.
|
||||
*
|
||||
* We also support fast iteration over all entries in the file (with a
|
||||
* stable, but unspecified iteration order).
|
||||
*
|
||||
* NOTE: If this is used on file descriptors inherited from a fork() operation,
|
||||
* you must be on a platform that implements pread() to guarantee correctness
|
||||
* on the shared file descriptors.
|
||||
*/
|
||||
class ZipFileRO {
|
||||
public:
|
||||
/* Zip compression methods we support */
|
||||
enum {
|
||||
kCompressStored = 0, // no compression
|
||||
kCompressDeflated = 8, // standard deflate
|
||||
};
|
||||
|
||||
/*
|
||||
* Open an archive.
|
||||
*/
|
||||
static ZipFileRO* open(const char* zipFileName);
|
||||
|
||||
/*
|
||||
* Find an entry, by name. Returns the entry identifier, or NULL if
|
||||
* not found.
|
||||
*/
|
||||
ZipEntryRO findEntryByName(const char* entryName) const;
|
||||
|
||||
|
||||
/*
|
||||
* Start iterating over the list of entries in the zip file. Requires
|
||||
* a matching call to endIteration with the same cookie.
|
||||
*/
|
||||
bool startIteration(void** cookie);
|
||||
|
||||
/**
|
||||
* Return the next entry in iteration order, or NULL if there are no more
|
||||
* entries in this archive.
|
||||
*/
|
||||
ZipEntryRO nextEntry(void* cookie);
|
||||
|
||||
void endIteration(void* cookie);
|
||||
|
||||
void releaseEntry(ZipEntryRO entry) const;
|
||||
|
||||
/*
|
||||
* Return the #of entries in the Zip archive.
|
||||
*/
|
||||
int getNumEntries();
|
||||
|
||||
/*
|
||||
* Copy the filename into the supplied buffer. Returns 0 on success,
|
||||
* -1 if "entry" is invalid, or the filename length if it didn't fit. The
|
||||
* length, and the returned string, include the null-termination.
|
||||
*/
|
||||
int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const;
|
||||
|
||||
/*
|
||||
* Get the vital stats for an entry. Pass in NULL pointers for anything
|
||||
* you don't need.
|
||||
*
|
||||
* "*pOffset" holds the Zip file offset of the entry's data.
|
||||
*
|
||||
* Returns "false" if "entry" is bogus or if the data in the Zip file
|
||||
* appears to be bad.
|
||||
*/
|
||||
bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
|
||||
size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const;
|
||||
|
||||
/*
|
||||
* Create a new FileMap object that maps a subset of the archive. For
|
||||
* an uncompressed entry this effectively provides a pointer to the
|
||||
* actual data, for a compressed entry this provides the input buffer
|
||||
* for inflate().
|
||||
*/
|
||||
FileMap* createEntryFileMap(ZipEntryRO entry) const;
|
||||
|
||||
/*
|
||||
* Uncompress the data into a buffer. Depending on the compression
|
||||
* format, this is either an "inflate" operation or a memcpy.
|
||||
*
|
||||
* Use "uncompLen" from getEntryInfo() to determine the required
|
||||
* buffer size.
|
||||
*
|
||||
* Returns "true" on success.
|
||||
*/
|
||||
bool uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const;
|
||||
|
||||
/*
|
||||
* Uncompress the data to an open file descriptor.
|
||||
*/
|
||||
bool uncompressEntry(ZipEntryRO entry, int fd) const;
|
||||
|
||||
~ZipFileRO();
|
||||
|
||||
private:
|
||||
/* these are private and not defined */
|
||||
ZipFileRO(const ZipFileRO& src);
|
||||
ZipFileRO& operator=(const ZipFileRO& src);
|
||||
|
||||
ZipFileRO(ZipArchiveHandle handle, char* fileName) : mHandle(handle),
|
||||
mFileName(fileName)
|
||||
{
|
||||
}
|
||||
|
||||
const ZipArchiveHandle mHandle;
|
||||
char* mFileName;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif /*__LIBS_ZIPFILERO_H*/
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Miscellaneous zip/gzip utility functions.
|
||||
//
|
||||
#ifndef __LIBS_ZIPUTILS_H
|
||||
#define __LIBS_ZIPUTILS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Container class for utility functions, primarily for namespace reasons.
|
||||
*/
|
||||
class ZipUtils {
|
||||
public:
|
||||
/*
|
||||
* General utility function for uncompressing "deflate" data from a file
|
||||
* to a buffer.
|
||||
*/
|
||||
static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen,
|
||||
long compressedLen);
|
||||
static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
|
||||
long compressedLen);
|
||||
static bool inflateToBuffer(void *in, void* buf, long uncompressedLen,
|
||||
long compressedLen);
|
||||
|
||||
/*
|
||||
* Someday we might want to make this generic and handle bzip2 ".bz2"
|
||||
* files too.
|
||||
*
|
||||
* We could declare gzip to be a sub-class of zip that has exactly
|
||||
* one always-compressed entry, but we currently want to treat Zip
|
||||
* and gzip as distinct, so there's no value.
|
||||
*
|
||||
* The zlib library has some gzip utilities, but it has no interface
|
||||
* for extracting the uncompressed length of the file (you do *not*
|
||||
* want to gzseek to the end).
|
||||
*
|
||||
* Pass in a seeked file pointer for the gzip file. If this is a gzip
|
||||
* file, we set our return values appropriately and return "true" with
|
||||
* the file seeked to the start of the compressed data.
|
||||
*/
|
||||
static bool examineGzip(FILE* fp, int* pCompressionMethod,
|
||||
long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32);
|
||||
|
||||
/*
|
||||
* Utility function to convert ZIP's time format to a timespec struct.
|
||||
*/
|
||||
static inline void zipTimeToTimespec(long when, struct tm* timespec) {
|
||||
const long date = when >> 16;
|
||||
timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980
|
||||
timespec->tm_mon = (date >> 5) & 0x0F;
|
||||
timespec->tm_mday = date & 0x1F;
|
||||
|
||||
timespec->tm_hour = (when >> 11) & 0x1F;
|
||||
timespec->tm_min = (when >> 5) & 0x3F;
|
||||
timespec->tm_sec = (when & 0x1F) << 1;
|
||||
}
|
||||
private:
|
||||
ZipUtils() {}
|
||||
~ZipUtils() {}
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif /*__LIBS_ZIPUTILS_H*/
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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 <sys/types.h>
|
||||
|
||||
//
|
||||
// Handy utility functions and portability code.
|
||||
//
|
||||
#ifndef _LIBS_ANDROID_FW_MISC_H
|
||||
#define _LIBS_ANDROID_FW_MISC_H
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Some utility functions for working with files. These could be made
|
||||
* part of a "File" class.
|
||||
*/
|
||||
typedef enum FileType {
|
||||
kFileTypeUnknown = 0,
|
||||
kFileTypeNonexistent, // i.e. ENOENT
|
||||
kFileTypeRegular,
|
||||
kFileTypeDirectory,
|
||||
kFileTypeCharDev,
|
||||
kFileTypeBlockDev,
|
||||
kFileTypeFifo,
|
||||
kFileTypeSymlink,
|
||||
kFileTypeSocket,
|
||||
} FileType;
|
||||
/* get the file's type; follows symlinks */
|
||||
FileType getFileType(const char* fileName);
|
||||
/* get the file's modification date; returns -1 w/errno set on failure */
|
||||
time_t getFileModDate(const char* fileName);
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // _LIBS_ANDROID_FW_MISC_H
|
@ -1,96 +0,0 @@
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
# libandroidfw is partially built for the host (used by obbtool and others)
|
||||
# These files are common to host and target builds.
|
||||
|
||||
commonSources := \
|
||||
Asset.cpp \
|
||||
AssetDir.cpp \
|
||||
AssetManager.cpp \
|
||||
misc.cpp \
|
||||
ObbFile.cpp \
|
||||
ResourceTypes.cpp \
|
||||
StreamingZipInflater.cpp \
|
||||
ZipFileRO.cpp \
|
||||
ZipUtils.cpp
|
||||
|
||||
deviceSources := \
|
||||
$(commonSources) \
|
||||
BackupData.cpp \
|
||||
BackupHelpers.cpp \
|
||||
CursorWindow.cpp
|
||||
|
||||
hostSources := \
|
||||
$(commonSources)
|
||||
|
||||
# For the host
|
||||
# =====================================================
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= $(hostSources)
|
||||
|
||||
LOCAL_MODULE:= libandroidfw
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
external/zlib
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := liblog libziparchive-host libutils
|
||||
|
||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||
|
||||
|
||||
# For the device
|
||||
# =====================================================
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= $(deviceSources)
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libbinder \
|
||||
liblog \
|
||||
libcutils \
|
||||
libutils \
|
||||
libz
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libziparchive
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
external/icu4c/common \
|
||||
external/zlib \
|
||||
system/core/include
|
||||
|
||||
LOCAL_MODULE:= libandroidfw
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
|
||||
# Include subdirectory makefiles
|
||||
# ============================================================
|
||||
|
||||
# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
|
||||
# team really wants is to build the stuff defined by this makefile.
|
||||
ifeq (,$(ONE_SHOT_MAKEFILE))
|
||||
include $(call first-makefiles-under,$(LOCAL_PATH))
|
||||
endif
|
@ -1,897 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Provide access to a read-only asset.
|
||||
//
|
||||
|
||||
#define LOG_TAG "asset"
|
||||
//#define NDEBUG 0
|
||||
|
||||
#include <androidfw/Asset.h>
|
||||
#include <androidfw/StreamingZipInflater.h>
|
||||
#include <androidfw/ZipFileRO.h>
|
||||
#include <androidfw/ZipUtils.h>
|
||||
#include <utils/Atomic.h>
|
||||
#include <utils/FileMap.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace android;
|
||||
|
||||
#ifndef O_BINARY
|
||||
# define O_BINARY 0
|
||||
#endif
|
||||
|
||||
static Mutex gAssetLock;
|
||||
static int32_t gCount = 0;
|
||||
static Asset* gHead = NULL;
|
||||
static Asset* gTail = NULL;
|
||||
|
||||
int32_t Asset::getGlobalCount()
|
||||
{
|
||||
AutoMutex _l(gAssetLock);
|
||||
return gCount;
|
||||
}
|
||||
|
||||
String8 Asset::getAssetAllocations()
|
||||
{
|
||||
AutoMutex _l(gAssetLock);
|
||||
String8 res;
|
||||
Asset* cur = gHead;
|
||||
while (cur != NULL) {
|
||||
if (cur->isAllocated()) {
|
||||
res.append(" ");
|
||||
res.append(cur->getAssetSource());
|
||||
off64_t size = (cur->getLength()+512)/1024;
|
||||
char buf[64];
|
||||
sprintf(buf, ": %dK\n", (int)size);
|
||||
res.append(buf);
|
||||
}
|
||||
cur = cur->mNext;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Asset::Asset(void)
|
||||
: mAccessMode(ACCESS_UNKNOWN)
|
||||
{
|
||||
AutoMutex _l(gAssetLock);
|
||||
gCount++;
|
||||
mNext = mPrev = NULL;
|
||||
if (gTail == NULL) {
|
||||
gHead = gTail = this;
|
||||
} else {
|
||||
mPrev = gTail;
|
||||
gTail->mNext = this;
|
||||
gTail = this;
|
||||
}
|
||||
//ALOGI("Creating Asset %p #%d\n", this, gCount);
|
||||
}
|
||||
|
||||
Asset::~Asset(void)
|
||||
{
|
||||
AutoMutex _l(gAssetLock);
|
||||
gCount--;
|
||||
if (gHead == this) {
|
||||
gHead = mNext;
|
||||
}
|
||||
if (gTail == this) {
|
||||
gTail = mPrev;
|
||||
}
|
||||
if (mNext != NULL) {
|
||||
mNext->mPrev = mPrev;
|
||||
}
|
||||
if (mPrev != NULL) {
|
||||
mPrev->mNext = mNext;
|
||||
}
|
||||
mNext = mPrev = NULL;
|
||||
//ALOGI("Destroying Asset in %p #%d\n", this, gCount);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new Asset from a file on disk. There is a fair chance that
|
||||
* the file doesn't actually exist.
|
||||
*
|
||||
* We can use "mode" to decide how we want to go about it.
|
||||
*/
|
||||
/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
|
||||
{
|
||||
_FileAsset* pAsset;
|
||||
status_t result;
|
||||
off64_t length;
|
||||
int fd;
|
||||
|
||||
fd = open(fileName, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Under Linux, the lseek fails if we actually opened a directory. To
|
||||
* be correct we should test the file type explicitly, but since we
|
||||
* always open things read-only it doesn't really matter, so there's
|
||||
* no value in incurring the extra overhead of an fstat() call.
|
||||
*/
|
||||
// TODO(kroot): replace this with fstat despite the plea above.
|
||||
#if 1
|
||||
length = lseek64(fd, 0, SEEK_END);
|
||||
if (length < 0) {
|
||||
::close(fd);
|
||||
return NULL;
|
||||
}
|
||||
(void) lseek64(fd, 0, SEEK_SET);
|
||||
#else
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0) {
|
||||
::close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
::close(fd);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
pAsset = new _FileAsset;
|
||||
result = pAsset->openChunk(fileName, fd, 0, length);
|
||||
if (result != NO_ERROR) {
|
||||
delete pAsset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pAsset->mAccessMode = mode;
|
||||
return pAsset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a new Asset from a compressed file on disk. There is a fair chance
|
||||
* that the file doesn't actually exist.
|
||||
*
|
||||
* We currently support gzip files. We might want to handle .bz2 someday.
|
||||
*/
|
||||
/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
|
||||
AccessMode mode)
|
||||
{
|
||||
_CompressedAsset* pAsset;
|
||||
status_t result;
|
||||
off64_t fileLen;
|
||||
bool scanResult;
|
||||
long offset;
|
||||
int method;
|
||||
long uncompressedLen, compressedLen;
|
||||
int fd;
|
||||
|
||||
fd = open(fileName, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
fileLen = lseek(fd, 0, SEEK_END);
|
||||
if (fileLen < 0) {
|
||||
::close(fd);
|
||||
return NULL;
|
||||
}
|
||||
(void) lseek(fd, 0, SEEK_SET);
|
||||
|
||||
/* want buffered I/O for the file scan; must dup so fclose() is safe */
|
||||
FILE* fp = fdopen(dup(fd), "rb");
|
||||
if (fp == NULL) {
|
||||
::close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned long crc32;
|
||||
scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
|
||||
&compressedLen, &crc32);
|
||||
offset = ftell(fp);
|
||||
fclose(fp);
|
||||
if (!scanResult) {
|
||||
ALOGD("File '%s' is not in gzip format\n", fileName);
|
||||
::close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pAsset = new _CompressedAsset;
|
||||
result = pAsset->openChunk(fd, offset, method, uncompressedLen,
|
||||
compressedLen);
|
||||
if (result != NO_ERROR) {
|
||||
delete pAsset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pAsset->mAccessMode = mode;
|
||||
return pAsset;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Create a new Asset from part of an open file.
|
||||
*/
|
||||
/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset,
|
||||
size_t length, AccessMode mode)
|
||||
{
|
||||
_FileAsset* pAsset;
|
||||
status_t result;
|
||||
|
||||
pAsset = new _FileAsset;
|
||||
result = pAsset->openChunk(NULL, fd, offset, length);
|
||||
if (result != NO_ERROR)
|
||||
return NULL;
|
||||
|
||||
pAsset->mAccessMode = mode;
|
||||
return pAsset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new Asset from compressed data in an open file.
|
||||
*/
|
||||
/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
|
||||
int compressionMethod, size_t uncompressedLen, size_t compressedLen,
|
||||
AccessMode mode)
|
||||
{
|
||||
_CompressedAsset* pAsset;
|
||||
status_t result;
|
||||
|
||||
pAsset = new _CompressedAsset;
|
||||
result = pAsset->openChunk(fd, offset, compressionMethod,
|
||||
uncompressedLen, compressedLen);
|
||||
if (result != NO_ERROR)
|
||||
return NULL;
|
||||
|
||||
pAsset->mAccessMode = mode;
|
||||
return pAsset;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create a new Asset from a memory mapping.
|
||||
*/
|
||||
/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
|
||||
AccessMode mode)
|
||||
{
|
||||
_FileAsset* pAsset;
|
||||
status_t result;
|
||||
|
||||
pAsset = new _FileAsset;
|
||||
result = pAsset->openChunk(dataMap);
|
||||
if (result != NO_ERROR)
|
||||
return NULL;
|
||||
|
||||
pAsset->mAccessMode = mode;
|
||||
return pAsset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new Asset from compressed data in a memory mapping.
|
||||
*/
|
||||
/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
|
||||
int method, size_t uncompressedLen, AccessMode mode)
|
||||
{
|
||||
_CompressedAsset* pAsset;
|
||||
status_t result;
|
||||
|
||||
pAsset = new _CompressedAsset;
|
||||
result = pAsset->openChunk(dataMap, method, uncompressedLen);
|
||||
if (result != NO_ERROR)
|
||||
return NULL;
|
||||
|
||||
pAsset->mAccessMode = mode;
|
||||
return pAsset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Do generic seek() housekeeping. Pass in the offset/whence values from
|
||||
* the seek request, along with the current chunk offset and the chunk
|
||||
* length.
|
||||
*
|
||||
* Returns the new chunk offset, or -1 if the seek is illegal.
|
||||
*/
|
||||
off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
|
||||
{
|
||||
off64_t newOffset;
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
newOffset = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
newOffset = curPosn + offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
newOffset = maxPosn + offset;
|
||||
break;
|
||||
default:
|
||||
ALOGW("unexpected whence %d\n", whence);
|
||||
// this was happening due to an off64_t size mismatch
|
||||
assert(false);
|
||||
return (off64_t) -1;
|
||||
}
|
||||
|
||||
if (newOffset < 0 || newOffset > maxPosn) {
|
||||
ALOGW("seek out of range: want %ld, end=%ld\n",
|
||||
(long) newOffset, (long) maxPosn);
|
||||
return (off64_t) -1;
|
||||
}
|
||||
|
||||
return newOffset;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* _FileAsset
|
||||
* ===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Constructor.
|
||||
*/
|
||||
_FileAsset::_FileAsset(void)
|
||||
: mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Destructor. Release resources.
|
||||
*/
|
||||
_FileAsset::~_FileAsset(void)
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Operate on a chunk of an uncompressed file.
|
||||
*
|
||||
* Zero-length chunks are allowed.
|
||||
*/
|
||||
status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
|
||||
{
|
||||
assert(mFp == NULL); // no reopen
|
||||
assert(mMap == NULL);
|
||||
assert(fd >= 0);
|
||||
assert(offset >= 0);
|
||||
|
||||
/*
|
||||
* Seek to end to get file length.
|
||||
*/
|
||||
off64_t fileLength;
|
||||
fileLength = lseek64(fd, 0, SEEK_END);
|
||||
if (fileLength == (off64_t) -1) {
|
||||
// probably a bad file descriptor
|
||||
ALOGD("failed lseek (errno=%d)\n", errno);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if ((off64_t) (offset + length) > fileLength) {
|
||||
ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
|
||||
(long) offset, (long) length, (long) fileLength);
|
||||
return BAD_INDEX;
|
||||
}
|
||||
|
||||
/* after fdopen, the fd will be closed on fclose() */
|
||||
mFp = fdopen(fd, "rb");
|
||||
if (mFp == NULL)
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
mStart = offset;
|
||||
mLength = length;
|
||||
assert(mOffset == 0);
|
||||
|
||||
/* seek the FILE* to the start of chunk */
|
||||
if (fseek(mFp, mStart, SEEK_SET) != 0) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
mFileName = fileName != NULL ? strdup(fileName) : NULL;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the chunk from the map.
|
||||
*/
|
||||
status_t _FileAsset::openChunk(FileMap* dataMap)
|
||||
{
|
||||
assert(mFp == NULL); // no reopen
|
||||
assert(mMap == NULL);
|
||||
assert(dataMap != NULL);
|
||||
|
||||
mMap = dataMap;
|
||||
mStart = -1; // not used
|
||||
mLength = dataMap->getDataLength();
|
||||
assert(mOffset == 0);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a chunk of data.
|
||||
*/
|
||||
ssize_t _FileAsset::read(void* buf, size_t count)
|
||||
{
|
||||
size_t maxLen;
|
||||
size_t actual;
|
||||
|
||||
assert(mOffset >= 0 && mOffset <= mLength);
|
||||
|
||||
if (getAccessMode() == ACCESS_BUFFER) {
|
||||
/*
|
||||
* On first access, read or map the entire file. The caller has
|
||||
* requested buffer access, either because they're going to be
|
||||
* using the buffer or because what they're doing has appropriate
|
||||
* performance needs and access patterns.
|
||||
*/
|
||||
if (mBuf == NULL)
|
||||
getBuffer(false);
|
||||
}
|
||||
|
||||
/* adjust count if we're near EOF */
|
||||
maxLen = mLength - mOffset;
|
||||
if (count > maxLen)
|
||||
count = maxLen;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
if (mMap != NULL) {
|
||||
/* copy from mapped area */
|
||||
//printf("map read\n");
|
||||
memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
|
||||
actual = count;
|
||||
} else if (mBuf != NULL) {
|
||||
/* copy from buffer */
|
||||
//printf("buf read\n");
|
||||
memcpy(buf, (char*)mBuf + mOffset, count);
|
||||
actual = count;
|
||||
} else {
|
||||
/* read from the file */
|
||||
//printf("file read\n");
|
||||
if (ftell(mFp) != mStart + mOffset) {
|
||||
ALOGE("Hosed: %ld != %ld+%ld\n",
|
||||
ftell(mFp), (long) mStart, (long) mOffset);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* This returns 0 on error or eof. We need to use ferror() or feof()
|
||||
* to tell the difference, but we don't currently have those on the
|
||||
* device. However, we know how much data is *supposed* to be in the
|
||||
* file, so if we don't read the full amount we know something is
|
||||
* hosed.
|
||||
*/
|
||||
actual = fread(buf, 1, count, mFp);
|
||||
if (actual == 0) // something failed -- I/O error?
|
||||
return -1;
|
||||
|
||||
assert(actual == count);
|
||||
}
|
||||
|
||||
mOffset += actual;
|
||||
return actual;
|
||||
}
|
||||
|
||||
/*
|
||||
* Seek to a new position.
|
||||
*/
|
||||
off64_t _FileAsset::seek(off64_t offset, int whence)
|
||||
{
|
||||
off64_t newPosn;
|
||||
off64_t actualOffset;
|
||||
|
||||
// compute new position within chunk
|
||||
newPosn = handleSeek(offset, whence, mOffset, mLength);
|
||||
if (newPosn == (off64_t) -1)
|
||||
return newPosn;
|
||||
|
||||
actualOffset = mStart + newPosn;
|
||||
|
||||
if (mFp != NULL) {
|
||||
if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
|
||||
return (off64_t) -1;
|
||||
}
|
||||
|
||||
mOffset = actualOffset - mStart;
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the asset.
|
||||
*/
|
||||
void _FileAsset::close(void)
|
||||
{
|
||||
if (mMap != NULL) {
|
||||
mMap->release();
|
||||
mMap = NULL;
|
||||
}
|
||||
if (mBuf != NULL) {
|
||||
delete[] mBuf;
|
||||
mBuf = NULL;
|
||||
}
|
||||
|
||||
if (mFileName != NULL) {
|
||||
free(mFileName);
|
||||
mFileName = NULL;
|
||||
}
|
||||
|
||||
if (mFp != NULL) {
|
||||
// can only be NULL when called from destructor
|
||||
// (otherwise we would never return this object)
|
||||
fclose(mFp);
|
||||
mFp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a read-only pointer to a buffer.
|
||||
*
|
||||
* We can either read the whole thing in or map the relevant piece of
|
||||
* the source file. Ideally a map would be established at a higher
|
||||
* level and we'd be using a different object, but we didn't, so we
|
||||
* deal with it here.
|
||||
*/
|
||||
const void* _FileAsset::getBuffer(bool wordAligned)
|
||||
{
|
||||
/* subsequent requests just use what we did previously */
|
||||
if (mBuf != NULL)
|
||||
return mBuf;
|
||||
if (mMap != NULL) {
|
||||
if (!wordAligned) {
|
||||
return mMap->getDataPtr();
|
||||
}
|
||||
return ensureAlignment(mMap);
|
||||
}
|
||||
|
||||
assert(mFp != NULL);
|
||||
|
||||
if (mLength < kReadVsMapThreshold) {
|
||||
unsigned char* buf;
|
||||
long allocLen;
|
||||
|
||||
/* zero-length files are allowed; not sure about zero-len allocs */
|
||||
/* (works fine with gcc + x86linux) */
|
||||
allocLen = mLength;
|
||||
if (mLength == 0)
|
||||
allocLen = 1;
|
||||
|
||||
buf = new unsigned char[allocLen];
|
||||
if (buf == NULL) {
|
||||
ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
|
||||
if (mLength > 0) {
|
||||
long oldPosn = ftell(mFp);
|
||||
fseek(mFp, mStart, SEEK_SET);
|
||||
if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
|
||||
ALOGE("failed reading %ld bytes\n", (long) mLength);
|
||||
delete[] buf;
|
||||
return NULL;
|
||||
}
|
||||
fseek(mFp, oldPosn, SEEK_SET);
|
||||
}
|
||||
|
||||
ALOGV(" getBuffer: loaded into buffer\n");
|
||||
|
||||
mBuf = buf;
|
||||
return mBuf;
|
||||
} else {
|
||||
FileMap* map;
|
||||
|
||||
map = new FileMap;
|
||||
if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
|
||||
map->release();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALOGV(" getBuffer: mapped\n");
|
||||
|
||||
mMap = map;
|
||||
if (!wordAligned) {
|
||||
return mMap->getDataPtr();
|
||||
}
|
||||
return ensureAlignment(mMap);
|
||||
}
|
||||
}
|
||||
|
||||
int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
|
||||
{
|
||||
if (mMap != NULL) {
|
||||
const char* fname = mMap->getFileName();
|
||||
if (fname == NULL) {
|
||||
fname = mFileName;
|
||||
}
|
||||
if (fname == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*outStart = mMap->getDataOffset();
|
||||
*outLength = mMap->getDataLength();
|
||||
return open(fname, O_RDONLY | O_BINARY);
|
||||
}
|
||||
if (mFileName == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*outStart = mStart;
|
||||
*outLength = mLength;
|
||||
return open(mFileName, O_RDONLY | O_BINARY);
|
||||
}
|
||||
|
||||
const void* _FileAsset::ensureAlignment(FileMap* map)
|
||||
{
|
||||
void* data = map->getDataPtr();
|
||||
if ((((size_t)data)&0x3) == 0) {
|
||||
// We can return this directly if it is aligned on a word
|
||||
// boundary.
|
||||
ALOGV("Returning aligned FileAsset %p (%s).", this,
|
||||
getAssetSource());
|
||||
return data;
|
||||
}
|
||||
// If not aligned on a word boundary, then we need to copy it into
|
||||
// our own buffer.
|
||||
ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
|
||||
getAssetSource(), (int)mLength);
|
||||
unsigned char* buf = new unsigned char[mLength];
|
||||
if (buf == NULL) {
|
||||
ALOGE("alloc of %ld bytes failed\n", (long) mLength);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(buf, data, mLength);
|
||||
mBuf = buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* _CompressedAsset
|
||||
* ===========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Constructor.
|
||||
*/
|
||||
_CompressedAsset::_CompressedAsset(void)
|
||||
: mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
|
||||
mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Destructor. Release resources.
|
||||
*/
|
||||
_CompressedAsset::~_CompressedAsset(void)
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Open a chunk of compressed data inside a file.
|
||||
*
|
||||
* This currently just sets up some values and returns. On the first
|
||||
* read, we expand the entire file into a buffer and return data from it.
|
||||
*/
|
||||
status_t _CompressedAsset::openChunk(int fd, off64_t offset,
|
||||
int compressionMethod, size_t uncompressedLen, size_t compressedLen)
|
||||
{
|
||||
assert(mFd < 0); // no re-open
|
||||
assert(mMap == NULL);
|
||||
assert(fd >= 0);
|
||||
assert(offset >= 0);
|
||||
assert(compressedLen > 0);
|
||||
|
||||
if (compressionMethod != ZipFileRO::kCompressDeflated) {
|
||||
assert(false);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mStart = offset;
|
||||
mCompressedLen = compressedLen;
|
||||
mUncompressedLen = uncompressedLen;
|
||||
assert(mOffset == 0);
|
||||
mFd = fd;
|
||||
assert(mBuf == NULL);
|
||||
|
||||
if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
|
||||
mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open a chunk of compressed data in a mapped region.
|
||||
*
|
||||
* Nothing is expanded until the first read call.
|
||||
*/
|
||||
status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
|
||||
size_t uncompressedLen)
|
||||
{
|
||||
assert(mFd < 0); // no re-open
|
||||
assert(mMap == NULL);
|
||||
assert(dataMap != NULL);
|
||||
|
||||
if (compressionMethod != ZipFileRO::kCompressDeflated) {
|
||||
assert(false);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
mMap = dataMap;
|
||||
mStart = -1; // not used
|
||||
mCompressedLen = dataMap->getDataLength();
|
||||
mUncompressedLen = uncompressedLen;
|
||||
assert(mOffset == 0);
|
||||
|
||||
if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
|
||||
mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from a chunk of compressed data.
|
||||
*
|
||||
* [For now, that's just copying data out of a buffer.]
|
||||
*/
|
||||
ssize_t _CompressedAsset::read(void* buf, size_t count)
|
||||
{
|
||||
size_t maxLen;
|
||||
size_t actual;
|
||||
|
||||
assert(mOffset >= 0 && mOffset <= mUncompressedLen);
|
||||
|
||||
/* If we're relying on a streaming inflater, go through that */
|
||||
if (mZipInflater) {
|
||||
actual = mZipInflater->read(buf, count);
|
||||
} else {
|
||||
if (mBuf == NULL) {
|
||||
if (getBuffer(false) == NULL)
|
||||
return -1;
|
||||
}
|
||||
assert(mBuf != NULL);
|
||||
|
||||
/* adjust count if we're near EOF */
|
||||
maxLen = mUncompressedLen - mOffset;
|
||||
if (count > maxLen)
|
||||
count = maxLen;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
/* copy from buffer */
|
||||
//printf("comp buf read\n");
|
||||
memcpy(buf, (char*)mBuf + mOffset, count);
|
||||
actual = count;
|
||||
}
|
||||
|
||||
mOffset += actual;
|
||||
return actual;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a seek request.
|
||||
*
|
||||
* If we're working in a streaming mode, this is going to be fairly
|
||||
* expensive, because it requires plowing through a bunch of compressed
|
||||
* data.
|
||||
*/
|
||||
off64_t _CompressedAsset::seek(off64_t offset, int whence)
|
||||
{
|
||||
off64_t newPosn;
|
||||
|
||||
// compute new position within chunk
|
||||
newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
|
||||
if (newPosn == (off64_t) -1)
|
||||
return newPosn;
|
||||
|
||||
if (mZipInflater) {
|
||||
mZipInflater->seekAbsolute(newPosn);
|
||||
}
|
||||
mOffset = newPosn;
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the asset.
|
||||
*/
|
||||
void _CompressedAsset::close(void)
|
||||
{
|
||||
if (mMap != NULL) {
|
||||
mMap->release();
|
||||
mMap = NULL;
|
||||
}
|
||||
|
||||
delete[] mBuf;
|
||||
mBuf = NULL;
|
||||
|
||||
delete mZipInflater;
|
||||
mZipInflater = NULL;
|
||||
|
||||
if (mFd > 0) {
|
||||
::close(mFd);
|
||||
mFd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a pointer to a read-only buffer of data.
|
||||
*
|
||||
* The first time this is called, we expand the compressed data into a
|
||||
* buffer.
|
||||
*/
|
||||
const void* _CompressedAsset::getBuffer(bool)
|
||||
{
|
||||
unsigned char* buf = NULL;
|
||||
|
||||
if (mBuf != NULL)
|
||||
return mBuf;
|
||||
|
||||
/*
|
||||
* Allocate a buffer and read the file into it.
|
||||
*/
|
||||
buf = new unsigned char[mUncompressedLen];
|
||||
if (buf == NULL) {
|
||||
ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (mMap != NULL) {
|
||||
if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
|
||||
mUncompressedLen, mCompressedLen))
|
||||
goto bail;
|
||||
} else {
|
||||
assert(mFd >= 0);
|
||||
|
||||
/*
|
||||
* Seek to the start of the compressed data.
|
||||
*/
|
||||
if (lseek(mFd, mStart, SEEK_SET) != mStart)
|
||||
goto bail;
|
||||
|
||||
/*
|
||||
* Expand the data into it.
|
||||
*/
|
||||
if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
|
||||
mCompressedLen))
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Success - now that we have the full asset in RAM we
|
||||
* no longer need the streaming inflater
|
||||
*/
|
||||
delete mZipInflater;
|
||||
mZipInflater = NULL;
|
||||
|
||||
mBuf = buf;
|
||||
buf = NULL;
|
||||
|
||||
bail:
|
||||
delete[] buf;
|
||||
return mBuf;
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Provide access to a virtual directory in "asset space". Most of the
|
||||
// implementation is in the header file or in friend functions in
|
||||
// AssetManager.
|
||||
//
|
||||
#include <androidfw/AssetDir.h>
|
||||
|
||||
using namespace android;
|
||||
|
||||
|
||||
/*
|
||||
* Find a matching entry in a vector of FileInfo. Because it's sorted, we
|
||||
* can use a binary search.
|
||||
*
|
||||
* Assumes the vector is sorted in ascending order.
|
||||
*/
|
||||
/*static*/ int AssetDir::FileInfo::findEntry(const SortedVector<FileInfo>* pVector,
|
||||
const String8& fileName)
|
||||
{
|
||||
FileInfo tmpInfo;
|
||||
|
||||
tmpInfo.setFileName(fileName);
|
||||
return pVector->indexOf(tmpInfo);
|
||||
|
||||
#if 0 // don't need this after all (uses 1/2 compares of SortedVector though)
|
||||
int lo, hi, cur;
|
||||
|
||||
lo = 0;
|
||||
hi = pVector->size() -1;
|
||||
while (lo <= hi) {
|
||||
int cmp;
|
||||
|
||||
cur = (hi + lo) / 2;
|
||||
cmp = strcmp(pVector->itemAt(cur).getFileName(), fileName);
|
||||
if (cmp == 0) {
|
||||
/* match, bail */
|
||||
return cur;
|
||||
} else if (cmp < 0) {
|
||||
/* too low */
|
||||
lo = cur + 1;
|
||||
} else {
|
||||
/* too high */
|
||||
hi = cur -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,382 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 "backup_data"
|
||||
|
||||
#include <androidfw/BackupHelpers.h>
|
||||
#include <utils/ByteOrder.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/log.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
static const bool DEBUG = false;
|
||||
|
||||
/*
|
||||
* File Format (v1):
|
||||
*
|
||||
* All ints are stored little-endian.
|
||||
*
|
||||
* - An app_header_v1 struct.
|
||||
* - The name of the package, utf-8, null terminated, padded to 4-byte boundary.
|
||||
* - A sequence of zero or more key/value paires (entities), each with
|
||||
* - A entity_header_v1 struct
|
||||
* - The key, utf-8, null terminated, padded to 4-byte boundary.
|
||||
* - The value, padded to 4 byte boundary
|
||||
*/
|
||||
|
||||
const static int ROUND_UP[4] = { 0, 3, 2, 1 };
|
||||
|
||||
static inline size_t
|
||||
round_up(size_t n)
|
||||
{
|
||||
return n + ROUND_UP[n % 4];
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
padding_extra(size_t n)
|
||||
{
|
||||
return ROUND_UP[n % 4];
|
||||
}
|
||||
|
||||
BackupDataWriter::BackupDataWriter(int fd)
|
||||
:m_fd(fd),
|
||||
m_status(NO_ERROR),
|
||||
m_pos(0),
|
||||
m_entityCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
BackupDataWriter::~BackupDataWriter()
|
||||
{
|
||||
}
|
||||
|
||||
// Pad out anything they've previously written to the next 4 byte boundary.
|
||||
status_t
|
||||
BackupDataWriter::write_padding_for(int n)
|
||||
{
|
||||
ssize_t amt;
|
||||
ssize_t paddingSize;
|
||||
|
||||
paddingSize = padding_extra(n);
|
||||
if (paddingSize > 0) {
|
||||
uint32_t padding = 0xbcbcbcbc;
|
||||
if (DEBUG) ALOGI("writing %d padding bytes for %d", paddingSize, n);
|
||||
amt = write(m_fd, &padding, paddingSize);
|
||||
if (amt != paddingSize) {
|
||||
m_status = errno;
|
||||
return m_status;
|
||||
}
|
||||
m_pos += amt;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t
|
||||
BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize)
|
||||
{
|
||||
if (m_status != NO_ERROR) {
|
||||
return m_status;
|
||||
}
|
||||
|
||||
ssize_t amt;
|
||||
|
||||
amt = write_padding_for(m_pos);
|
||||
if (amt != 0) {
|
||||
return amt;
|
||||
}
|
||||
|
||||
String8 k;
|
||||
if (m_keyPrefix.length() > 0) {
|
||||
k = m_keyPrefix;
|
||||
k += ":";
|
||||
k += key;
|
||||
} else {
|
||||
k = key;
|
||||
}
|
||||
if (DEBUG) {
|
||||
ALOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(),
|
||||
key.string(), dataSize);
|
||||
}
|
||||
|
||||
entity_header_v1 header;
|
||||
ssize_t keyLen;
|
||||
|
||||
keyLen = k.length();
|
||||
|
||||
header.type = tolel(BACKUP_HEADER_ENTITY_V1);
|
||||
header.keyLen = tolel(keyLen);
|
||||
header.dataSize = tolel(dataSize);
|
||||
|
||||
if (DEBUG) ALOGI("writing entity header, %d bytes", sizeof(entity_header_v1));
|
||||
amt = write(m_fd, &header, sizeof(entity_header_v1));
|
||||
if (amt != sizeof(entity_header_v1)) {
|
||||
m_status = errno;
|
||||
return m_status;
|
||||
}
|
||||
m_pos += amt;
|
||||
|
||||
if (DEBUG) ALOGI("writing entity header key, %d bytes", keyLen+1);
|
||||
amt = write(m_fd, k.string(), keyLen+1);
|
||||
if (amt != keyLen+1) {
|
||||
m_status = errno;
|
||||
return m_status;
|
||||
}
|
||||
m_pos += amt;
|
||||
|
||||
amt = write_padding_for(keyLen+1);
|
||||
|
||||
m_entityCount++;
|
||||
|
||||
return amt;
|
||||
}
|
||||
|
||||
status_t
|
||||
BackupDataWriter::WriteEntityData(const void* data, size_t size)
|
||||
{
|
||||
if (DEBUG) ALOGD("Writing data: size=%lu", (unsigned long) size);
|
||||
|
||||
if (m_status != NO_ERROR) {
|
||||
if (DEBUG) {
|
||||
ALOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status));
|
||||
}
|
||||
return m_status;
|
||||
}
|
||||
|
||||
// We don't write padding here, because they're allowed to call this several
|
||||
// times with smaller buffers. We write it at the end of WriteEntityHeader
|
||||
// instead.
|
||||
ssize_t amt = write(m_fd, data, size);
|
||||
if (amt != (ssize_t)size) {
|
||||
m_status = errno;
|
||||
if (DEBUG) ALOGD("write returned error %d (%s)", m_status, strerror(m_status));
|
||||
return m_status;
|
||||
}
|
||||
m_pos += amt;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void
|
||||
BackupDataWriter::SetKeyPrefix(const String8& keyPrefix)
|
||||
{
|
||||
m_keyPrefix = keyPrefix;
|
||||
}
|
||||
|
||||
|
||||
BackupDataReader::BackupDataReader(int fd)
|
||||
:m_fd(fd),
|
||||
m_done(false),
|
||||
m_status(NO_ERROR),
|
||||
m_pos(0),
|
||||
m_entityCount(0)
|
||||
{
|
||||
memset(&m_header, 0, sizeof(m_header));
|
||||
}
|
||||
|
||||
BackupDataReader::~BackupDataReader()
|
||||
{
|
||||
}
|
||||
|
||||
status_t
|
||||
BackupDataReader::Status()
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
#define CHECK_SIZE(actual, expected) \
|
||||
do { \
|
||||
if ((actual) != (expected)) { \
|
||||
if ((actual) == 0) { \
|
||||
m_status = EIO; \
|
||||
m_done = true; \
|
||||
} else { \
|
||||
m_status = errno; \
|
||||
ALOGD("CHECK_SIZE(a=%ld e=%ld) failed at line %d m_status='%s'", \
|
||||
long(actual), long(expected), __LINE__, strerror(m_status)); \
|
||||
} \
|
||||
return m_status; \
|
||||
} \
|
||||
} while(0)
|
||||
#define SKIP_PADDING() \
|
||||
do { \
|
||||
status_t err = skip_padding(); \
|
||||
if (err != NO_ERROR) { \
|
||||
ALOGD("SKIP_PADDING FAILED at line %d", __LINE__); \
|
||||
m_status = err; \
|
||||
return err; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
status_t
|
||||
BackupDataReader::ReadNextHeader(bool* done, int* type)
|
||||
{
|
||||
*done = m_done;
|
||||
if (m_status != NO_ERROR) {
|
||||
return m_status;
|
||||
}
|
||||
|
||||
int amt;
|
||||
|
||||
amt = skip_padding();
|
||||
if (amt == EIO) {
|
||||
*done = m_done = true;
|
||||
return NO_ERROR;
|
||||
}
|
||||
else if (amt != NO_ERROR) {
|
||||
return amt;
|
||||
}
|
||||
amt = read(m_fd, &m_header, sizeof(m_header));
|
||||
*done = m_done = (amt == 0);
|
||||
if (*done) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
CHECK_SIZE(amt, sizeof(m_header));
|
||||
m_pos += sizeof(m_header);
|
||||
if (type) {
|
||||
*type = m_header.type;
|
||||
}
|
||||
|
||||
// validate and fix up the fields.
|
||||
m_header.type = fromlel(m_header.type);
|
||||
switch (m_header.type)
|
||||
{
|
||||
case BACKUP_HEADER_ENTITY_V1:
|
||||
{
|
||||
m_header.entity.keyLen = fromlel(m_header.entity.keyLen);
|
||||
if (m_header.entity.keyLen <= 0) {
|
||||
ALOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos,
|
||||
(int)m_header.entity.keyLen);
|
||||
m_status = EINVAL;
|
||||
}
|
||||
m_header.entity.dataSize = fromlel(m_header.entity.dataSize);
|
||||
m_entityCount++;
|
||||
|
||||
// read the rest of the header (filename)
|
||||
size_t size = m_header.entity.keyLen;
|
||||
char* buf = m_key.lockBuffer(size);
|
||||
if (buf == NULL) {
|
||||
m_status = ENOMEM;
|
||||
return m_status;
|
||||
}
|
||||
int amt = read(m_fd, buf, size+1);
|
||||
CHECK_SIZE(amt, (int)size+1);
|
||||
m_key.unlockBuffer(size);
|
||||
m_pos += size+1;
|
||||
SKIP_PADDING();
|
||||
m_dataEndPos = m_pos + m_header.entity.dataSize;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ALOGD("Chunk header at %d has invalid type: 0x%08x",
|
||||
(int)(m_pos - sizeof(m_header)), (int)m_header.type);
|
||||
m_status = EINVAL;
|
||||
}
|
||||
|
||||
return m_status;
|
||||
}
|
||||
|
||||
bool
|
||||
BackupDataReader::HasEntities()
|
||||
{
|
||||
return m_status == NO_ERROR && m_header.type == BACKUP_HEADER_ENTITY_V1;
|
||||
}
|
||||
|
||||
status_t
|
||||
BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize)
|
||||
{
|
||||
if (m_status != NO_ERROR) {
|
||||
return m_status;
|
||||
}
|
||||
if (m_header.type != BACKUP_HEADER_ENTITY_V1) {
|
||||
return EINVAL;
|
||||
}
|
||||
*key = m_key;
|
||||
*dataSize = m_header.entity.dataSize;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t
|
||||
BackupDataReader::SkipEntityData()
|
||||
{
|
||||
if (m_status != NO_ERROR) {
|
||||
return m_status;
|
||||
}
|
||||
if (m_header.type != BACKUP_HEADER_ENTITY_V1) {
|
||||
return EINVAL;
|
||||
}
|
||||
if (m_header.entity.dataSize > 0) {
|
||||
int pos = lseek(m_fd, m_dataEndPos, SEEK_SET);
|
||||
if (pos == -1) {
|
||||
return errno;
|
||||
}
|
||||
m_pos = pos;
|
||||
}
|
||||
SKIP_PADDING();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
BackupDataReader::ReadEntityData(void* data, size_t size)
|
||||
{
|
||||
if (m_status != NO_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
int remaining = m_dataEndPos - m_pos;
|
||||
//ALOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n",
|
||||
// size, m_pos, m_dataEndPos, remaining);
|
||||
if (remaining <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (((int)size) > remaining) {
|
||||
size = remaining;
|
||||
}
|
||||
//ALOGD(" reading %d bytes", size);
|
||||
int amt = read(m_fd, data, size);
|
||||
if (amt < 0) {
|
||||
m_status = errno;
|
||||
return -1;
|
||||
}
|
||||
if (amt == 0) {
|
||||
m_status = EIO;
|
||||
m_done = true;
|
||||
}
|
||||
m_pos += amt;
|
||||
return amt;
|
||||
}
|
||||
|
||||
status_t
|
||||
BackupDataReader::skip_padding()
|
||||
{
|
||||
ssize_t amt;
|
||||
ssize_t paddingSize;
|
||||
|
||||
paddingSize = padding_extra(m_pos);
|
||||
if (paddingSize > 0) {
|
||||
uint32_t padding;
|
||||
amt = read(m_fd, &padding, paddingSize);
|
||||
CHECK_SIZE(amt, paddingSize);
|
||||
m_pos += amt;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
} // namespace android
|
File diff suppressed because it is too large
Load Diff
@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2007 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.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "CursorWindow"
|
||||
|
||||
#include <androidfw/CursorWindow.h>
|
||||
#include <binder/Parcel.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <cutils/ashmem.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
CursorWindow::CursorWindow(const String8& name, int ashmemFd,
|
||||
void* data, size_t size, bool readOnly) :
|
||||
mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
|
||||
mHeader = static_cast<Header*>(mData);
|
||||
}
|
||||
|
||||
CursorWindow::~CursorWindow() {
|
||||
::munmap(mData, mSize);
|
||||
::close(mAshmemFd);
|
||||
}
|
||||
|
||||
status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
|
||||
String8 ashmemName("CursorWindow: ");
|
||||
ashmemName.append(name);
|
||||
|
||||
status_t result;
|
||||
int ashmemFd = ashmem_create_region(ashmemName.string(), size);
|
||||
if (ashmemFd < 0) {
|
||||
result = -errno;
|
||||
} else {
|
||||
result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
|
||||
if (result >= 0) {
|
||||
void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
result = -errno;
|
||||
} else {
|
||||
result = ashmem_set_prot_region(ashmemFd, PROT_READ);
|
||||
if (result >= 0) {
|
||||
CursorWindow* window = new CursorWindow(name, ashmemFd,
|
||||
data, size, false /*readOnly*/);
|
||||
result = window->clear();
|
||||
if (!result) {
|
||||
LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
|
||||
"numRows=%d, numColumns=%d, mSize=%d, mData=%p",
|
||||
window->mHeader->freeOffset,
|
||||
window->mHeader->numRows,
|
||||
window->mHeader->numColumns,
|
||||
window->mSize, window->mData);
|
||||
*outCursorWindow = window;
|
||||
return OK;
|
||||
}
|
||||
delete window;
|
||||
}
|
||||
}
|
||||
::munmap(data, size);
|
||||
}
|
||||
::close(ashmemFd);
|
||||
}
|
||||
*outCursorWindow = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
|
||||
String8 name = parcel->readString8();
|
||||
|
||||
status_t result;
|
||||
int ashmemFd = parcel->readFileDescriptor();
|
||||
if (ashmemFd == int(BAD_TYPE)) {
|
||||
result = BAD_TYPE;
|
||||
} else {
|
||||
ssize_t size = ashmem_get_size_region(ashmemFd);
|
||||
if (size < 0) {
|
||||
result = UNKNOWN_ERROR;
|
||||
} else {
|
||||
int dupAshmemFd = ::dup(ashmemFd);
|
||||
if (dupAshmemFd < 0) {
|
||||
result = -errno;
|
||||
} else {
|
||||
void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
result = -errno;
|
||||
} else {
|
||||
CursorWindow* window = new CursorWindow(name, dupAshmemFd,
|
||||
data, size, true /*readOnly*/);
|
||||
LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
|
||||
"numRows=%d, numColumns=%d, mSize=%d, mData=%p",
|
||||
window->mHeader->freeOffset,
|
||||
window->mHeader->numRows,
|
||||
window->mHeader->numColumns,
|
||||
window->mSize, window->mData);
|
||||
*outCursorWindow = window;
|
||||
return OK;
|
||||
}
|
||||
::close(dupAshmemFd);
|
||||
}
|
||||
}
|
||||
}
|
||||
*outCursorWindow = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t CursorWindow::writeToParcel(Parcel* parcel) {
|
||||
status_t status = parcel->writeString8(mName);
|
||||
if (!status) {
|
||||
status = parcel->writeDupFileDescriptor(mAshmemFd);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t CursorWindow::clear() {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
|
||||
mHeader->firstChunkOffset = sizeof(Header);
|
||||
mHeader->numRows = 0;
|
||||
mHeader->numColumns = 0;
|
||||
|
||||
RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
|
||||
firstChunk->nextChunkOffset = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t CursorWindow::setNumColumns(uint32_t numColumns) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
uint32_t cur = mHeader->numColumns;
|
||||
if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
|
||||
ALOGE("Trying to go from %d columns to %d", cur, numColumns);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
mHeader->numColumns = numColumns;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t CursorWindow::allocRow() {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
// Fill in the row slot
|
||||
RowSlot* rowSlot = allocRowSlot();
|
||||
if (rowSlot == NULL) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
// Allocate the slots for the field directory
|
||||
size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
|
||||
uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
|
||||
if (!fieldDirOffset) {
|
||||
mHeader->numRows--;
|
||||
LOG_WINDOW("The row failed, so back out the new row accounting "
|
||||
"from allocRowSlot %d", mHeader->numRows);
|
||||
return NO_MEMORY;
|
||||
}
|
||||
FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
|
||||
memset(fieldDir, 0, fieldDirSize);
|
||||
|
||||
LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n",
|
||||
mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
|
||||
rowSlot->offset = fieldDirOffset;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t CursorWindow::freeLastRow() {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
if (mHeader->numRows > 0) {
|
||||
mHeader->numRows--;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
uint32_t CursorWindow::alloc(size_t size, bool aligned) {
|
||||
uint32_t padding;
|
||||
if (aligned) {
|
||||
// 4 byte alignment
|
||||
padding = (~mHeader->freeOffset + 1) & 3;
|
||||
} else {
|
||||
padding = 0;
|
||||
}
|
||||
|
||||
uint32_t offset = mHeader->freeOffset + padding;
|
||||
uint32_t nextFreeOffset = offset + size;
|
||||
if (nextFreeOffset > mSize) {
|
||||
ALOGW("Window is full: requested allocation %d bytes, "
|
||||
"free space %d bytes, window size %d bytes",
|
||||
size, freeSpace(), mSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mHeader->freeOffset = nextFreeOffset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
|
||||
uint32_t chunkPos = row;
|
||||
RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
|
||||
offsetToPtr(mHeader->firstChunkOffset));
|
||||
while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
|
||||
chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
|
||||
chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
}
|
||||
return &chunk->slots[chunkPos];
|
||||
}
|
||||
|
||||
CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
|
||||
uint32_t chunkPos = mHeader->numRows;
|
||||
RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
|
||||
offsetToPtr(mHeader->firstChunkOffset));
|
||||
while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
|
||||
chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
|
||||
chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
}
|
||||
if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
|
||||
if (!chunk->nextChunkOffset) {
|
||||
chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
|
||||
if (!chunk->nextChunkOffset) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
|
||||
chunk->nextChunkOffset = 0;
|
||||
chunkPos = 0;
|
||||
}
|
||||
mHeader->numRows += 1;
|
||||
return &chunk->slots[chunkPos];
|
||||
}
|
||||
|
||||
CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
|
||||
if (row >= mHeader->numRows || column >= mHeader->numColumns) {
|
||||
ALOGE("Failed to read row %d, column %d from a CursorWindow which "
|
||||
"has %d rows, %d columns.",
|
||||
row, column, mHeader->numRows, mHeader->numColumns);
|
||||
return NULL;
|
||||
}
|
||||
RowSlot* rowSlot = getRowSlot(row);
|
||||
if (!rowSlot) {
|
||||
ALOGE("Failed to find rowSlot for row %d.", row);
|
||||
return NULL;
|
||||
}
|
||||
FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
|
||||
return &fieldDir[column];
|
||||
}
|
||||
|
||||
status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
|
||||
return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
|
||||
}
|
||||
|
||||
status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
|
||||
size_t sizeIncludingNull) {
|
||||
return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
|
||||
}
|
||||
|
||||
status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
|
||||
const void* value, size_t size, int32_t type) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
||||
if (!fieldSlot) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
uint32_t offset = alloc(size);
|
||||
if (!offset) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(offsetToPtr(offset), value, size);
|
||||
|
||||
fieldSlot->type = type;
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = size;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
||||
if (!fieldSlot) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
fieldSlot->type = FIELD_TYPE_INTEGER;
|
||||
fieldSlot->data.l = value;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
||||
if (!fieldSlot) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
fieldSlot->type = FIELD_TYPE_FLOAT;
|
||||
fieldSlot->data.d = value;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
|
||||
if (mReadOnly) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
FieldSlot* fieldSlot = getFieldSlot(row, column);
|
||||
if (!fieldSlot) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
fieldSlot->type = FIELD_TYPE_NULL;
|
||||
fieldSlot->data.buffer.offset = 0;
|
||||
fieldSlot->data.buffer.size = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
}; // namespace android
|
@ -1,190 +0,0 @@
|
||||
|
||||
Copyright (c) 2005-2008, 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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
@ -1,345 +0,0 @@
|
||||
/*
|
||||
* 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 <androidfw/ObbFile.h>
|
||||
#include <utils/Compat.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
//#define DEBUG 1
|
||||
|
||||
#define kFooterTagSize 8 /* last two 32-bit integers */
|
||||
|
||||
#define kFooterMinSize 33 /* 32-bit signature version (4 bytes)
|
||||
* 32-bit package version (4 bytes)
|
||||
* 32-bit flags (4 bytes)
|
||||
* 64-bit salt (8 bytes)
|
||||
* 32-bit package name size (4 bytes)
|
||||
* >=1-character package name (1 byte)
|
||||
* 32-bit footer size (4 bytes)
|
||||
* 32-bit footer marker (4 bytes)
|
||||
*/
|
||||
|
||||
#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 kFlagsOffset 8
|
||||
#define kSaltOffset 12
|
||||
#define kPackageNameLenOffset 20
|
||||
#define kPackageNameOffset 24
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
ObbFile::ObbFile()
|
||||
: mPackageName("")
|
||||
, mVersion(-1)
|
||||
, mFlags(0)
|
||||
{
|
||||
memset(mSalt, 0, sizeof(mSalt));
|
||||
}
|
||||
|
||||
ObbFile::~ObbFile() {
|
||||
}
|
||||
|
||||
bool ObbFile::readFrom(const char* filename)
|
||||
{
|
||||
int fd;
|
||||
bool success = false;
|
||||
|
||||
fd = ::open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ALOGW("couldn't open file %s: %s", filename, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
success = readFrom(fd);
|
||||
close(fd);
|
||||
|
||||
if (!success) {
|
||||
ALOGW("failed to read from %s (fd=%d)\n", filename, fd);
|
||||
}
|
||||
|
||||
out:
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ObbFile::readFrom(int fd)
|
||||
{
|
||||
if (fd < 0) {
|
||||
ALOGW("attempt to read from invalid fd\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return parseObbFile(fd);
|
||||
}
|
||||
|
||||
bool ObbFile::parseObbFile(int fd)
|
||||
{
|
||||
off64_t fileLength = lseek64(fd, 0, SEEK_END);
|
||||
|
||||
if (fileLength < kFooterMinSize) {
|
||||
if (fileLength < 0) {
|
||||
ALOGW("error seeking in ObbFile: %s\n", strerror(errno));
|
||||
} else {
|
||||
ALOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t actual;
|
||||
size_t footerSize;
|
||||
|
||||
{
|
||||
lseek64(fd, fileLength - kFooterTagSize, SEEK_SET);
|
||||
|
||||
char footer[kFooterTagSize];
|
||||
actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize));
|
||||
if (actual != kFooterTagSize) {
|
||||
ALOGW("couldn't read footer signature: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t));
|
||||
if (fileSig != kSignature) {
|
||||
ALOGW("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) {
|
||||
ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n",
|
||||
footerSize, fileLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (footerSize < (kFooterMinSize - kFooterTagSize)) {
|
||||
ALOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n",
|
||||
footerSize, kFooterMinSize - kFooterTagSize);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
off64_t fileOffset = fileLength - footerSize - kFooterTagSize;
|
||||
if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) {
|
||||
ALOGW("seek %lld failed: %s\n", fileOffset, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
mFooterStart = fileOffset;
|
||||
|
||||
char* scanBuf = (char*)malloc(footerSize);
|
||||
if (scanBuf == NULL) {
|
||||
ALOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize));
|
||||
// readAmount is guaranteed to be less than kMaxBufSize
|
||||
if (actual != (ssize_t)footerSize) {
|
||||
ALOGI("couldn't read ObbFile footer: %s\n", strerror(errno));
|
||||
free(scanBuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (int i = 0; i < footerSize; ++i) {
|
||||
ALOGI("char: 0x%02x\n", scanBuf[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t sigVersion = get4LE((unsigned char*)scanBuf);
|
||||
if (sigVersion != kSigVersion) {
|
||||
ALOGW("Unsupported ObbFile version %d\n", sigVersion);
|
||||
free(scanBuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
|
||||
mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset);
|
||||
|
||||
memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt));
|
||||
|
||||
size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
|
||||
if (packageNameLen == 0
|
||||
|| packageNameLen > (footerSize - kPackageNameOffset)) {
|
||||
ALOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n",
|
||||
packageNameLen, footerSize - kPackageNameOffset);
|
||||
free(scanBuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset);
|
||||
mPackageName = String8(const_cast<char*>(packageName), packageNameLen);
|
||||
|
||||
free(scanBuf);
|
||||
|
||||
#ifdef DEBUG
|
||||
ALOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion);
|
||||
#endif
|
||||
|
||||
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) {
|
||||
ALOGW("failed to write to %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ObbFile::writeTo(int fd)
|
||||
{
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lseek64(fd, 0, SEEK_END);
|
||||
|
||||
if (mPackageName.size() == 0 || mVersion == -1) {
|
||||
ALOGW("tried to write uninitialized ObbFile data\n");
|
||||
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)) {
|
||||
ALOGW("couldn't write signature version: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
put4LE(intBuf, mVersion);
|
||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||
ALOGW("couldn't write package version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
put4LE(intBuf, mFlags);
|
||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||
ALOGW("couldn't write package version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) {
|
||||
ALOGW("couldn't write salt: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t packageNameLen = mPackageName.size();
|
||||
put4LE(intBuf, packageNameLen);
|
||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||
ALOGW("couldn't write package name length: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
|
||||
ALOGW("couldn't write package name: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
put4LE(intBuf, kPackageNameOffset + packageNameLen);
|
||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||
ALOGW("couldn't write footer size: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
put4LE(intBuf, kSignature);
|
||||
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
|
||||
ALOGW("couldn't write footer magic signature: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObbFile::removeFrom(const char* filename)
|
||||
{
|
||||
int fd;
|
||||
bool success = false;
|
||||
|
||||
fd = ::open(filename, O_RDWR);
|
||||
if (fd < 0) {
|
||||
goto out;
|
||||
}
|
||||
success = removeFrom(fd);
|
||||
close(fd);
|
||||
|
||||
out:
|
||||
if (!success) {
|
||||
ALOGW("failed to remove signature from %s: %s\n", filename, strerror(errno));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ObbFile::removeFrom(int fd)
|
||||
{
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readFrom(fd)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ftruncate(fd, mFooterStart);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,242 +0,0 @@
|
||||
/*
|
||||
* 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_NDEBUG 0
|
||||
#define LOG_TAG "szipinf"
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <androidfw/StreamingZipInflater.h>
|
||||
#include <utils/FileMap.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; }
|
||||
|
||||
using namespace android;
|
||||
|
||||
/*
|
||||
* Streaming access to compressed asset data in an open fd
|
||||
*/
|
||||
StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart,
|
||||
size_t uncompSize, size_t compSize) {
|
||||
mFd = fd;
|
||||
mDataMap = NULL;
|
||||
mInFileStart = compDataStart;
|
||||
mOutTotalSize = uncompSize;
|
||||
mInTotalSize = compSize;
|
||||
|
||||
mInBufSize = StreamingZipInflater::INPUT_CHUNK_SIZE;
|
||||
mInBuf = new uint8_t[mInBufSize];
|
||||
|
||||
mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
|
||||
mOutBuf = new uint8_t[mOutBufSize];
|
||||
|
||||
initInflateState();
|
||||
}
|
||||
|
||||
/*
|
||||
* Streaming access to compressed data held in an mmapped region of memory
|
||||
*/
|
||||
StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) {
|
||||
mFd = -1;
|
||||
mDataMap = dataMap;
|
||||
mOutTotalSize = uncompSize;
|
||||
mInTotalSize = dataMap->getDataLength();
|
||||
|
||||
mInBuf = (uint8_t*) dataMap->getDataPtr();
|
||||
mInBufSize = mInTotalSize;
|
||||
|
||||
mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
|
||||
mOutBuf = new uint8_t[mOutBufSize];
|
||||
|
||||
initInflateState();
|
||||
}
|
||||
|
||||
StreamingZipInflater::~StreamingZipInflater() {
|
||||
// tear down the in-flight zip state just in case
|
||||
::inflateEnd(&mInflateState);
|
||||
|
||||
if (mDataMap == NULL) {
|
||||
delete [] mInBuf;
|
||||
}
|
||||
delete [] mOutBuf;
|
||||
}
|
||||
|
||||
void StreamingZipInflater::initInflateState() {
|
||||
ALOGV("Initializing inflate state");
|
||||
|
||||
memset(&mInflateState, 0, sizeof(mInflateState));
|
||||
mInflateState.zalloc = Z_NULL;
|
||||
mInflateState.zfree = Z_NULL;
|
||||
mInflateState.opaque = Z_NULL;
|
||||
mInflateState.next_in = (Bytef*)mInBuf;
|
||||
mInflateState.next_out = (Bytef*) mOutBuf;
|
||||
mInflateState.avail_out = mOutBufSize;
|
||||
mInflateState.data_type = Z_UNKNOWN;
|
||||
|
||||
mOutLastDecoded = mOutDeliverable = mOutCurPosition = 0;
|
||||
mInNextChunkOffset = 0;
|
||||
mStreamNeedsInit = true;
|
||||
|
||||
if (mDataMap == NULL) {
|
||||
::lseek(mFd, mInFileStart, SEEK_SET);
|
||||
mInflateState.avail_in = 0; // set when a chunk is read in
|
||||
} else {
|
||||
mInflateState.avail_in = mInBufSize;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic approach:
|
||||
*
|
||||
* 1. If we have undelivered uncompressed data, send it. At this point
|
||||
* either we've satisfied the request, or we've exhausted the available
|
||||
* output data in mOutBuf.
|
||||
*
|
||||
* 2. While we haven't sent enough data to satisfy the request:
|
||||
* 0. if the request is for more data than exists, bail.
|
||||
* a. if there is no input data to decode, read some into the input buffer
|
||||
* and readjust the z_stream input pointers
|
||||
* b. point the output to the start of the output buffer and decode what we can
|
||||
* c. deliver whatever output data we can
|
||||
*/
|
||||
ssize_t StreamingZipInflater::read(void* outBuf, size_t count) {
|
||||
uint8_t* dest = (uint8_t*) outBuf;
|
||||
size_t bytesRead = 0;
|
||||
size_t toRead = min_of(count, size_t(mOutTotalSize - mOutCurPosition));
|
||||
while (toRead > 0) {
|
||||
// First, write from whatever we already have decoded and ready to go
|
||||
size_t deliverable = min_of(toRead, mOutLastDecoded - mOutDeliverable);
|
||||
if (deliverable > 0) {
|
||||
if (outBuf != NULL) memcpy(dest, mOutBuf + mOutDeliverable, deliverable);
|
||||
mOutDeliverable += deliverable;
|
||||
mOutCurPosition += deliverable;
|
||||
dest += deliverable;
|
||||
bytesRead += deliverable;
|
||||
toRead -= deliverable;
|
||||
}
|
||||
|
||||
// need more data? time to decode some.
|
||||
if (toRead > 0) {
|
||||
// if we don't have any data to decode, read some in. If we're working
|
||||
// from mmapped data this won't happen, because the clipping to total size
|
||||
// will prevent reading off the end of the mapped input chunk.
|
||||
if ((mInflateState.avail_in == 0) && (mDataMap == NULL)) {
|
||||
int err = readNextChunk();
|
||||
if (err < 0) {
|
||||
ALOGE("Unable to access asset data: %d", err);
|
||||
if (!mStreamNeedsInit) {
|
||||
::inflateEnd(&mInflateState);
|
||||
initInflateState();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// we know we've drained whatever is in the out buffer now, so just
|
||||
// start from scratch there, reading all the input we have at present.
|
||||
mInflateState.next_out = (Bytef*) mOutBuf;
|
||||
mInflateState.avail_out = mOutBufSize;
|
||||
|
||||
/*
|
||||
ALOGV("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p",
|
||||
mInflateState.avail_in, mInflateState.avail_out,
|
||||
mInflateState.next_in, mInflateState.next_out);
|
||||
*/
|
||||
int result = Z_OK;
|
||||
if (mStreamNeedsInit) {
|
||||
ALOGV("Initializing zlib to inflate");
|
||||
result = inflateInit2(&mInflateState, -MAX_WBITS);
|
||||
mStreamNeedsInit = false;
|
||||
}
|
||||
if (result == Z_OK) result = ::inflate(&mInflateState, Z_SYNC_FLUSH);
|
||||
if (result < 0) {
|
||||
// Whoops, inflation failed
|
||||
ALOGE("Error inflating asset: %d", result);
|
||||
::inflateEnd(&mInflateState);
|
||||
initInflateState();
|
||||
return -1;
|
||||
} else {
|
||||
if (result == Z_STREAM_END) {
|
||||
// we know we have to have reached the target size here and will
|
||||
// not try to read any further, so just wind things up.
|
||||
::inflateEnd(&mInflateState);
|
||||
}
|
||||
|
||||
// Note how much data we got, and off we go
|
||||
mOutDeliverable = 0;
|
||||
mOutLastDecoded = mOutBufSize - mInflateState.avail_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
int StreamingZipInflater::readNextChunk() {
|
||||
assert(mDataMap == NULL);
|
||||
|
||||
if (mInNextChunkOffset < mInTotalSize) {
|
||||
size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset);
|
||||
if (toRead > 0) {
|
||||
ssize_t didRead = TEMP_FAILURE_RETRY(::read(mFd, mInBuf, toRead));
|
||||
//ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead);
|
||||
if (didRead < 0) {
|
||||
ALOGE("Error reading asset data: %s", strerror(errno));
|
||||
return didRead;
|
||||
} else {
|
||||
mInNextChunkOffset += didRead;
|
||||
mInflateState.next_in = (Bytef*) mInBuf;
|
||||
mInflateState.avail_in = didRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// seeking backwards requires uncompressing fom the beginning, so is very
|
||||
// expensive. seeking forwards only requires uncompressing from the current
|
||||
// position to the destination.
|
||||
off64_t StreamingZipInflater::seekAbsolute(off64_t absoluteInputPosition) {
|
||||
if (absoluteInputPosition < mOutCurPosition) {
|
||||
// rewind and reprocess the data from the beginning
|
||||
if (!mStreamNeedsInit) {
|
||||
::inflateEnd(&mInflateState);
|
||||
}
|
||||
initInflateState();
|
||||
read(NULL, absoluteInputPosition);
|
||||
} else if (absoluteInputPosition > mOutCurPosition) {
|
||||
read(NULL, absoluteInputPosition - mOutCurPosition);
|
||||
}
|
||||
// else if the target position *is* our current position, do nothing
|
||||
return absoluteInputPosition;
|
||||
}
|
@ -1,249 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Read-only access to Zip archives, with minimal heap allocation.
|
||||
//
|
||||
#define LOG_TAG "zipro"
|
||||
//#define LOG_NDEBUG 0
|
||||
#include <androidfw/ZipFileRO.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Compat.h>
|
||||
#include <utils/misc.h>
|
||||
#include <utils/threads.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* We must open binary files using open(path, ... | O_BINARY) under Windows.
|
||||
* Otherwise strange read errors will happen.
|
||||
*/
|
||||
#ifndef O_BINARY
|
||||
# define O_BINARY 0
|
||||
#endif
|
||||
|
||||
using namespace android;
|
||||
|
||||
class _ZipEntryRO {
|
||||
public:
|
||||
ZipEntry entry;
|
||||
ZipEntryName name;
|
||||
void *cookie;
|
||||
|
||||
_ZipEntryRO() : cookie(NULL) {
|
||||
}
|
||||
|
||||
private:
|
||||
_ZipEntryRO(const _ZipEntryRO& other);
|
||||
_ZipEntryRO& operator=(const _ZipEntryRO& other);
|
||||
};
|
||||
|
||||
ZipFileRO::~ZipFileRO() {
|
||||
CloseArchive(mHandle);
|
||||
free(mFileName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the specified file read-only. We memory-map the entire thing and
|
||||
* close the file before returning.
|
||||
*/
|
||||
/* static */ ZipFileRO* ZipFileRO::open(const char* zipFileName)
|
||||
{
|
||||
ZipArchiveHandle handle;
|
||||
const int32_t error = OpenArchive(zipFileName, &handle);
|
||||
if (error) {
|
||||
ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new ZipFileRO(handle, strdup(zipFileName));
|
||||
}
|
||||
|
||||
|
||||
ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const
|
||||
{
|
||||
_ZipEntryRO* data = new _ZipEntryRO;
|
||||
const int32_t error = FindEntry(mHandle, entryName, &(data->entry));
|
||||
if (error) {
|
||||
delete data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->name.name = entryName;
|
||||
data->name.name_length = strlen(entryName);
|
||||
|
||||
return (ZipEntryRO) data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the useful fields from the zip entry.
|
||||
*
|
||||
* Returns "false" if the offsets to the fields or the contents of the fields
|
||||
* appear to be bogus.
|
||||
*/
|
||||
bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
|
||||
size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const
|
||||
{
|
||||
const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
||||
const ZipEntry& ze = zipEntry->entry;
|
||||
|
||||
if (pMethod != NULL) {
|
||||
*pMethod = ze.method;
|
||||
}
|
||||
if (pUncompLen != NULL) {
|
||||
*pUncompLen = ze.uncompressed_length;
|
||||
}
|
||||
if (pCompLen != NULL) {
|
||||
*pCompLen = ze.compressed_length;
|
||||
}
|
||||
if (pOffset != NULL) {
|
||||
*pOffset = ze.offset;
|
||||
}
|
||||
if (pModWhen != NULL) {
|
||||
*pModWhen = ze.mod_time;
|
||||
}
|
||||
if (pCrc32 != NULL) {
|
||||
*pCrc32 = ze.crc32;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZipFileRO::startIteration(void** cookie)
|
||||
{
|
||||
_ZipEntryRO* ze = new _ZipEntryRO;
|
||||
int32_t error = StartIteration(mHandle, &(ze->cookie), NULL /* prefix */);
|
||||
if (error) {
|
||||
ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error));
|
||||
delete ze;
|
||||
return false;
|
||||
}
|
||||
|
||||
*cookie = ze;
|
||||
return true;
|
||||
}
|
||||
|
||||
ZipEntryRO ZipFileRO::nextEntry(void* cookie)
|
||||
{
|
||||
_ZipEntryRO* ze = reinterpret_cast<_ZipEntryRO*>(cookie);
|
||||
int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name));
|
||||
if (error) {
|
||||
if (error != -1) {
|
||||
ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &(ze->entry);
|
||||
}
|
||||
|
||||
void ZipFileRO::endIteration(void* cookie)
|
||||
{
|
||||
delete reinterpret_cast<_ZipEntryRO*>(cookie);
|
||||
}
|
||||
|
||||
void ZipFileRO::releaseEntry(ZipEntryRO entry) const
|
||||
{
|
||||
delete reinterpret_cast<_ZipEntryRO*>(entry);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the entry's filename to the buffer.
|
||||
*/
|
||||
int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen)
|
||||
const
|
||||
{
|
||||
const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
||||
const uint16_t requiredSize = zipEntry->name.name_length + 1;
|
||||
|
||||
if (bufLen < requiredSize) {
|
||||
ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize);
|
||||
return requiredSize;
|
||||
}
|
||||
|
||||
memcpy(buffer, zipEntry->name.name, requiredSize - 1);
|
||||
buffer[requiredSize - 1] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new FileMap object that spans the data in "entry".
|
||||
*/
|
||||
FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
|
||||
{
|
||||
const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
||||
const ZipEntry& ze = zipEntry->entry;
|
||||
int fd = GetFileDescriptor(mHandle);
|
||||
size_t actualLen = 0;
|
||||
|
||||
if (ze.method == kCompressStored) {
|
||||
actualLen = ze.uncompressed_length;
|
||||
} else {
|
||||
actualLen = ze.compressed_length;
|
||||
}
|
||||
|
||||
FileMap* newMap = new FileMap();
|
||||
if (!newMap->create(mFileName, fd, ze.offset, actualLen, true)) {
|
||||
newMap->release();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Uncompress an entry, in its entirety, into the provided output buffer.
|
||||
*
|
||||
* This doesn't verify the data's CRC, which might be useful for
|
||||
* uncompressed data. The caller should be able to manage it.
|
||||
*/
|
||||
bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const
|
||||
{
|
||||
_ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
||||
const int32_t error = ExtractToMemory(mHandle, &(zipEntry->entry),
|
||||
(uint8_t*) buffer, size);
|
||||
if (error) {
|
||||
ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Uncompress an entry, in its entirety, to an open file descriptor.
|
||||
*
|
||||
* This doesn't verify the data's CRC, but probably should.
|
||||
*/
|
||||
bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const
|
||||
{
|
||||
_ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
|
||||
const int32_t error = ExtractEntryToFile(mHandle, &(zipEntry->entry), fd);
|
||||
if (error) {
|
||||
ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -1,330 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// Misc zip/gzip utility functions.
|
||||
//
|
||||
|
||||
#define LOG_TAG "ziputil"
|
||||
|
||||
#include <androidfw/ZipUtils.h>
|
||||
#include <androidfw/ZipFileRO.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Compat.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
using namespace android;
|
||||
|
||||
static inline unsigned long get4LE(const unsigned char* buf) {
|
||||
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||
}
|
||||
|
||||
|
||||
static const unsigned long kReadBufSize = 32768;
|
||||
|
||||
/*
|
||||
* Utility function that expands zip/gzip "deflate" compressed data
|
||||
* into a buffer.
|
||||
*
|
||||
* (This is a clone of the previous function, but it takes a FILE* instead
|
||||
* of an fd. We could pass fileno(fd) to the above, but we can run into
|
||||
* trouble when "fp" has a different notion of what fd's file position is.)
|
||||
*
|
||||
* "fp" is an open file positioned at the start of the "deflate" data
|
||||
* "buf" must hold at least "uncompressedLen" bytes.
|
||||
*/
|
||||
/*static*/ template<typename T> bool inflateToBuffer(T& reader, void* buf,
|
||||
long uncompressedLen, long compressedLen)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
z_stream zstream;
|
||||
int zerr;
|
||||
unsigned long compRemaining;
|
||||
|
||||
assert(uncompressedLen >= 0);
|
||||
assert(compressedLen >= 0);
|
||||
|
||||
compRemaining = compressedLen;
|
||||
|
||||
/*
|
||||
* Initialize the zlib stream.
|
||||
*/
|
||||
memset(&zstream, 0, sizeof(zstream));
|
||||
zstream.zalloc = Z_NULL;
|
||||
zstream.zfree = Z_NULL;
|
||||
zstream.opaque = Z_NULL;
|
||||
zstream.next_in = NULL;
|
||||
zstream.avail_in = 0;
|
||||
zstream.next_out = (Bytef*) buf;
|
||||
zstream.avail_out = uncompressedLen;
|
||||
zstream.data_type = Z_UNKNOWN;
|
||||
|
||||
/*
|
||||
* Use the undocumented "negative window bits" feature to tell zlib
|
||||
* that there's no zlib header waiting for it.
|
||||
*/
|
||||
zerr = inflateInit2(&zstream, -MAX_WBITS);
|
||||
if (zerr != Z_OK) {
|
||||
if (zerr == Z_VERSION_ERROR) {
|
||||
ALOGE("Installed zlib is not compatible with linked version (%s)\n",
|
||||
ZLIB_VERSION);
|
||||
} else {
|
||||
ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
|
||||
}
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop while we have data.
|
||||
*/
|
||||
do {
|
||||
unsigned long getSize;
|
||||
|
||||
/* read as much as we can */
|
||||
if (zstream.avail_in == 0) {
|
||||
getSize = (compRemaining > kReadBufSize) ?
|
||||
kReadBufSize : compRemaining;
|
||||
ALOGV("+++ reading %ld bytes (%ld left)\n",
|
||||
getSize, compRemaining);
|
||||
|
||||
unsigned char* nextBuffer = NULL;
|
||||
const unsigned long nextSize = reader.read(&nextBuffer, getSize);
|
||||
|
||||
if (nextSize < getSize || nextBuffer == NULL) {
|
||||
ALOGD("inflate read failed (%ld vs %ld)\n", nextSize, getSize);
|
||||
goto z_bail;
|
||||
}
|
||||
|
||||
compRemaining -= nextSize;
|
||||
|
||||
zstream.next_in = nextBuffer;
|
||||
zstream.avail_in = nextSize;
|
||||
}
|
||||
|
||||
/* uncompress the data */
|
||||
zerr = inflate(&zstream, Z_NO_FLUSH);
|
||||
if (zerr != Z_OK && zerr != Z_STREAM_END) {
|
||||
ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
|
||||
goto z_bail;
|
||||
}
|
||||
|
||||
/* output buffer holds all, so no need to write the output */
|
||||
} while (zerr == Z_OK);
|
||||
|
||||
assert(zerr == Z_STREAM_END); /* other errors should've been caught */
|
||||
|
||||
if ((long) zstream.total_out != uncompressedLen) {
|
||||
ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
|
||||
zstream.total_out, uncompressedLen);
|
||||
goto z_bail;
|
||||
}
|
||||
|
||||
// success!
|
||||
result = true;
|
||||
|
||||
z_bail:
|
||||
inflateEnd(&zstream); /* free up any allocated structures */
|
||||
|
||||
bail:
|
||||
return result;
|
||||
}
|
||||
|
||||
class FileReader {
|
||||
public:
|
||||
FileReader(FILE* fp) :
|
||||
mFp(fp), mReadBuf(new unsigned char[kReadBufSize])
|
||||
{
|
||||
}
|
||||
|
||||
~FileReader() {
|
||||
delete[] mReadBuf;
|
||||
}
|
||||
|
||||
long read(unsigned char** nextBuffer, long readSize) const {
|
||||
*nextBuffer = mReadBuf;
|
||||
return fread(mReadBuf, 1, readSize, mFp);
|
||||
}
|
||||
|
||||
FILE* mFp;
|
||||
unsigned char* mReadBuf;
|
||||
};
|
||||
|
||||
class FdReader {
|
||||
public:
|
||||
FdReader(int fd) :
|
||||
mFd(fd), mReadBuf(new unsigned char[kReadBufSize])
|
||||
{
|
||||
}
|
||||
|
||||
~FdReader() {
|
||||
delete[] mReadBuf;
|
||||
}
|
||||
|
||||
long read(unsigned char** nextBuffer, long readSize) const {
|
||||
*nextBuffer = mReadBuf;
|
||||
return TEMP_FAILURE_RETRY(::read(mFd, mReadBuf, readSize));
|
||||
}
|
||||
|
||||
int mFd;
|
||||
unsigned char* mReadBuf;
|
||||
};
|
||||
|
||||
class BufferReader {
|
||||
public:
|
||||
BufferReader(void* input, size_t inputSize) :
|
||||
mInput(reinterpret_cast<unsigned char*>(input)),
|
||||
mInputSize(inputSize),
|
||||
mBufferReturned(false)
|
||||
{
|
||||
}
|
||||
|
||||
long read(unsigned char** nextBuffer, long readSize) {
|
||||
if (!mBufferReturned) {
|
||||
mBufferReturned = true;
|
||||
*nextBuffer = mInput;
|
||||
return mInputSize;
|
||||
}
|
||||
|
||||
*nextBuffer = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char* mInput;
|
||||
const size_t mInputSize;
|
||||
bool mBufferReturned;
|
||||
};
|
||||
|
||||
/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
|
||||
long uncompressedLen, long compressedLen)
|
||||
{
|
||||
FileReader reader(fp);
|
||||
return ::inflateToBuffer<FileReader>(reader, buf,
|
||||
uncompressedLen, compressedLen);
|
||||
}
|
||||
|
||||
/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
|
||||
long uncompressedLen, long compressedLen)
|
||||
{
|
||||
FdReader reader(fd);
|
||||
return ::inflateToBuffer<FdReader>(reader, buf,
|
||||
uncompressedLen, compressedLen);
|
||||
}
|
||||
|
||||
/*static*/ bool ZipUtils::inflateToBuffer(void* in, void* buf,
|
||||
long uncompressedLen, long compressedLen)
|
||||
{
|
||||
BufferReader reader(in, compressedLen);
|
||||
return ::inflateToBuffer<BufferReader>(reader, buf,
|
||||
uncompressedLen, compressedLen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Look at the contents of a gzip archive. We want to know where the
|
||||
* data starts, and how long it will be after it is uncompressed.
|
||||
*
|
||||
* We expect to find the CRC and length as the last 8 bytes on the file.
|
||||
* This is a pretty reasonable thing to expect for locally-compressed
|
||||
* files, but there's a small chance that some extra padding got thrown
|
||||
* on (the man page talks about compressed data written to tape). We
|
||||
* don't currently deal with that here. If "gzip -l" whines, we're going
|
||||
* to fail too.
|
||||
*
|
||||
* On exit, "fp" is pointing at the start of the compressed data.
|
||||
*/
|
||||
/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod,
|
||||
long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32)
|
||||
{
|
||||
enum { // flags
|
||||
FTEXT = 0x01,
|
||||
FHCRC = 0x02,
|
||||
FEXTRA = 0x04,
|
||||
FNAME = 0x08,
|
||||
FCOMMENT = 0x10,
|
||||
};
|
||||
int ic;
|
||||
int method, flags;
|
||||
int i;
|
||||
|
||||
ic = getc(fp);
|
||||
if (ic != 0x1f || getc(fp) != 0x8b)
|
||||
return false; // not gzip
|
||||
method = getc(fp);
|
||||
flags = getc(fp);
|
||||
|
||||
/* quick sanity checks */
|
||||
if (method == EOF || flags == EOF)
|
||||
return false;
|
||||
if (method != ZipFileRO::kCompressDeflated)
|
||||
return false;
|
||||
|
||||
/* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */
|
||||
for (i = 0; i < 6; i++)
|
||||
(void) getc(fp);
|
||||
/* consume "extra" field, if present */
|
||||
if ((flags & FEXTRA) != 0) {
|
||||
int len;
|
||||
|
||||
len = getc(fp);
|
||||
len |= getc(fp) << 8;
|
||||
while (len-- && getc(fp) != EOF)
|
||||
;
|
||||
}
|
||||
/* consume filename, if present */
|
||||
if ((flags & FNAME) != 0) {
|
||||
do {
|
||||
ic = getc(fp);
|
||||
} while (ic != 0 && ic != EOF);
|
||||
}
|
||||
/* consume comment, if present */
|
||||
if ((flags & FCOMMENT) != 0) {
|
||||
do {
|
||||
ic = getc(fp);
|
||||
} while (ic != 0 && ic != EOF);
|
||||
}
|
||||
/* consume 16-bit header CRC, if present */
|
||||
if ((flags & FHCRC) != 0) {
|
||||
(void) getc(fp);
|
||||
(void) getc(fp);
|
||||
}
|
||||
|
||||
if (feof(fp) || ferror(fp))
|
||||
return false;
|
||||
|
||||
/* seek to the end; CRC and length are in the last 8 bytes */
|
||||
long curPosn = ftell(fp);
|
||||
unsigned char buf[8];
|
||||
fseek(fp, -8, SEEK_END);
|
||||
*pCompressedLen = ftell(fp) - curPosn;
|
||||
|
||||
if (fread(buf, 1, 8, fp) != 8)
|
||||
return false;
|
||||
/* seek back to start of compressed data */
|
||||
fseek(fp, curPosn, SEEK_SET);
|
||||
|
||||
*pCompressionMethod = method;
|
||||
*pCRC32 = get4LE(&buf[0]);
|
||||
*pUncompressedLen = get4LE(&buf[4]);
|
||||
|
||||
return true;
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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 "misc"
|
||||
|
||||
//
|
||||
// Miscellaneous utility functions.
|
||||
//
|
||||
#include <androidfw/misc.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace android;
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Get a file's type.
|
||||
*/
|
||||
FileType getFileType(const char* fileName)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (stat(fileName, &sb) < 0) {
|
||||
if (errno == ENOENT || errno == ENOTDIR)
|
||||
return kFileTypeNonexistent;
|
||||
else {
|
||||
fprintf(stderr, "getFileType got errno=%d on '%s'\n",
|
||||
errno, fileName);
|
||||
return kFileTypeUnknown;
|
||||
}
|
||||
} else {
|
||||
if (S_ISREG(sb.st_mode))
|
||||
return kFileTypeRegular;
|
||||
else if (S_ISDIR(sb.st_mode))
|
||||
return kFileTypeDirectory;
|
||||
else if (S_ISCHR(sb.st_mode))
|
||||
return kFileTypeCharDev;
|
||||
else if (S_ISBLK(sb.st_mode))
|
||||
return kFileTypeBlockDev;
|
||||
else if (S_ISFIFO(sb.st_mode))
|
||||
return kFileTypeFifo;
|
||||
#ifdef HAVE_SYMLINKS
|
||||
else if (S_ISLNK(sb.st_mode))
|
||||
return kFileTypeSymlink;
|
||||
else if (S_ISSOCK(sb.st_mode))
|
||||
return kFileTypeSocket;
|
||||
#endif
|
||||
else
|
||||
return kFileTypeUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a file's modification date.
|
||||
*/
|
||||
time_t getFileModDate(const char* fileName)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (stat(fileName, &sb) < 0)
|
||||
return (time_t) -1;
|
||||
|
||||
return sb.st_mtime;
|
||||
}
|
||||
|
||||
}; // namespace android
|
@ -1,32 +0,0 @@
|
||||
# Build the unit tests.
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Build the unit tests.
|
||||
test_src_files := \
|
||||
BackupData_test.cpp \
|
||||
ObbFile_test.cpp \
|
||||
ZipUtils_test.cpp
|
||||
|
||||
shared_libraries := \
|
||||
libandroidfw \
|
||||
libcutils \
|
||||
libutils \
|
||||
libui \
|
||||
libstlport
|
||||
|
||||
static_libraries := \
|
||||
libgtest \
|
||||
libgtest_main
|
||||
|
||||
$(foreach file,$(test_src_files), \
|
||||
$(eval include $(CLEAR_VARS)) \
|
||||
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
|
||||
$(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
|
||||
$(eval LOCAL_SRC_FILES := $(file)) \
|
||||
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
|
||||
$(eval include $(BUILD_NATIVE_TEST)) \
|
||||
)
|
||||
|
||||
# Build the manual test programs.
|
||||
include $(call all-makefiles-under, $(LOCAL_PATH))
|
@ -1,438 +0,0 @@
|
||||
/*
|
||||
* 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 <androidfw/BackupHelpers.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
#define TEST_FILENAME "/test.bd"
|
||||
|
||||
// keys of different lengths to test padding
|
||||
#define KEY1 "key1"
|
||||
#define KEY2 "key2a"
|
||||
#define KEY3 "key3bc"
|
||||
#define KEY4 "key4def"
|
||||
|
||||
// payloads of different lengths to test padding
|
||||
#define DATA1 "abcdefg"
|
||||
#define DATA2 "hijklmnopq"
|
||||
#define DATA3 "rstuvwxyz"
|
||||
// KEY4 is only ever deleted
|
||||
|
||||
class BackupDataTest : public testing::Test {
|
||||
protected:
|
||||
char* m_external_storage;
|
||||
char* m_filename;
|
||||
String8 mKey1;
|
||||
String8 mKey2;
|
||||
String8 mKey3;
|
||||
String8 mKey4;
|
||||
|
||||
virtual void SetUp() {
|
||||
m_external_storage = getenv("EXTERNAL_STORAGE");
|
||||
|
||||
const int totalLen = strlen(m_external_storage) + strlen(TEST_FILENAME) + 1;
|
||||
m_filename = new char[totalLen];
|
||||
snprintf(m_filename, totalLen, "%s%s", m_external_storage, TEST_FILENAME);
|
||||
|
||||
::unlink(m_filename);
|
||||
int fd = ::open(m_filename, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
FAIL() << "Couldn't create " << m_filename << " for writing";
|
||||
}
|
||||
mKey1 = String8(KEY1);
|
||||
mKey2 = String8(KEY2);
|
||||
mKey3 = String8(KEY3);
|
||||
mKey4 = String8(KEY4);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BackupDataTest, WriteAndReadSingle) {
|
||||
int fd = ::open(m_filename, O_WRONLY);
|
||||
BackupDataWriter* writer = new BackupDataWriter(fd);
|
||||
|
||||
EXPECT_EQ(NO_ERROR, writer->WriteEntityHeader(mKey1, sizeof(DATA1)))
|
||||
<< "WriteEntityHeader returned an error";
|
||||
EXPECT_EQ(NO_ERROR, writer->WriteEntityData(DATA1, sizeof(DATA1)))
|
||||
<< "WriteEntityData returned an error";
|
||||
|
||||
::close(fd);
|
||||
fd = ::open(m_filename, O_RDONLY);
|
||||
BackupDataReader* reader = new BackupDataReader(fd);
|
||||
EXPECT_EQ(NO_ERROR, reader->Status())
|
||||
<< "Reader ctor failed";
|
||||
|
||||
bool done;
|
||||
int type;
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader";
|
||||
|
||||
String8 key;
|
||||
size_t dataSize;
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error";
|
||||
EXPECT_EQ(mKey1, key)
|
||||
<< "wrong key from ReadEntityHeader";
|
||||
EXPECT_EQ(sizeof(DATA1), dataSize)
|
||||
<< "wrong size from ReadEntityHeader";
|
||||
|
||||
char* dataBytes = new char[dataSize];
|
||||
EXPECT_EQ((int) dataSize, reader->ReadEntityData(dataBytes, dataSize))
|
||||
<< "ReadEntityData returned an error";
|
||||
for (unsigned int i = 0; i < sizeof(DATA1); i++) {
|
||||
EXPECT_EQ(DATA1[i], dataBytes[i])
|
||||
<< "data character " << i << " should be equal";
|
||||
}
|
||||
delete dataBytes;
|
||||
delete writer;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
TEST_F(BackupDataTest, WriteAndReadMultiple) {
|
||||
int fd = ::open(m_filename, O_WRONLY);
|
||||
BackupDataWriter* writer = new BackupDataWriter(fd);
|
||||
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
|
||||
writer->WriteEntityData(DATA1, sizeof(DATA1));
|
||||
writer->WriteEntityHeader(mKey2, sizeof(DATA2));
|
||||
writer->WriteEntityData(DATA2, sizeof(DATA2));
|
||||
|
||||
::close(fd);
|
||||
fd = ::open(m_filename, O_RDONLY);
|
||||
BackupDataReader* reader = new BackupDataReader(fd);
|
||||
|
||||
bool done;
|
||||
int type;
|
||||
String8 key;
|
||||
size_t dataSize;
|
||||
char* dataBytes;
|
||||
// read first entity
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
reader->ReadEntityHeader(&key, &dataSize);
|
||||
dataBytes = new char[dataSize];
|
||||
reader->ReadEntityData(dataBytes, dataSize);
|
||||
delete dataBytes;
|
||||
|
||||
// read and verify second entity
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on second entity";
|
||||
EXPECT_EQ(mKey2, key)
|
||||
<< "wrong key from ReadEntityHeader on second entity";
|
||||
EXPECT_EQ(sizeof(DATA2), dataSize)
|
||||
<< "wrong size from ReadEntityHeader on second entity";
|
||||
|
||||
dataBytes = new char[dataSize];
|
||||
EXPECT_EQ((int)dataSize, reader->ReadEntityData(dataBytes, dataSize))
|
||||
<< "ReadEntityData returned an error on second entity";
|
||||
for (unsigned int i = 0; i < sizeof(DATA2); i++) {
|
||||
EXPECT_EQ(DATA2[i], dataBytes[i])
|
||||
<< "data character " << i << " should be equal";
|
||||
}
|
||||
delete dataBytes;
|
||||
delete writer;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
TEST_F(BackupDataTest, SkipEntity) {
|
||||
int fd = ::open(m_filename, O_WRONLY);
|
||||
BackupDataWriter* writer = new BackupDataWriter(fd);
|
||||
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
|
||||
writer->WriteEntityData(DATA1, sizeof(DATA1));
|
||||
writer->WriteEntityHeader(mKey2, sizeof(DATA2));
|
||||
writer->WriteEntityData(DATA2, sizeof(DATA2));
|
||||
writer->WriteEntityHeader(mKey3, sizeof(DATA3));
|
||||
writer->WriteEntityData(DATA3, sizeof(DATA3));
|
||||
|
||||
::close(fd);
|
||||
fd = ::open(m_filename, O_RDONLY);
|
||||
BackupDataReader* reader = new BackupDataReader(fd);
|
||||
|
||||
bool done;
|
||||
int type;
|
||||
String8 key;
|
||||
size_t dataSize;
|
||||
char* dataBytes;
|
||||
// read first entity
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
reader->ReadEntityHeader(&key, &dataSize);
|
||||
dataBytes = new char[dataSize];
|
||||
reader->ReadEntityData(dataBytes, dataSize);
|
||||
delete dataBytes;
|
||||
|
||||
// skip second entity
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
reader->ReadEntityHeader(&key, &dataSize);
|
||||
reader->SkipEntityData();
|
||||
|
||||
// read and verify third entity
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader after skip";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on third entity";
|
||||
EXPECT_EQ(mKey3, key)
|
||||
<< "wrong key from ReadEntityHeader on third entity";
|
||||
EXPECT_EQ(sizeof(DATA3), dataSize)
|
||||
<< "wrong size from ReadEntityHeader on third entity";
|
||||
|
||||
dataBytes = new char[dataSize];
|
||||
EXPECT_EQ((int) dataSize, reader->ReadEntityData(dataBytes, dataSize))
|
||||
<< "ReadEntityData returned an error on third entity";
|
||||
for (unsigned int i = 0; i < sizeof(DATA3); i++) {
|
||||
EXPECT_EQ(DATA3[i], dataBytes[i])
|
||||
<< "data character " << i << " should be equal";
|
||||
}
|
||||
delete dataBytes;
|
||||
delete writer;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
TEST_F(BackupDataTest, DeleteEntity) {
|
||||
int fd = ::open(m_filename, O_WRONLY);
|
||||
BackupDataWriter* writer = new BackupDataWriter(fd);
|
||||
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
|
||||
writer->WriteEntityData(DATA1, sizeof(DATA1));
|
||||
writer->WriteEntityHeader(mKey2, -1);
|
||||
|
||||
::close(fd);
|
||||
fd = ::open(m_filename, O_RDONLY);
|
||||
BackupDataReader* reader = new BackupDataReader(fd);
|
||||
|
||||
bool done;
|
||||
int type;
|
||||
String8 key;
|
||||
size_t dataSize;
|
||||
char* dataBytes;
|
||||
// read first entity
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
reader->ReadEntityHeader(&key, &dataSize);
|
||||
dataBytes = new char[dataSize];
|
||||
reader->ReadEntityData(dataBytes, dataSize);
|
||||
delete dataBytes;
|
||||
|
||||
// read and verify deletion
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader on deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on second entity";
|
||||
EXPECT_EQ(mKey2, key)
|
||||
<< "wrong key from ReadEntityHeader on second entity";
|
||||
EXPECT_EQ(-1, (int) dataSize)
|
||||
<< "not recognizing deletion on second entity";
|
||||
|
||||
delete writer;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
TEST_F(BackupDataTest, EneityAfterDelete) {
|
||||
int fd = ::open(m_filename, O_WRONLY);
|
||||
BackupDataWriter* writer = new BackupDataWriter(fd);
|
||||
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
|
||||
writer->WriteEntityData(DATA1, sizeof(DATA1));
|
||||
writer->WriteEntityHeader(mKey2, -1);
|
||||
writer->WriteEntityHeader(mKey3, sizeof(DATA3));
|
||||
writer->WriteEntityData(DATA3, sizeof(DATA3));
|
||||
|
||||
::close(fd);
|
||||
fd = ::open(m_filename, O_RDONLY);
|
||||
BackupDataReader* reader = new BackupDataReader(fd);
|
||||
|
||||
bool done;
|
||||
int type;
|
||||
String8 key;
|
||||
size_t dataSize;
|
||||
char* dataBytes;
|
||||
// read first entity
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
reader->ReadEntityHeader(&key, &dataSize);
|
||||
dataBytes = new char[dataSize];
|
||||
reader->ReadEntityData(dataBytes, dataSize);
|
||||
delete dataBytes;
|
||||
|
||||
// read and verify deletion
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader on deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on second entity";
|
||||
EXPECT_EQ(mKey2, key)
|
||||
<< "wrong key from ReadEntityHeader on second entity";
|
||||
EXPECT_EQ(-1, (int)dataSize)
|
||||
<< "not recognizing deletion on second entity";
|
||||
|
||||
// read and verify third entity
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader after deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on third entity";
|
||||
EXPECT_EQ(mKey3, key)
|
||||
<< "wrong key from ReadEntityHeader on third entity";
|
||||
EXPECT_EQ(sizeof(DATA3), dataSize)
|
||||
<< "wrong size from ReadEntityHeader on third entity";
|
||||
|
||||
dataBytes = new char[dataSize];
|
||||
EXPECT_EQ((int) dataSize, reader->ReadEntityData(dataBytes, dataSize))
|
||||
<< "ReadEntityData returned an error on third entity";
|
||||
for (unsigned int i = 0; i < sizeof(DATA3); i++) {
|
||||
EXPECT_EQ(DATA3[i], dataBytes[i])
|
||||
<< "data character " << i << " should be equal";
|
||||
}
|
||||
delete dataBytes;
|
||||
delete writer;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
TEST_F(BackupDataTest, OnlyDeleteEntities) {
|
||||
int fd = ::open(m_filename, O_WRONLY);
|
||||
BackupDataWriter* writer = new BackupDataWriter(fd);
|
||||
writer->WriteEntityHeader(mKey1, -1);
|
||||
writer->WriteEntityHeader(mKey2, -1);
|
||||
writer->WriteEntityHeader(mKey3, -1);
|
||||
writer->WriteEntityHeader(mKey4, -1);
|
||||
|
||||
::close(fd);
|
||||
fd = ::open(m_filename, O_RDONLY);
|
||||
BackupDataReader* reader = new BackupDataReader(fd);
|
||||
|
||||
bool done;
|
||||
int type;
|
||||
String8 key;
|
||||
size_t dataSize;
|
||||
// read and verify first deletion
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader first deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on first entity";
|
||||
EXPECT_EQ(mKey1, key)
|
||||
<< "wrong key from ReadEntityHeader on first entity";
|
||||
EXPECT_EQ(-1, (int) dataSize)
|
||||
<< "not recognizing deletion on first entity";
|
||||
|
||||
// read and verify second deletion
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader second deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on second entity";
|
||||
EXPECT_EQ(mKey2, key)
|
||||
<< "wrong key from ReadEntityHeader on second entity";
|
||||
EXPECT_EQ(-1, (int) dataSize)
|
||||
<< "not recognizing deletion on second entity";
|
||||
|
||||
// read and verify third deletion
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader third deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on third entity";
|
||||
EXPECT_EQ(mKey3, key)
|
||||
<< "wrong key from ReadEntityHeader on third entity";
|
||||
EXPECT_EQ(-1, (int) dataSize)
|
||||
<< "not recognizing deletion on third entity";
|
||||
|
||||
// read and verify fourth deletion
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader fourth deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on fourth entity";
|
||||
EXPECT_EQ(mKey4, key)
|
||||
<< "wrong key from ReadEntityHeader on fourth entity";
|
||||
EXPECT_EQ(-1, (int) dataSize)
|
||||
<< "not recognizing deletion on fourth entity";
|
||||
|
||||
delete writer;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
TEST_F(BackupDataTest, ReadDeletedEntityData) {
|
||||
int fd = ::open(m_filename, O_WRONLY);
|
||||
BackupDataWriter* writer = new BackupDataWriter(fd);
|
||||
writer->WriteEntityHeader(mKey1, -1);
|
||||
writer->WriteEntityHeader(mKey2, -1);
|
||||
|
||||
::close(fd);
|
||||
fd = ::open(m_filename, O_RDONLY);
|
||||
BackupDataReader* reader = new BackupDataReader(fd);
|
||||
|
||||
bool done;
|
||||
int type;
|
||||
String8 key;
|
||||
size_t dataSize;
|
||||
// read and verify first deletion
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader first deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on first entity";
|
||||
EXPECT_EQ(mKey1, key)
|
||||
<< "wrong key from ReadEntityHeader on first entity";
|
||||
EXPECT_EQ(-1, (int) dataSize)
|
||||
<< "not recognizing deletion on first entity";
|
||||
|
||||
// erroneously try to read first entity data
|
||||
char* dataBytes = new char[10];
|
||||
dataBytes[0] = 'A';
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityData(dataBytes, dataSize));
|
||||
// expect dataBytes to be unmodofied
|
||||
EXPECT_EQ('A', dataBytes[0]);
|
||||
|
||||
// read and verify second deletion
|
||||
reader->ReadNextHeader(&done, &type);
|
||||
EXPECT_EQ(BACKUP_HEADER_ENTITY_V1, type)
|
||||
<< "wrong type from ReadNextHeader second deletion";
|
||||
|
||||
EXPECT_EQ(NO_ERROR, reader->ReadEntityHeader(&key, &dataSize))
|
||||
<< "ReadEntityHeader returned an error on second entity";
|
||||
EXPECT_EQ(mKey2, key)
|
||||
<< "wrong key from ReadEntityHeader on second entity";
|
||||
EXPECT_EQ(-1, (int) dataSize)
|
||||
<< "not recognizing deletion on second entity";
|
||||
|
||||
delete writer;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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 <androidfw/ObbFile.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.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);
|
||||
|
||||
int fd = ::open(mFileName, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
FAIL() << "Couldn't create " << mFileName << " for tests";
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
#define SALT_SIZE 8
|
||||
unsigned char salt[SALT_SIZE] = {0x01, 0x10, 0x55, 0xAA, 0xFF, 0x00, 0x5A, 0xA5};
|
||||
EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE))
|
||||
<< "Salt should be successfully set";
|
||||
|
||||
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";
|
||||
|
||||
size_t saltLen;
|
||||
const unsigned char* newSalt = mObbFile->getSalt(&saltLen);
|
||||
|
||||
EXPECT_EQ(sizeof(salt), saltLen)
|
||||
<< "salt sizes were not the same";
|
||||
|
||||
for (int i = 0; i < sizeof(salt); i++) {
|
||||
EXPECT_EQ(salt[i], newSalt[i])
|
||||
<< "salt character " << i << " should be equal";
|
||||
}
|
||||
EXPECT_TRUE(memcmp(newSalt, salt, sizeof(salt)) == 0)
|
||||
<< "salts should be the same";
|
||||
}
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 "ZipUtils_test"
|
||||
#include <utils/Log.h>
|
||||
#include <androidfw/ZipUtils.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class ZipUtilsTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ZipUtilsTest, ZipTimeConvertSuccess) {
|
||||
struct tm t;
|
||||
|
||||
// 2011-06-29 14:40:40
|
||||
long when = 0x3EDD7514;
|
||||
|
||||
ZipUtils::zipTimeToTimespec(when, &t);
|
||||
|
||||
EXPECT_EQ(2011, t.tm_year + 1900)
|
||||
<< "Year was improperly converted.";
|
||||
|
||||
EXPECT_EQ(6, t.tm_mon)
|
||||
<< "Month was improperly converted.";
|
||||
|
||||
EXPECT_EQ(29, t.tm_mday)
|
||||
<< "Day was improperly converted.";
|
||||
|
||||
EXPECT_EQ(14, t.tm_hour)
|
||||
<< "Hour was improperly converted.";
|
||||
|
||||
EXPECT_EQ(40, t.tm_min)
|
||||
<< "Minute was improperly converted.";
|
||||
|
||||
EXPECT_EQ(40, t.tm_sec)
|
||||
<< "Second was improperly converted.";
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user