/* * 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