blob: 7294cefb62d2026f3eb52407361dbe88e74147db [file] [log] [blame]
/*
* 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 "PtpCursor"
#include "MtpDebug.h"
#include "MtpClient.h"
#include "PtpCursor.h"
#include "MtpDevice.h"
#include "MtpDeviceInfo.h"
#include "MtpObjectInfo.h"
#include "MtpStorageInfo.h"
#include "binder/CursorWindow.h"
namespace android {
/* Device Column IDs */
/* These must match the values in PtpCursor.java */
#define DEVICE_ROW_ID 1
#define DEVICE_MANUFACTURER 2
#define DEVICE_MODEL 3
/* Storage Column IDs */
/* These must match the values in PtpCursor.java */
#define STORAGE_ROW_ID 101
#define STORAGE_IDENTIFIER 102
#define STORAGE_DESCRIPTION 103
/* Object Column IDs */
/* These must match the values in PtpCursor.java */
#define OBJECT_ROW_ID 201
#define OBJECT_STORAGE_ID 202
#define OBJECT_FORMAT 203
#define OBJECT_PROTECTION_STATUS 204
#define OBJECT_SIZE 205
#define OBJECT_THUMB_FORMAT 206
#define OBJECT_THUMB_SIZE 207
#define OBJECT_THUMB_WIDTH 208
#define OBJECT_THUMB_HEIGHT 209
#define OBJECT_IMAGE_WIDTH 210
#define OBJECT_IMAGE_HEIGHT 211
#define OBJECT_IMAGE_DEPTH 212
#define OBJECT_PARENT 213
#define OBJECT_ASSOCIATION_TYPE 214
#define OBJECT_ASSOCIATION_DESC 215
#define OBJECT_SEQUENCE_NUMBER 216
#define OBJECT_NAME 217
#define OBJECT_DATE_CREATED 218
#define OBJECT_DATE_MODIFIED 219
#define OBJECT_KEYWORDS 220
#define OBJECT_THUMB 221
PtpCursor::PtpCursor(MtpClient* client, int queryType, int deviceID,
MtpStorageID storageID, MtpObjectHandle objectID,
int columnCount, int* columns)
: mClient(client),
mQueryType(queryType),
mDeviceID(deviceID),
mStorageID(storageID),
mQbjectID(objectID),
mColumnCount(columnCount),
mColumns(NULL)
{
if (columns) {
mColumns = new int[columnCount];
memcpy(mColumns, columns, columnCount * sizeof(int));
}
}
PtpCursor::~PtpCursor() {
delete[] mColumns;
}
int PtpCursor::fillWindow(CursorWindow* window, int startPos) {
LOGD("PtpCursor::fillWindow mQueryType: %d\n", mQueryType);
switch (mQueryType) {
case DEVICE:
return fillDevices(window, startPos);
case DEVICE_ID:
return fillDevice(window, startPos);
case STORAGE:
return fillStorages(window, startPos);
case STORAGE_ID:
return fillStorage(window, startPos);
case OBJECT:
return fillObjects(window, 0, startPos);
case OBJECT_ID:
return fillObject(window, startPos);
case STORAGE_CHILDREN:
return fillObjects(window, -1, startPos);
case OBJECT_CHILDREN:
return fillObjects(window, mQbjectID, startPos);
default:
LOGE("PtpCursor::fillWindow: unknown query type %d\n", mQueryType);
return 0;
}
}
int PtpCursor::fillDevices(CursorWindow* window, int startPos) {
int count = 0;
MtpDeviceList& deviceList = mClient->getDeviceList();
for (int i = 0; i < deviceList.size(); i++) {
MtpDevice* device = deviceList[i];
if (fillDevice(window, device, startPos)) {
count++;
startPos++;
} else {
break;
}
}
return count;
}
int PtpCursor::fillDevice(CursorWindow* window, int startPos) {
MtpDevice* device = mClient->getDevice(mDeviceID);
if (device && fillDevice(window, device, startPos))
return 1;
else
return 0;
}
int PtpCursor::fillStorages(CursorWindow* window, int startPos) {
int count = 0;
MtpDevice* device = mClient->getDevice(mDeviceID);
if (!device)
return 0;
MtpStorageIDList* storageIDs = device->getStorageIDs();
if (!storageIDs)
return 0;
for (int i = 0; i < storageIDs->size(); i++) {
MtpStorageID storageID = (*storageIDs)[i];
if (fillStorage(window, device, storageID, startPos)) {
count++;
startPos++;
} else {
break;
}
}
delete storageIDs;
return count;
}
int PtpCursor::fillStorage(CursorWindow* window, int startPos) {
MtpDevice* device = mClient->getDevice(mDeviceID);
if (device && fillStorage(window, device, mStorageID, startPos))
return 1;
else
return 0;
}
int PtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) {
int count = 0;
MtpDevice* device = mClient->getDevice(mDeviceID);
if (!device)
return 0;
MtpObjectHandleList* handles = device->getObjectHandles(mStorageID, 0, parent);
if (!handles)
return 0;
for (int i = 0; i < handles->size(); i++) {
MtpObjectHandle handle = (*handles)[i];
if (fillObject(window, device, handle, startPos)) {
count++;
startPos++;
} else {
break;
}
}
delete handles;
return count;
}
int PtpCursor::fillObject(CursorWindow* window, int startPos) {
MtpDevice* device = mClient->getDevice(mDeviceID);
if (device && fillObject(window, device, mQbjectID, startPos))
return 1;
else
return 0;
}
bool PtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) {
MtpDeviceInfo* deviceInfo = device->getDeviceInfo();
if (!deviceInfo)
return false;
if (!prepareRow(window))
return false;
for (int i = 0; i < mColumnCount; i++) {
switch (mColumns[i]) {
case DEVICE_ROW_ID:
if (!putLong(window, device->getID(), row, i))
return false;
break;
case DEVICE_MANUFACTURER:
if (!putString(window, deviceInfo->mManufacturer, row, i))
return false;
break;
case DEVICE_MODEL:
if (!putString(window, deviceInfo->mModel, row, i))
return false;
break;
default:
LOGE("fillDevice: unknown column %d\n", mColumns[i]);
return false;
}
}
return true;
}
bool PtpCursor::fillStorage(CursorWindow* window, MtpDevice* device,
MtpStorageID storageID, int row) {
LOGD("fillStorage %d\n", storageID);
MtpStorageInfo* storageInfo = device->getStorageInfo(storageID);
if (!storageInfo)
return false;
if (!prepareRow(window)) {
delete storageInfo;
return false;
}
const char* text;
for (int i = 0; i < mColumnCount; i++) {
switch (mColumns[i]) {
case STORAGE_ROW_ID:
if (!putLong(window, storageID, row, i))
goto fail;
break;
case STORAGE_IDENTIFIER:
text = storageInfo->mVolumeIdentifier;
if (!text || !text[0])
text = "Camera Storage";
if (!putString(window, text, row, i))
goto fail;
break;
case STORAGE_DESCRIPTION:
text = storageInfo->mStorageDescription;
if (!text || !text[0])
text = "Storage Description";
if (!putString(window, text, row, i))
goto fail;
break;
default:
LOGE("fillStorage: unknown column %d\n", mColumns[i]);
goto fail;
}
}
delete storageInfo;
return true;
fail:
delete storageInfo;
return false;
}
bool PtpCursor::fillObject(CursorWindow* window, MtpDevice* device,
MtpObjectHandle objectID, int row) {
MtpObjectInfo* objectInfo = device->getObjectInfo(objectID);
if (!objectInfo)
return false;
// objectInfo->print();
if (!prepareRow(window)) {
delete objectInfo;
return false;
}
for (int i = 0; i < mColumnCount; i++) {
switch (mColumns[i]) {
case OBJECT_ROW_ID:
if (!putLong(window, objectID, row, i))
goto fail;
break;
case OBJECT_STORAGE_ID:
if (!putLong(window, objectInfo->mStorageID, row, i))
goto fail;
break;
case OBJECT_FORMAT:
if (!putLong(window, objectInfo->mFormat, row, i))
goto fail;
break;
case OBJECT_PROTECTION_STATUS:
if (!putLong(window, objectInfo->mProtectionStatus, row, i))
goto fail;
break;
case OBJECT_SIZE:
if (!putLong(window, objectInfo->mCompressedSize, row, i))
goto fail;
break;
case OBJECT_THUMB_FORMAT:
if (!putLong(window, objectInfo->mThumbFormat, row, i))
goto fail;
break;
case OBJECT_THUMB_SIZE:
if (!putLong(window, objectInfo->mThumbCompressedSize, row, i))
goto fail;
break;
case OBJECT_THUMB_WIDTH:
if (!putLong(window, objectInfo->mThumbPixWidth, row, i))
goto fail;
break;
case OBJECT_THUMB_HEIGHT:
if (!putLong(window, objectInfo->mThumbPixHeight, row, i))
goto fail;
break;
case OBJECT_IMAGE_WIDTH:
if (!putLong(window, objectInfo->mImagePixWidth, row, i))
goto fail;
break;
case OBJECT_IMAGE_HEIGHT:
if (!putLong(window, objectInfo->mImagePixHeight, row, i))
goto fail;
break;
case OBJECT_IMAGE_DEPTH:
if (!putLong(window, objectInfo->mImagePixDepth, row, i))
goto fail;
break;
case OBJECT_PARENT:
if (!putLong(window, objectInfo->mParent, row, i))
goto fail;
break;
case OBJECT_ASSOCIATION_TYPE:
if (!putLong(window, objectInfo->mAssociationType, row, i))
goto fail;
break;
case OBJECT_ASSOCIATION_DESC:
if (!putLong(window, objectInfo->mAssociationDesc, row, i))
goto fail;
break;
case OBJECT_SEQUENCE_NUMBER:
if (!putLong(window, objectInfo->mSequenceNumber, row, i))
goto fail;
break;
case OBJECT_NAME:
if (!putString(window, objectInfo->mName, row, i))
goto fail;
break;
case OBJECT_DATE_CREATED:
if (!putLong(window, objectInfo->mDateCreated, row, i))
goto fail;
break;
case OBJECT_DATE_MODIFIED:
if (!putLong(window, objectInfo->mDateModified, row, i))
goto fail;
break;
case OBJECT_KEYWORDS:
if (!putString(window, objectInfo->mKeywords, row, i))
goto fail;
break;
case OBJECT_THUMB:
if (!putThumbnail(window, objectID, objectInfo->mFormat, row, i))
goto fail;
break;
default:
LOGE("fillObject: unknown column %d\n", mColumns[i]);
goto fail;
}
}
delete objectInfo;
return true;
fail:
delete objectInfo;
return false;
}
bool PtpCursor::prepareRow(CursorWindow* window) {
if (!window->setNumColumns(mColumnCount)) {
LOGE("Failed to change column count from %d to %d", window->getNumColumns(), mColumnCount);
return false;
}
field_slot_t * fieldDir = window->allocRow();
if (!fieldDir) {
LOGE("Failed allocating fieldDir");
return false;
}
return true;
}
bool PtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column) {
if (!window->putLong(row, column, value)) {
window->freeLastRow();
LOGE("Failed allocating space for a long in column %d", column);
return false;
}
return true;
}
bool PtpCursor::putString(CursorWindow* window, const char* text, int row, int column) {
int size = strlen(text) + 1;
int offset = window->alloc(size);
if (!offset) {
window->freeLastRow();
LOGE("Failed allocating %u bytes for text/blob %s", size, text);
return false;
}
window->copyIn(offset, (const uint8_t*)text, size);
// This must be updated after the call to alloc(), since that
// may move the field around in the window
field_slot_t * fieldSlot = window->getFieldSlot(row, column);
fieldSlot->type = FIELD_TYPE_STRING;
fieldSlot->data.buffer.offset = offset;
fieldSlot->data.buffer.size = size;
return true;
}
bool PtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
MtpObjectFormat format, int row, int column) {
MtpDevice* device = mClient->getDevice(mDeviceID);
void* thumbnail;
int size, offset;
if (format == MTP_FORMAT_ASSOCIATION) {
thumbnail = NULL;
size = offset = 0;
} else {
thumbnail = device->getThumbnail(objectID, size);
LOGV("putThumbnail: %p, size: %d\n", thumbnail, size);
offset = window->alloc(size);
if (!offset) {
window->freeLastRow();
LOGE("Failed allocating %u bytes for thumbnail", size);
return false;
}
}
if (thumbnail)
window->copyIn(offset, (const uint8_t*)thumbnail, size);
// This must be updated after the call to alloc(), since that
// may move the field around in the window
field_slot_t * fieldSlot = window->getFieldSlot(row, column);
fieldSlot->type = FIELD_TYPE_BLOB;
fieldSlot->data.buffer.offset = offset;
fieldSlot->data.buffer.size = size;
return true;
}
} // namespace android