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