xref: /aosp_15_r20/frameworks/av/media/mtp/MtpServer.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker  * Copyright (C) 2010 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker  *
4*ec779b8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker  *
8*ec779b8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker  *
10*ec779b8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker  * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker  */
16*ec779b8eSAndroid Build Coastguard Worker 
17*ec779b8eSAndroid Build Coastguard Worker #include <algorithm>
18*ec779b8eSAndroid Build Coastguard Worker #include <android-base/logging.h>
19*ec779b8eSAndroid Build Coastguard Worker #include <android-base/properties.h>
20*ec779b8eSAndroid Build Coastguard Worker #include <chrono>
21*ec779b8eSAndroid Build Coastguard Worker #include <dirent.h>
22*ec779b8eSAndroid Build Coastguard Worker #include <errno.h>
23*ec779b8eSAndroid Build Coastguard Worker #include <fcntl.h>
24*ec779b8eSAndroid Build Coastguard Worker #include <inttypes.h>
25*ec779b8eSAndroid Build Coastguard Worker #include <stdio.h>
26*ec779b8eSAndroid Build Coastguard Worker #include <stdlib.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <sys/types.h>
28*ec779b8eSAndroid Build Coastguard Worker #include <sys/stat.h>
29*ec779b8eSAndroid Build Coastguard Worker #include <sys/stat.h>
30*ec779b8eSAndroid Build Coastguard Worker #include <sys/time.h>
31*ec779b8eSAndroid Build Coastguard Worker 
32*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "MtpServer"
33*ec779b8eSAndroid Build Coastguard Worker 
34*ec779b8eSAndroid Build Coastguard Worker #include "MtpDebug.h"
35*ec779b8eSAndroid Build Coastguard Worker #include "IMtpDatabase.h"
36*ec779b8eSAndroid Build Coastguard Worker #include "MtpDescriptors.h"
37*ec779b8eSAndroid Build Coastguard Worker #include "MtpDevHandle.h"
38*ec779b8eSAndroid Build Coastguard Worker #include "MtpFfsCompatHandle.h"
39*ec779b8eSAndroid Build Coastguard Worker #include "MtpFfsHandle.h"
40*ec779b8eSAndroid Build Coastguard Worker #include "MtpObjectInfo.h"
41*ec779b8eSAndroid Build Coastguard Worker #include "MtpProperty.h"
42*ec779b8eSAndroid Build Coastguard Worker #include "MtpServer.h"
43*ec779b8eSAndroid Build Coastguard Worker #include "MtpStorage.h"
44*ec779b8eSAndroid Build Coastguard Worker #include "MtpStringBuffer.h"
45*ec779b8eSAndroid Build Coastguard Worker #include "android-base/strings.h"
46*ec779b8eSAndroid Build Coastguard Worker 
47*ec779b8eSAndroid Build Coastguard Worker namespace android {
48*ec779b8eSAndroid Build Coastguard Worker static const int SN_EVENT_LOG_ID = 0x534e4554;
49*ec779b8eSAndroid Build Coastguard Worker 
50*ec779b8eSAndroid Build Coastguard Worker static const MtpOperationCode kSupportedOperationCodes[] = {
51*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_DEVICE_INFO,
52*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_OPEN_SESSION,
53*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_CLOSE_SESSION,
54*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_STORAGE_IDS,
55*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_STORAGE_INFO,
56*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_NUM_OBJECTS,
57*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_OBJECT_HANDLES,
58*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_OBJECT_INFO,
59*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_OBJECT,
60*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_THUMB,
61*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_DELETE_OBJECT,
62*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_SEND_OBJECT_INFO,
63*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_SEND_OBJECT,
64*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_INITIATE_CAPTURE,
65*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_FORMAT_STORE,
66*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_RESET_DEVICE,
67*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_SELF_TEST,
68*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_SET_OBJECT_PROTECTION,
69*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_POWER_DOWN,
70*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_DEVICE_PROP_DESC,
71*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_DEVICE_PROP_VALUE,
72*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_SET_DEVICE_PROP_VALUE,
73*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
74*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
75*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_MOVE_OBJECT,
76*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_COPY_OBJECT,
77*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_PARTIAL_OBJECT,
78*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
79*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
80*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_OBJECT_PROP_DESC,
81*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_OBJECT_PROP_VALUE,
82*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_SET_OBJECT_PROP_VALUE,
83*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_OBJECT_PROP_LIST,
84*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_SET_OBJECT_PROP_LIST,
85*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
86*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_SEND_OBJECT_PROP_LIST,
87*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_GET_OBJECT_REFERENCES,
88*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_SET_OBJECT_REFERENCES,
89*ec779b8eSAndroid Build Coastguard Worker //    MTP_OPERATION_SKIP,
90*ec779b8eSAndroid Build Coastguard Worker     // Android extension for direct file IO
91*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_GET_PARTIAL_OBJECT_64,
92*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_SEND_PARTIAL_OBJECT,
93*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_TRUNCATE_OBJECT,
94*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_BEGIN_EDIT_OBJECT,
95*ec779b8eSAndroid Build Coastguard Worker     MTP_OPERATION_END_EDIT_OBJECT,
96*ec779b8eSAndroid Build Coastguard Worker };
97*ec779b8eSAndroid Build Coastguard Worker 
98*ec779b8eSAndroid Build Coastguard Worker static const MtpEventCode kSupportedEventCodes[] = {
99*ec779b8eSAndroid Build Coastguard Worker     MTP_EVENT_OBJECT_ADDED,
100*ec779b8eSAndroid Build Coastguard Worker     MTP_EVENT_OBJECT_REMOVED,
101*ec779b8eSAndroid Build Coastguard Worker     MTP_EVENT_STORE_ADDED,
102*ec779b8eSAndroid Build Coastguard Worker     MTP_EVENT_STORE_REMOVED,
103*ec779b8eSAndroid Build Coastguard Worker     MTP_EVENT_DEVICE_PROP_CHANGED,
104*ec779b8eSAndroid Build Coastguard Worker     MTP_EVENT_OBJECT_INFO_CHANGED,
105*ec779b8eSAndroid Build Coastguard Worker };
106*ec779b8eSAndroid Build Coastguard Worker 
MtpServer(IMtpDatabase * database,int controlFd,bool ptp,const char * deviceInfoManufacturer,const char * deviceInfoModel,const char * deviceInfoDeviceVersion,const char * deviceInfoSerialNumber)107*ec779b8eSAndroid Build Coastguard Worker MtpServer::MtpServer(IMtpDatabase* database, int controlFd, bool ptp,
108*ec779b8eSAndroid Build Coastguard Worker                     const char *deviceInfoManufacturer,
109*ec779b8eSAndroid Build Coastguard Worker                     const char *deviceInfoModel,
110*ec779b8eSAndroid Build Coastguard Worker                     const char *deviceInfoDeviceVersion,
111*ec779b8eSAndroid Build Coastguard Worker                     const char *deviceInfoSerialNumber)
112*ec779b8eSAndroid Build Coastguard Worker     :   mDatabase(database),
113*ec779b8eSAndroid Build Coastguard Worker         mPtp(ptp),
114*ec779b8eSAndroid Build Coastguard Worker         mDeviceInfoManufacturer(deviceInfoManufacturer),
115*ec779b8eSAndroid Build Coastguard Worker         mDeviceInfoModel(deviceInfoModel),
116*ec779b8eSAndroid Build Coastguard Worker         mDeviceInfoDeviceVersion(deviceInfoDeviceVersion),
117*ec779b8eSAndroid Build Coastguard Worker         mDeviceInfoSerialNumber(deviceInfoSerialNumber),
118*ec779b8eSAndroid Build Coastguard Worker         mSessionID(0),
119*ec779b8eSAndroid Build Coastguard Worker         mSessionOpen(false),
120*ec779b8eSAndroid Build Coastguard Worker         mSendObjectHandle(kInvalidObjectHandle),
121*ec779b8eSAndroid Build Coastguard Worker         mSendObjectFormat(0),
122*ec779b8eSAndroid Build Coastguard Worker         mSendObjectFileSize(0),
123*ec779b8eSAndroid Build Coastguard Worker         mSendObjectModifiedTime(0)
124*ec779b8eSAndroid Build Coastguard Worker {
125*ec779b8eSAndroid Build Coastguard Worker     bool ffs_ok = access(FFS_MTP_EP0, W_OK) == 0;
126*ec779b8eSAndroid Build Coastguard Worker     if (ffs_ok) {
127*ec779b8eSAndroid Build Coastguard Worker         bool aio_compat = android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false);
128*ec779b8eSAndroid Build Coastguard Worker         mHandle = aio_compat ? new MtpFfsCompatHandle(controlFd) : new MtpFfsHandle(controlFd);
129*ec779b8eSAndroid Build Coastguard Worker     } else {
130*ec779b8eSAndroid Build Coastguard Worker         mHandle = new MtpDevHandle();
131*ec779b8eSAndroid Build Coastguard Worker     }
132*ec779b8eSAndroid Build Coastguard Worker }
133*ec779b8eSAndroid Build Coastguard Worker 
~MtpServer()134*ec779b8eSAndroid Build Coastguard Worker MtpServer::~MtpServer() {
135*ec779b8eSAndroid Build Coastguard Worker     if (mHandle) {
136*ec779b8eSAndroid Build Coastguard Worker         delete mHandle;
137*ec779b8eSAndroid Build Coastguard Worker         mHandle = NULL;
138*ec779b8eSAndroid Build Coastguard Worker     }
139*ec779b8eSAndroid Build Coastguard Worker }
140*ec779b8eSAndroid Build Coastguard Worker 
addStorage(MtpStorage * storage)141*ec779b8eSAndroid Build Coastguard Worker void MtpServer::addStorage(MtpStorage* storage) {
142*ec779b8eSAndroid Build Coastguard Worker     std::lock_guard<std::mutex> lg(mMutex);
143*ec779b8eSAndroid Build Coastguard Worker 
144*ec779b8eSAndroid Build Coastguard Worker     mStorages.push_back(storage);
145*ec779b8eSAndroid Build Coastguard Worker     sendStoreAdded(storage->getStorageID());
146*ec779b8eSAndroid Build Coastguard Worker }
147*ec779b8eSAndroid Build Coastguard Worker 
removeStorage(MtpStorage * storage)148*ec779b8eSAndroid Build Coastguard Worker void MtpServer::removeStorage(MtpStorage* storage) {
149*ec779b8eSAndroid Build Coastguard Worker     std::lock_guard<std::mutex> lg(mMutex);
150*ec779b8eSAndroid Build Coastguard Worker     auto iter = std::find(mStorages.begin(), mStorages.end(), storage);
151*ec779b8eSAndroid Build Coastguard Worker     if (iter != mStorages.end()) {
152*ec779b8eSAndroid Build Coastguard Worker         sendStoreRemoved(storage->getStorageID());
153*ec779b8eSAndroid Build Coastguard Worker         mStorages.erase(iter);
154*ec779b8eSAndroid Build Coastguard Worker     }
155*ec779b8eSAndroid Build Coastguard Worker }
156*ec779b8eSAndroid Build Coastguard Worker 
getStorage(MtpStorageID id)157*ec779b8eSAndroid Build Coastguard Worker MtpStorage* MtpServer::getStorage(MtpStorageID id) {
158*ec779b8eSAndroid Build Coastguard Worker     if (id == 0)
159*ec779b8eSAndroid Build Coastguard Worker         return mStorages[0];
160*ec779b8eSAndroid Build Coastguard Worker     for (MtpStorage *storage : mStorages) {
161*ec779b8eSAndroid Build Coastguard Worker         if (storage->getStorageID() == id)
162*ec779b8eSAndroid Build Coastguard Worker             return storage;
163*ec779b8eSAndroid Build Coastguard Worker     }
164*ec779b8eSAndroid Build Coastguard Worker     return nullptr;
165*ec779b8eSAndroid Build Coastguard Worker }
166*ec779b8eSAndroid Build Coastguard Worker 
hasStorage(MtpStorageID id)167*ec779b8eSAndroid Build Coastguard Worker bool MtpServer::hasStorage(MtpStorageID id) {
168*ec779b8eSAndroid Build Coastguard Worker     if (id == 0 || id == 0xFFFFFFFF)
169*ec779b8eSAndroid Build Coastguard Worker         return mStorages.size() > 0;
170*ec779b8eSAndroid Build Coastguard Worker     return (getStorage(id) != nullptr);
171*ec779b8eSAndroid Build Coastguard Worker }
172*ec779b8eSAndroid Build Coastguard Worker 
run()173*ec779b8eSAndroid Build Coastguard Worker void MtpServer::run() {
174*ec779b8eSAndroid Build Coastguard Worker     if (mHandle->start(mPtp)) {
175*ec779b8eSAndroid Build Coastguard Worker         ALOGE("Failed to start usb driver!");
176*ec779b8eSAndroid Build Coastguard Worker         mHandle->close();
177*ec779b8eSAndroid Build Coastguard Worker         return;
178*ec779b8eSAndroid Build Coastguard Worker     }
179*ec779b8eSAndroid Build Coastguard Worker 
180*ec779b8eSAndroid Build Coastguard Worker     while (1) {
181*ec779b8eSAndroid Build Coastguard Worker         int ret = mRequest.read(mHandle);
182*ec779b8eSAndroid Build Coastguard Worker         if (ret < 0) {
183*ec779b8eSAndroid Build Coastguard Worker             ALOGE("request read returned %d, errno: %d", ret, errno);
184*ec779b8eSAndroid Build Coastguard Worker             if (errno == ECANCELED) {
185*ec779b8eSAndroid Build Coastguard Worker                 // return to top of loop and wait for next command
186*ec779b8eSAndroid Build Coastguard Worker                 continue;
187*ec779b8eSAndroid Build Coastguard Worker             }
188*ec779b8eSAndroid Build Coastguard Worker             break;
189*ec779b8eSAndroid Build Coastguard Worker         }
190*ec779b8eSAndroid Build Coastguard Worker         MtpOperationCode operation = mRequest.getOperationCode();
191*ec779b8eSAndroid Build Coastguard Worker         MtpTransactionID transaction = mRequest.getTransactionID();
192*ec779b8eSAndroid Build Coastguard Worker 
193*ec779b8eSAndroid Build Coastguard Worker         ALOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
194*ec779b8eSAndroid Build Coastguard Worker         // FIXME need to generalize this
195*ec779b8eSAndroid Build Coastguard Worker         bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
196*ec779b8eSAndroid Build Coastguard Worker                     || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
197*ec779b8eSAndroid Build Coastguard Worker                     || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
198*ec779b8eSAndroid Build Coastguard Worker                     || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
199*ec779b8eSAndroid Build Coastguard Worker         if (dataIn) {
200*ec779b8eSAndroid Build Coastguard Worker             int ret = mData.read(mHandle);
201*ec779b8eSAndroid Build Coastguard Worker             if (ret < 0) {
202*ec779b8eSAndroid Build Coastguard Worker                 ALOGE("data read returned %d, errno: %d", ret, errno);
203*ec779b8eSAndroid Build Coastguard Worker                 if (errno == ECANCELED) {
204*ec779b8eSAndroid Build Coastguard Worker                     // return to top of loop and wait for next command
205*ec779b8eSAndroid Build Coastguard Worker                     continue;
206*ec779b8eSAndroid Build Coastguard Worker                 }
207*ec779b8eSAndroid Build Coastguard Worker                 break;
208*ec779b8eSAndroid Build Coastguard Worker             }
209*ec779b8eSAndroid Build Coastguard Worker             ALOGV("received data:");
210*ec779b8eSAndroid Build Coastguard Worker         } else {
211*ec779b8eSAndroid Build Coastguard Worker             mData.reset();
212*ec779b8eSAndroid Build Coastguard Worker         }
213*ec779b8eSAndroid Build Coastguard Worker 
214*ec779b8eSAndroid Build Coastguard Worker         if (handleRequest()) {
215*ec779b8eSAndroid Build Coastguard Worker             if (!dataIn && mData.hasData()) {
216*ec779b8eSAndroid Build Coastguard Worker                 mData.setOperationCode(operation);
217*ec779b8eSAndroid Build Coastguard Worker                 mData.setTransactionID(transaction);
218*ec779b8eSAndroid Build Coastguard Worker                 ALOGV("sending data:");
219*ec779b8eSAndroid Build Coastguard Worker                 ret = mData.write(mHandle);
220*ec779b8eSAndroid Build Coastguard Worker                 if (ret < 0) {
221*ec779b8eSAndroid Build Coastguard Worker                     ALOGE("request write returned %d, errno: %d", ret, errno);
222*ec779b8eSAndroid Build Coastguard Worker                     if (errno == ECANCELED) {
223*ec779b8eSAndroid Build Coastguard Worker                         // return to top of loop and wait for next command
224*ec779b8eSAndroid Build Coastguard Worker                         continue;
225*ec779b8eSAndroid Build Coastguard Worker                     }
226*ec779b8eSAndroid Build Coastguard Worker                     break;
227*ec779b8eSAndroid Build Coastguard Worker                 }
228*ec779b8eSAndroid Build Coastguard Worker             }
229*ec779b8eSAndroid Build Coastguard Worker 
230*ec779b8eSAndroid Build Coastguard Worker             mResponse.setTransactionID(transaction);
231*ec779b8eSAndroid Build Coastguard Worker             ALOGV("sending response %04X", mResponse.getResponseCode());
232*ec779b8eSAndroid Build Coastguard Worker             ret = mResponse.write(mHandle);
233*ec779b8eSAndroid Build Coastguard Worker             const int savedErrno = errno;
234*ec779b8eSAndroid Build Coastguard Worker             if (ret < 0) {
235*ec779b8eSAndroid Build Coastguard Worker                 ALOGE("request write returned %d, errno: %d", ret, errno);
236*ec779b8eSAndroid Build Coastguard Worker                 if (savedErrno == ECANCELED) {
237*ec779b8eSAndroid Build Coastguard Worker                     // return to top of loop and wait for next command
238*ec779b8eSAndroid Build Coastguard Worker                     continue;
239*ec779b8eSAndroid Build Coastguard Worker                 }
240*ec779b8eSAndroid Build Coastguard Worker                 break;
241*ec779b8eSAndroid Build Coastguard Worker             }
242*ec779b8eSAndroid Build Coastguard Worker         } else {
243*ec779b8eSAndroid Build Coastguard Worker             ALOGV("skipping response\n");
244*ec779b8eSAndroid Build Coastguard Worker         }
245*ec779b8eSAndroid Build Coastguard Worker     }
246*ec779b8eSAndroid Build Coastguard Worker 
247*ec779b8eSAndroid Build Coastguard Worker     // commit any open edits
248*ec779b8eSAndroid Build Coastguard Worker     int count = mObjectEditList.size();
249*ec779b8eSAndroid Build Coastguard Worker     for (int i = 0; i < count; i++) {
250*ec779b8eSAndroid Build Coastguard Worker         ObjectEdit* edit = mObjectEditList[i];
251*ec779b8eSAndroid Build Coastguard Worker         commitEdit(edit);
252*ec779b8eSAndroid Build Coastguard Worker         delete edit;
253*ec779b8eSAndroid Build Coastguard Worker     }
254*ec779b8eSAndroid Build Coastguard Worker     mObjectEditList.clear();
255*ec779b8eSAndroid Build Coastguard Worker 
256*ec779b8eSAndroid Build Coastguard Worker     mHandle->close();
257*ec779b8eSAndroid Build Coastguard Worker }
258*ec779b8eSAndroid Build Coastguard Worker 
sendObjectAdded(MtpObjectHandle handle)259*ec779b8eSAndroid Build Coastguard Worker void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
260*ec779b8eSAndroid Build Coastguard Worker     ALOGV("sendObjectAdded %d\n", handle);
261*ec779b8eSAndroid Build Coastguard Worker     sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
262*ec779b8eSAndroid Build Coastguard Worker }
263*ec779b8eSAndroid Build Coastguard Worker 
sendObjectRemoved(MtpObjectHandle handle)264*ec779b8eSAndroid Build Coastguard Worker void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
265*ec779b8eSAndroid Build Coastguard Worker     ALOGV("sendObjectRemoved %d\n", handle);
266*ec779b8eSAndroid Build Coastguard Worker     sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
267*ec779b8eSAndroid Build Coastguard Worker }
268*ec779b8eSAndroid Build Coastguard Worker 
sendObjectInfoChanged(MtpObjectHandle handle)269*ec779b8eSAndroid Build Coastguard Worker void MtpServer::sendObjectInfoChanged(MtpObjectHandle handle) {
270*ec779b8eSAndroid Build Coastguard Worker     ALOGV("sendObjectInfoChanged %d\n", handle);
271*ec779b8eSAndroid Build Coastguard Worker     sendEvent(MTP_EVENT_OBJECT_INFO_CHANGED, handle);
272*ec779b8eSAndroid Build Coastguard Worker }
273*ec779b8eSAndroid Build Coastguard Worker 
sendStoreAdded(MtpStorageID id)274*ec779b8eSAndroid Build Coastguard Worker void MtpServer::sendStoreAdded(MtpStorageID id) {
275*ec779b8eSAndroid Build Coastguard Worker     ALOGV("sendStoreAdded %08X\n", id);
276*ec779b8eSAndroid Build Coastguard Worker     sendEvent(MTP_EVENT_STORE_ADDED, id);
277*ec779b8eSAndroid Build Coastguard Worker }
278*ec779b8eSAndroid Build Coastguard Worker 
sendStoreRemoved(MtpStorageID id)279*ec779b8eSAndroid Build Coastguard Worker void MtpServer::sendStoreRemoved(MtpStorageID id) {
280*ec779b8eSAndroid Build Coastguard Worker     ALOGV("sendStoreRemoved %08X\n", id);
281*ec779b8eSAndroid Build Coastguard Worker     sendEvent(MTP_EVENT_STORE_REMOVED, id);
282*ec779b8eSAndroid Build Coastguard Worker }
283*ec779b8eSAndroid Build Coastguard Worker 
sendDevicePropertyChanged(MtpDeviceProperty property)284*ec779b8eSAndroid Build Coastguard Worker void MtpServer::sendDevicePropertyChanged(MtpDeviceProperty property) {
285*ec779b8eSAndroid Build Coastguard Worker     ALOGV("sendDevicePropertyChanged %d\n", property);
286*ec779b8eSAndroid Build Coastguard Worker     sendEvent(MTP_EVENT_DEVICE_PROP_CHANGED, property);
287*ec779b8eSAndroid Build Coastguard Worker }
288*ec779b8eSAndroid Build Coastguard Worker 
sendEvent(MtpEventCode code,uint32_t param1)289*ec779b8eSAndroid Build Coastguard Worker void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
290*ec779b8eSAndroid Build Coastguard Worker     if (mSessionOpen) {
291*ec779b8eSAndroid Build Coastguard Worker         mEvent.setEventCode(code);
292*ec779b8eSAndroid Build Coastguard Worker         mEvent.setTransactionID(mRequest.getTransactionID());
293*ec779b8eSAndroid Build Coastguard Worker         mEvent.setParameter(1, param1);
294*ec779b8eSAndroid Build Coastguard Worker         if (mEvent.write(mHandle))
295*ec779b8eSAndroid Build Coastguard Worker             ALOGE("Mtp send event failed: %s", strerror(errno));
296*ec779b8eSAndroid Build Coastguard Worker     }
297*ec779b8eSAndroid Build Coastguard Worker }
298*ec779b8eSAndroid Build Coastguard Worker 
addEditObject(MtpObjectHandle handle,MtpStringBuffer & path,uint64_t size,MtpObjectFormat format,int fd)299*ec779b8eSAndroid Build Coastguard Worker void MtpServer::addEditObject(MtpObjectHandle handle, MtpStringBuffer& path,
300*ec779b8eSAndroid Build Coastguard Worker         uint64_t size, MtpObjectFormat format, int fd) {
301*ec779b8eSAndroid Build Coastguard Worker     ObjectEdit*  edit = new ObjectEdit(handle, path, size, format, fd);
302*ec779b8eSAndroid Build Coastguard Worker     mObjectEditList.push_back(edit);
303*ec779b8eSAndroid Build Coastguard Worker }
304*ec779b8eSAndroid Build Coastguard Worker 
getEditObject(MtpObjectHandle handle)305*ec779b8eSAndroid Build Coastguard Worker MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
306*ec779b8eSAndroid Build Coastguard Worker     int count = mObjectEditList.size();
307*ec779b8eSAndroid Build Coastguard Worker     for (int i = 0; i < count; i++) {
308*ec779b8eSAndroid Build Coastguard Worker         ObjectEdit* edit = mObjectEditList[i];
309*ec779b8eSAndroid Build Coastguard Worker         if (edit->mHandle == handle) return edit;
310*ec779b8eSAndroid Build Coastguard Worker     }
311*ec779b8eSAndroid Build Coastguard Worker     return nullptr;
312*ec779b8eSAndroid Build Coastguard Worker }
313*ec779b8eSAndroid Build Coastguard Worker 
removeEditObject(MtpObjectHandle handle)314*ec779b8eSAndroid Build Coastguard Worker void MtpServer::removeEditObject(MtpObjectHandle handle) {
315*ec779b8eSAndroid Build Coastguard Worker     int count = mObjectEditList.size();
316*ec779b8eSAndroid Build Coastguard Worker     for (int i = 0; i < count; i++) {
317*ec779b8eSAndroid Build Coastguard Worker         ObjectEdit* edit = mObjectEditList[i];
318*ec779b8eSAndroid Build Coastguard Worker         if (edit->mHandle == handle) {
319*ec779b8eSAndroid Build Coastguard Worker             delete edit;
320*ec779b8eSAndroid Build Coastguard Worker             mObjectEditList.erase(mObjectEditList.begin() + i);
321*ec779b8eSAndroid Build Coastguard Worker             return;
322*ec779b8eSAndroid Build Coastguard Worker         }
323*ec779b8eSAndroid Build Coastguard Worker     }
324*ec779b8eSAndroid Build Coastguard Worker     ALOGE("ObjectEdit not found in removeEditObject");
325*ec779b8eSAndroid Build Coastguard Worker }
326*ec779b8eSAndroid Build Coastguard Worker 
commitEdit(ObjectEdit * edit)327*ec779b8eSAndroid Build Coastguard Worker void MtpServer::commitEdit(ObjectEdit* edit) {
328*ec779b8eSAndroid Build Coastguard Worker     mDatabase->rescanFile((const char *)edit->mPath, edit->mHandle, edit->mFormat);
329*ec779b8eSAndroid Build Coastguard Worker }
330*ec779b8eSAndroid Build Coastguard Worker 
331*ec779b8eSAndroid Build Coastguard Worker 
handleRequest()332*ec779b8eSAndroid Build Coastguard Worker bool MtpServer::handleRequest() {
333*ec779b8eSAndroid Build Coastguard Worker     std::lock_guard<std::mutex> lg(mMutex);
334*ec779b8eSAndroid Build Coastguard Worker 
335*ec779b8eSAndroid Build Coastguard Worker     MtpOperationCode operation = mRequest.getOperationCode();
336*ec779b8eSAndroid Build Coastguard Worker     MtpResponseCode response;
337*ec779b8eSAndroid Build Coastguard Worker 
338*ec779b8eSAndroid Build Coastguard Worker     mResponse.reset();
339*ec779b8eSAndroid Build Coastguard Worker 
340*ec779b8eSAndroid Build Coastguard Worker     if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
341*ec779b8eSAndroid Build Coastguard Worker         mSendObjectHandle = kInvalidObjectHandle;
342*ec779b8eSAndroid Build Coastguard Worker         mSendObjectFormat = 0;
343*ec779b8eSAndroid Build Coastguard Worker         mSendObjectModifiedTime = 0;
344*ec779b8eSAndroid Build Coastguard Worker     }
345*ec779b8eSAndroid Build Coastguard Worker 
346*ec779b8eSAndroid Build Coastguard Worker     int containertype = mRequest.getContainerType();
347*ec779b8eSAndroid Build Coastguard Worker     if (containertype != MTP_CONTAINER_TYPE_COMMAND) {
348*ec779b8eSAndroid Build Coastguard Worker         ALOGE("wrong container type %d", containertype);
349*ec779b8eSAndroid Build Coastguard Worker         return false;
350*ec779b8eSAndroid Build Coastguard Worker     }
351*ec779b8eSAndroid Build Coastguard Worker 
352*ec779b8eSAndroid Build Coastguard Worker     ALOGV("got command %s (%x)", MtpDebug::getOperationCodeName(operation), operation);
353*ec779b8eSAndroid Build Coastguard Worker 
354*ec779b8eSAndroid Build Coastguard Worker     switch (operation) {
355*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_DEVICE_INFO:
356*ec779b8eSAndroid Build Coastguard Worker             response = doGetDeviceInfo();
357*ec779b8eSAndroid Build Coastguard Worker             break;
358*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_OPEN_SESSION:
359*ec779b8eSAndroid Build Coastguard Worker             response = doOpenSession();
360*ec779b8eSAndroid Build Coastguard Worker             break;
361*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_RESET_DEVICE:
362*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_CLOSE_SESSION:
363*ec779b8eSAndroid Build Coastguard Worker             response = doCloseSession();
364*ec779b8eSAndroid Build Coastguard Worker             break;
365*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_STORAGE_IDS:
366*ec779b8eSAndroid Build Coastguard Worker             response = doGetStorageIDs();
367*ec779b8eSAndroid Build Coastguard Worker             break;
368*ec779b8eSAndroid Build Coastguard Worker          case MTP_OPERATION_GET_STORAGE_INFO:
369*ec779b8eSAndroid Build Coastguard Worker             response = doGetStorageInfo();
370*ec779b8eSAndroid Build Coastguard Worker             break;
371*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
372*ec779b8eSAndroid Build Coastguard Worker             response = doGetObjectPropsSupported();
373*ec779b8eSAndroid Build Coastguard Worker             break;
374*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_OBJECT_HANDLES:
375*ec779b8eSAndroid Build Coastguard Worker             response = doGetObjectHandles();
376*ec779b8eSAndroid Build Coastguard Worker             break;
377*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_NUM_OBJECTS:
378*ec779b8eSAndroid Build Coastguard Worker             response = doGetNumObjects();
379*ec779b8eSAndroid Build Coastguard Worker             break;
380*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_OBJECT_REFERENCES:
381*ec779b8eSAndroid Build Coastguard Worker             response = doGetObjectReferences();
382*ec779b8eSAndroid Build Coastguard Worker             break;
383*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_SET_OBJECT_REFERENCES:
384*ec779b8eSAndroid Build Coastguard Worker             response = doSetObjectReferences();
385*ec779b8eSAndroid Build Coastguard Worker             break;
386*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
387*ec779b8eSAndroid Build Coastguard Worker             response = doGetObjectPropValue();
388*ec779b8eSAndroid Build Coastguard Worker             break;
389*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
390*ec779b8eSAndroid Build Coastguard Worker             response = doSetObjectPropValue();
391*ec779b8eSAndroid Build Coastguard Worker             break;
392*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
393*ec779b8eSAndroid Build Coastguard Worker             response = doGetDevicePropValue();
394*ec779b8eSAndroid Build Coastguard Worker             break;
395*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
396*ec779b8eSAndroid Build Coastguard Worker             response = doSetDevicePropValue();
397*ec779b8eSAndroid Build Coastguard Worker             break;
398*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
399*ec779b8eSAndroid Build Coastguard Worker             response = doResetDevicePropValue();
400*ec779b8eSAndroid Build Coastguard Worker             break;
401*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_OBJECT_PROP_LIST:
402*ec779b8eSAndroid Build Coastguard Worker             response = doGetObjectPropList();
403*ec779b8eSAndroid Build Coastguard Worker             break;
404*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_OBJECT_INFO:
405*ec779b8eSAndroid Build Coastguard Worker             response = doGetObjectInfo();
406*ec779b8eSAndroid Build Coastguard Worker             break;
407*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_OBJECT:
408*ec779b8eSAndroid Build Coastguard Worker             response = doGetObject();
409*ec779b8eSAndroid Build Coastguard Worker             break;
410*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_THUMB:
411*ec779b8eSAndroid Build Coastguard Worker             response = doGetThumb();
412*ec779b8eSAndroid Build Coastguard Worker             break;
413*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_PARTIAL_OBJECT:
414*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
415*ec779b8eSAndroid Build Coastguard Worker             response = doGetPartialObject(operation);
416*ec779b8eSAndroid Build Coastguard Worker             break;
417*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_SEND_OBJECT_INFO:
418*ec779b8eSAndroid Build Coastguard Worker             response = doSendObjectInfo();
419*ec779b8eSAndroid Build Coastguard Worker             break;
420*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_SEND_OBJECT:
421*ec779b8eSAndroid Build Coastguard Worker             response = doSendObject();
422*ec779b8eSAndroid Build Coastguard Worker             break;
423*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_DELETE_OBJECT:
424*ec779b8eSAndroid Build Coastguard Worker             response = doDeleteObject();
425*ec779b8eSAndroid Build Coastguard Worker             break;
426*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_COPY_OBJECT:
427*ec779b8eSAndroid Build Coastguard Worker             response = doCopyObject();
428*ec779b8eSAndroid Build Coastguard Worker             break;
429*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_MOVE_OBJECT:
430*ec779b8eSAndroid Build Coastguard Worker             response = doMoveObject();
431*ec779b8eSAndroid Build Coastguard Worker             break;
432*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_OBJECT_PROP_DESC:
433*ec779b8eSAndroid Build Coastguard Worker             response = doGetObjectPropDesc();
434*ec779b8eSAndroid Build Coastguard Worker             break;
435*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_GET_DEVICE_PROP_DESC:
436*ec779b8eSAndroid Build Coastguard Worker             response = doGetDevicePropDesc();
437*ec779b8eSAndroid Build Coastguard Worker             break;
438*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_SEND_PARTIAL_OBJECT:
439*ec779b8eSAndroid Build Coastguard Worker             response = doSendPartialObject();
440*ec779b8eSAndroid Build Coastguard Worker             break;
441*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_TRUNCATE_OBJECT:
442*ec779b8eSAndroid Build Coastguard Worker             response = doTruncateObject();
443*ec779b8eSAndroid Build Coastguard Worker             break;
444*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_BEGIN_EDIT_OBJECT:
445*ec779b8eSAndroid Build Coastguard Worker             response = doBeginEditObject();
446*ec779b8eSAndroid Build Coastguard Worker             break;
447*ec779b8eSAndroid Build Coastguard Worker         case MTP_OPERATION_END_EDIT_OBJECT:
448*ec779b8eSAndroid Build Coastguard Worker             response = doEndEditObject();
449*ec779b8eSAndroid Build Coastguard Worker             break;
450*ec779b8eSAndroid Build Coastguard Worker         default:
451*ec779b8eSAndroid Build Coastguard Worker             ALOGE("got unsupported command %s (%x)",
452*ec779b8eSAndroid Build Coastguard Worker                     MtpDebug::getOperationCodeName(operation), operation);
453*ec779b8eSAndroid Build Coastguard Worker             response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
454*ec779b8eSAndroid Build Coastguard Worker             break;
455*ec779b8eSAndroid Build Coastguard Worker     }
456*ec779b8eSAndroid Build Coastguard Worker 
457*ec779b8eSAndroid Build Coastguard Worker     if (response != MTP_RESPONSE_OK)
458*ec779b8eSAndroid Build Coastguard Worker       ALOGW("[MTP] got response 0x%X in command %s (%x)", response,
459*ec779b8eSAndroid Build Coastguard Worker             MtpDebug::getOperationCodeName(operation), operation);
460*ec779b8eSAndroid Build Coastguard Worker     if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
461*ec779b8eSAndroid Build Coastguard Worker         return false;
462*ec779b8eSAndroid Build Coastguard Worker     mResponse.setResponseCode(response);
463*ec779b8eSAndroid Build Coastguard Worker     return true;
464*ec779b8eSAndroid Build Coastguard Worker }
465*ec779b8eSAndroid Build Coastguard Worker 
doGetDeviceInfo()466*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetDeviceInfo() {
467*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer   string;
468*ec779b8eSAndroid Build Coastguard Worker 
469*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
470*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
471*ec779b8eSAndroid Build Coastguard Worker     MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
472*ec779b8eSAndroid Build Coastguard Worker 
473*ec779b8eSAndroid Build Coastguard Worker     // fill in device info
474*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt16(MTP_STANDARD_VERSION);
475*ec779b8eSAndroid Build Coastguard Worker     if (mPtp) {
476*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(0);
477*ec779b8eSAndroid Build Coastguard Worker     } else {
478*ec779b8eSAndroid Build Coastguard Worker         // MTP Vendor Extension ID
479*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(6);
480*ec779b8eSAndroid Build Coastguard Worker     }
481*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt16(MTP_STANDARD_VERSION);
482*ec779b8eSAndroid Build Coastguard Worker     if (mPtp) {
483*ec779b8eSAndroid Build Coastguard Worker         // no extensions
484*ec779b8eSAndroid Build Coastguard Worker         string.set("");
485*ec779b8eSAndroid Build Coastguard Worker     } else {
486*ec779b8eSAndroid Build Coastguard Worker         // MTP extensions
487*ec779b8eSAndroid Build Coastguard Worker         string.set("microsoft.com: 1.0; android.com: 1.0;");
488*ec779b8eSAndroid Build Coastguard Worker     }
489*ec779b8eSAndroid Build Coastguard Worker     mData.putString(string); // MTP Extensions
490*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt16(0); //Functional Mode
491*ec779b8eSAndroid Build Coastguard Worker     mData.putAUInt16(kSupportedOperationCodes,
492*ec779b8eSAndroid Build Coastguard Worker             sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
493*ec779b8eSAndroid Build Coastguard Worker     mData.putAUInt16(kSupportedEventCodes,
494*ec779b8eSAndroid Build Coastguard Worker             sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
495*ec779b8eSAndroid Build Coastguard Worker     mData.putAUInt16(deviceProperties); // Device Properties Supported
496*ec779b8eSAndroid Build Coastguard Worker     mData.putAUInt16(captureFormats); // Capture Formats
497*ec779b8eSAndroid Build Coastguard Worker     mData.putAUInt16(playbackFormats);  // Playback Formats
498*ec779b8eSAndroid Build Coastguard Worker 
499*ec779b8eSAndroid Build Coastguard Worker     mData.putString(mDeviceInfoManufacturer); // Manufacturer
500*ec779b8eSAndroid Build Coastguard Worker     mData.putString(mDeviceInfoModel); // Model
501*ec779b8eSAndroid Build Coastguard Worker     mData.putString(mDeviceInfoDeviceVersion); // Device Version
502*ec779b8eSAndroid Build Coastguard Worker     mData.putString(mDeviceInfoSerialNumber); // Serial Number
503*ec779b8eSAndroid Build Coastguard Worker 
504*ec779b8eSAndroid Build Coastguard Worker     delete playbackFormats;
505*ec779b8eSAndroid Build Coastguard Worker     delete captureFormats;
506*ec779b8eSAndroid Build Coastguard Worker     delete deviceProperties;
507*ec779b8eSAndroid Build Coastguard Worker 
508*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
509*ec779b8eSAndroid Build Coastguard Worker }
510*ec779b8eSAndroid Build Coastguard Worker 
doOpenSession()511*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doOpenSession() {
512*ec779b8eSAndroid Build Coastguard Worker     if (mSessionOpen) {
513*ec779b8eSAndroid Build Coastguard Worker         mResponse.setParameter(1, mSessionID);
514*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_ALREADY_OPEN;
515*ec779b8eSAndroid Build Coastguard Worker     }
516*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
517*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
518*ec779b8eSAndroid Build Coastguard Worker 
519*ec779b8eSAndroid Build Coastguard Worker     mSessionID = mRequest.getParameter(1);
520*ec779b8eSAndroid Build Coastguard Worker     mSessionOpen = true;
521*ec779b8eSAndroid Build Coastguard Worker 
522*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
523*ec779b8eSAndroid Build Coastguard Worker }
524*ec779b8eSAndroid Build Coastguard Worker 
doCloseSession()525*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doCloseSession() {
526*ec779b8eSAndroid Build Coastguard Worker     if (!mSessionOpen)
527*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_NOT_OPEN;
528*ec779b8eSAndroid Build Coastguard Worker     mSessionID = 0;
529*ec779b8eSAndroid Build Coastguard Worker     mSessionOpen = false;
530*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
531*ec779b8eSAndroid Build Coastguard Worker }
532*ec779b8eSAndroid Build Coastguard Worker 
doGetStorageIDs()533*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetStorageIDs() {
534*ec779b8eSAndroid Build Coastguard Worker     if (!mSessionOpen)
535*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_NOT_OPEN;
536*ec779b8eSAndroid Build Coastguard Worker 
537*ec779b8eSAndroid Build Coastguard Worker     int count = mStorages.size();
538*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt32(count);
539*ec779b8eSAndroid Build Coastguard Worker     for (int i = 0; i < count; i++)
540*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(mStorages[i]->getStorageID());
541*ec779b8eSAndroid Build Coastguard Worker 
542*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
543*ec779b8eSAndroid Build Coastguard Worker }
544*ec779b8eSAndroid Build Coastguard Worker 
doGetStorageInfo()545*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetStorageInfo() {
546*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer   string;
547*ec779b8eSAndroid Build Coastguard Worker 
548*ec779b8eSAndroid Build Coastguard Worker     if (!mSessionOpen)
549*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_NOT_OPEN;
550*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
551*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
552*ec779b8eSAndroid Build Coastguard Worker 
553*ec779b8eSAndroid Build Coastguard Worker     MtpStorageID id = mRequest.getParameter(1);
554*ec779b8eSAndroid Build Coastguard Worker     MtpStorage* storage = getStorage(id);
555*ec779b8eSAndroid Build Coastguard Worker     if (!storage)
556*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_STORAGE_ID;
557*ec779b8eSAndroid Build Coastguard Worker 
558*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt16(storage->getType());
559*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt16(storage->getFileSystemType());
560*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt16(storage->getAccessCapability());
561*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt64(storage->getMaxCapacity());
562*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt64(storage->getFreeSpace());
563*ec779b8eSAndroid Build Coastguard Worker     mData.putUInt32(1024*1024*1024); // Free Space in Objects
564*ec779b8eSAndroid Build Coastguard Worker     string.set(storage->getDescription());
565*ec779b8eSAndroid Build Coastguard Worker     mData.putString(string);
566*ec779b8eSAndroid Build Coastguard Worker     mData.putEmptyString();   // Volume Identifier
567*ec779b8eSAndroid Build Coastguard Worker 
568*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
569*ec779b8eSAndroid Build Coastguard Worker }
570*ec779b8eSAndroid Build Coastguard Worker 
doGetObjectPropsSupported()571*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetObjectPropsSupported() {
572*ec779b8eSAndroid Build Coastguard Worker     if (!mSessionOpen)
573*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_NOT_OPEN;
574*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
575*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
576*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format = mRequest.getParameter(1);
577*ec779b8eSAndroid Build Coastguard Worker     MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
578*ec779b8eSAndroid Build Coastguard Worker     mData.putAUInt16(properties);
579*ec779b8eSAndroid Build Coastguard Worker     delete properties;
580*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
581*ec779b8eSAndroid Build Coastguard Worker }
582*ec779b8eSAndroid Build Coastguard Worker 
doGetObjectHandles()583*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetObjectHandles() {
584*ec779b8eSAndroid Build Coastguard Worker     if (!mSessionOpen)
585*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_NOT_OPEN;
586*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 3)
587*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
588*ec779b8eSAndroid Build Coastguard Worker     MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
589*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
590*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
591*ec779b8eSAndroid Build Coastguard Worker                                                             // 0x00000000 for all objects
592*ec779b8eSAndroid Build Coastguard Worker 
593*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage(storageID))
594*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_STORAGE_ID;
595*ec779b8eSAndroid Build Coastguard Worker 
596*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
597*ec779b8eSAndroid Build Coastguard Worker     if (handles == NULL)
598*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
599*ec779b8eSAndroid Build Coastguard Worker     mData.putAUInt32(handles);
600*ec779b8eSAndroid Build Coastguard Worker     delete handles;
601*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
602*ec779b8eSAndroid Build Coastguard Worker }
603*ec779b8eSAndroid Build Coastguard Worker 
doGetNumObjects()604*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetNumObjects() {
605*ec779b8eSAndroid Build Coastguard Worker     if (!mSessionOpen)
606*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_NOT_OPEN;
607*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 3)
608*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
609*ec779b8eSAndroid Build Coastguard Worker     MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
610*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
611*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
612*ec779b8eSAndroid Build Coastguard Worker                                                             // 0x00000000 for all objects
613*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage(storageID))
614*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_STORAGE_ID;
615*ec779b8eSAndroid Build Coastguard Worker 
616*ec779b8eSAndroid Build Coastguard Worker     int count = mDatabase->getNumObjects(storageID, format, parent);
617*ec779b8eSAndroid Build Coastguard Worker     if (count >= 0) {
618*ec779b8eSAndroid Build Coastguard Worker         mResponse.setParameter(1, count);
619*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_OK;
620*ec779b8eSAndroid Build Coastguard Worker     } else {
621*ec779b8eSAndroid Build Coastguard Worker         mResponse.setParameter(1, 0);
622*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
623*ec779b8eSAndroid Build Coastguard Worker     }
624*ec779b8eSAndroid Build Coastguard Worker }
625*ec779b8eSAndroid Build Coastguard Worker 
doGetObjectReferences()626*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetObjectReferences() {
627*ec779b8eSAndroid Build Coastguard Worker     if (!mSessionOpen)
628*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_NOT_OPEN;
629*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
630*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
631*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
632*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
633*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
634*ec779b8eSAndroid Build Coastguard Worker 
635*ec779b8eSAndroid Build Coastguard Worker     // FIXME - check for invalid object handle
636*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
637*ec779b8eSAndroid Build Coastguard Worker     if (handles) {
638*ec779b8eSAndroid Build Coastguard Worker         mData.putAUInt32(handles);
639*ec779b8eSAndroid Build Coastguard Worker         delete handles;
640*ec779b8eSAndroid Build Coastguard Worker     } else {
641*ec779b8eSAndroid Build Coastguard Worker         mData.putEmptyArray();
642*ec779b8eSAndroid Build Coastguard Worker     }
643*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
644*ec779b8eSAndroid Build Coastguard Worker }
645*ec779b8eSAndroid Build Coastguard Worker 
doSetObjectReferences()646*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doSetObjectReferences() {
647*ec779b8eSAndroid Build Coastguard Worker     if (!mSessionOpen)
648*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_SESSION_NOT_OPEN;
649*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
650*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
651*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
652*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
653*ec779b8eSAndroid Build Coastguard Worker     MtpStorageID handle = mRequest.getParameter(1);
654*ec779b8eSAndroid Build Coastguard Worker 
655*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandleList* references = mData.getAUInt32();
656*ec779b8eSAndroid Build Coastguard Worker     if (!references)
657*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
658*ec779b8eSAndroid Build Coastguard Worker     MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
659*ec779b8eSAndroid Build Coastguard Worker     delete references;
660*ec779b8eSAndroid Build Coastguard Worker     return result;
661*ec779b8eSAndroid Build Coastguard Worker }
662*ec779b8eSAndroid Build Coastguard Worker 
doGetObjectPropValue()663*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetObjectPropValue() {
664*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
665*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
666*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 2)
667*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
668*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
669*ec779b8eSAndroid Build Coastguard Worker     MtpObjectProperty property = mRequest.getParameter(2);
670*ec779b8eSAndroid Build Coastguard Worker     ALOGV("GetObjectPropValue %d %s (0x%04X)\n", handle,
671*ec779b8eSAndroid Build Coastguard Worker           MtpDebug::getObjectPropCodeName(property), property);
672*ec779b8eSAndroid Build Coastguard Worker 
673*ec779b8eSAndroid Build Coastguard Worker     return mDatabase->getObjectPropertyValue(handle, property, mData);
674*ec779b8eSAndroid Build Coastguard Worker }
675*ec779b8eSAndroid Build Coastguard Worker 
doSetObjectPropValue()676*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doSetObjectPropValue() {
677*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
678*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
679*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 2)
680*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
681*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
682*ec779b8eSAndroid Build Coastguard Worker     MtpObjectProperty property = mRequest.getParameter(2);
683*ec779b8eSAndroid Build Coastguard Worker     ALOGV("SetObjectPropValue %d %s\n", handle,
684*ec779b8eSAndroid Build Coastguard Worker             MtpDebug::getObjectPropCodeName(property));
685*ec779b8eSAndroid Build Coastguard Worker 
686*ec779b8eSAndroid Build Coastguard Worker     return mDatabase->setObjectPropertyValue(handle, property, mData);
687*ec779b8eSAndroid Build Coastguard Worker }
688*ec779b8eSAndroid Build Coastguard Worker 
doGetDevicePropValue()689*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetDevicePropValue() {
690*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
691*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
692*ec779b8eSAndroid Build Coastguard Worker     MtpDeviceProperty property = mRequest.getParameter(1);
693*ec779b8eSAndroid Build Coastguard Worker     ALOGV("GetDevicePropValue %s\n",
694*ec779b8eSAndroid Build Coastguard Worker             MtpDebug::getDevicePropCodeName(property));
695*ec779b8eSAndroid Build Coastguard Worker 
696*ec779b8eSAndroid Build Coastguard Worker     return mDatabase->getDevicePropertyValue(property, mData);
697*ec779b8eSAndroid Build Coastguard Worker }
698*ec779b8eSAndroid Build Coastguard Worker 
doSetDevicePropValue()699*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doSetDevicePropValue() {
700*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
701*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
702*ec779b8eSAndroid Build Coastguard Worker     MtpDeviceProperty property = mRequest.getParameter(1);
703*ec779b8eSAndroid Build Coastguard Worker     ALOGV("SetDevicePropValue %s\n",
704*ec779b8eSAndroid Build Coastguard Worker             MtpDebug::getDevicePropCodeName(property));
705*ec779b8eSAndroid Build Coastguard Worker 
706*ec779b8eSAndroid Build Coastguard Worker     return mDatabase->setDevicePropertyValue(property, mData);
707*ec779b8eSAndroid Build Coastguard Worker }
708*ec779b8eSAndroid Build Coastguard Worker 
doResetDevicePropValue()709*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doResetDevicePropValue() {
710*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
711*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
712*ec779b8eSAndroid Build Coastguard Worker     MtpDeviceProperty property = mRequest.getParameter(1);
713*ec779b8eSAndroid Build Coastguard Worker     ALOGV("ResetDevicePropValue %s\n",
714*ec779b8eSAndroid Build Coastguard Worker             MtpDebug::getDevicePropCodeName(property));
715*ec779b8eSAndroid Build Coastguard Worker 
716*ec779b8eSAndroid Build Coastguard Worker     return mDatabase->resetDeviceProperty(property);
717*ec779b8eSAndroid Build Coastguard Worker }
718*ec779b8eSAndroid Build Coastguard Worker 
doGetObjectPropList()719*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetObjectPropList() {
720*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
721*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
722*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 5)
723*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
724*ec779b8eSAndroid Build Coastguard Worker 
725*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
726*ec779b8eSAndroid Build Coastguard Worker     // use uint32_t so we can support 0xFFFFFFFF
727*ec779b8eSAndroid Build Coastguard Worker     uint32_t format = mRequest.getParameter(2);
728*ec779b8eSAndroid Build Coastguard Worker     uint32_t property = mRequest.getParameter(3);
729*ec779b8eSAndroid Build Coastguard Worker     int groupCode = mRequest.getParameter(4);
730*ec779b8eSAndroid Build Coastguard Worker     int depth = mRequest.getParameter(5);
731*ec779b8eSAndroid Build Coastguard Worker    ALOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
732*ec779b8eSAndroid Build Coastguard Worker             handle, MtpDebug::getFormatCodeName(format),
733*ec779b8eSAndroid Build Coastguard Worker             MtpDebug::getObjectPropCodeName(property), groupCode, depth);
734*ec779b8eSAndroid Build Coastguard Worker 
735*ec779b8eSAndroid Build Coastguard Worker     return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
736*ec779b8eSAndroid Build Coastguard Worker }
737*ec779b8eSAndroid Build Coastguard Worker 
doGetObjectInfo()738*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetObjectInfo() {
739*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
740*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
741*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
742*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
743*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
744*ec779b8eSAndroid Build Coastguard Worker     MtpObjectInfo info(handle);
745*ec779b8eSAndroid Build Coastguard Worker     MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
746*ec779b8eSAndroid Build Coastguard Worker     if (result == MTP_RESPONSE_OK) {
747*ec779b8eSAndroid Build Coastguard Worker         char    date[20];
748*ec779b8eSAndroid Build Coastguard Worker 
749*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mStorageID);
750*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt16(info.mFormat);
751*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt16(info.mProtectionStatus);
752*ec779b8eSAndroid Build Coastguard Worker 
753*ec779b8eSAndroid Build Coastguard Worker         // if object is being edited the database size may be out of date
754*ec779b8eSAndroid Build Coastguard Worker         uint32_t size = info.mCompressedSize;
755*ec779b8eSAndroid Build Coastguard Worker         ObjectEdit* edit = getEditObject(handle);
756*ec779b8eSAndroid Build Coastguard Worker         if (edit)
757*ec779b8eSAndroid Build Coastguard Worker             size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
758*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(size);
759*ec779b8eSAndroid Build Coastguard Worker 
760*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt16(info.mThumbFormat);
761*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mThumbCompressedSize);
762*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mThumbPixWidth);
763*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mThumbPixHeight);
764*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mImagePixWidth);
765*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mImagePixHeight);
766*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mImagePixDepth);
767*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mParent);
768*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt16(info.mAssociationType);
769*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mAssociationDesc);
770*ec779b8eSAndroid Build Coastguard Worker         mData.putUInt32(info.mSequenceNumber);
771*ec779b8eSAndroid Build Coastguard Worker         mData.putString(info.mName);
772*ec779b8eSAndroid Build Coastguard Worker         formatDateTime(info.mDateCreated, date, sizeof(date));
773*ec779b8eSAndroid Build Coastguard Worker         mData.putString(date);   // date created
774*ec779b8eSAndroid Build Coastguard Worker         formatDateTime(info.mDateModified, date, sizeof(date));
775*ec779b8eSAndroid Build Coastguard Worker         mData.putString(date);   // date modified
776*ec779b8eSAndroid Build Coastguard Worker         mData.putEmptyString();   // keywords
777*ec779b8eSAndroid Build Coastguard Worker     }
778*ec779b8eSAndroid Build Coastguard Worker     return result;
779*ec779b8eSAndroid Build Coastguard Worker }
780*ec779b8eSAndroid Build Coastguard Worker 
doGetObject()781*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetObject() {
782*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
783*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
784*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
785*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
786*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
787*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer pathBuf;
788*ec779b8eSAndroid Build Coastguard Worker     int64_t fileLength;
789*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format;
790*ec779b8eSAndroid Build Coastguard Worker     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
791*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
792*ec779b8eSAndroid Build Coastguard Worker         return result;
793*ec779b8eSAndroid Build Coastguard Worker 
794*ec779b8eSAndroid Build Coastguard Worker     auto start = std::chrono::steady_clock::now();
795*ec779b8eSAndroid Build Coastguard Worker 
796*ec779b8eSAndroid Build Coastguard Worker     const char* filePath = (const char *)pathBuf;
797*ec779b8eSAndroid Build Coastguard Worker     mtp_file_range mfr;
798*ec779b8eSAndroid Build Coastguard Worker     struct stat sstat;
799*ec779b8eSAndroid Build Coastguard Worker     uint64_t finalsize;
800*ec779b8eSAndroid Build Coastguard Worker     bool transcode = android::base::GetBoolProperty("sys.fuse.transcode_mtp", false);
801*ec779b8eSAndroid Build Coastguard Worker     bool filePathAccess = true;
802*ec779b8eSAndroid Build Coastguard Worker     ALOGD("Mtp transcode = %d", transcode);
803*ec779b8eSAndroid Build Coastguard Worker 
804*ec779b8eSAndroid Build Coastguard Worker     // For performance reasons, only attempt a ContentResolver open when transcode is required.
805*ec779b8eSAndroid Build Coastguard Worker     // This is fine as long as we don't transcode by default on the device. If we suddenly
806*ec779b8eSAndroid Build Coastguard Worker     // transcode by default, we'll need to ensure that MTP doesn't transcode by default and we
807*ec779b8eSAndroid Build Coastguard Worker     // might need to make a binder call to avoid transcoding or come up with a better strategy.
808*ec779b8eSAndroid Build Coastguard Worker     if (transcode) {
809*ec779b8eSAndroid Build Coastguard Worker         mfr.fd = mDatabase->openFilePath(filePath, true);
810*ec779b8eSAndroid Build Coastguard Worker         fstat(mfr.fd, &sstat);
811*ec779b8eSAndroid Build Coastguard Worker         finalsize = sstat.st_size;
812*ec779b8eSAndroid Build Coastguard Worker         fileLength = finalsize;
813*ec779b8eSAndroid Build Coastguard Worker         if (mfr.fd < 0) {
814*ec779b8eSAndroid Build Coastguard Worker             ALOGW("Mtp open via IMtpDatabase failed for %s. Falling back to the original",
815*ec779b8eSAndroid Build Coastguard Worker                   filePath);
816*ec779b8eSAndroid Build Coastguard Worker             filePathAccess = true;
817*ec779b8eSAndroid Build Coastguard Worker         } else {
818*ec779b8eSAndroid Build Coastguard Worker             filePathAccess = false;
819*ec779b8eSAndroid Build Coastguard Worker         }
820*ec779b8eSAndroid Build Coastguard Worker     }
821*ec779b8eSAndroid Build Coastguard Worker 
822*ec779b8eSAndroid Build Coastguard Worker     if (filePathAccess) {
823*ec779b8eSAndroid Build Coastguard Worker         mfr.fd = open(filePath, O_RDONLY);
824*ec779b8eSAndroid Build Coastguard Worker         if (mfr.fd < 0) {
825*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_GENERAL_ERROR;
826*ec779b8eSAndroid Build Coastguard Worker         }
827*ec779b8eSAndroid Build Coastguard Worker         fstat(mfr.fd, &sstat);
828*ec779b8eSAndroid Build Coastguard Worker         finalsize = sstat.st_size;
829*ec779b8eSAndroid Build Coastguard Worker     }
830*ec779b8eSAndroid Build Coastguard Worker 
831*ec779b8eSAndroid Build Coastguard Worker     mfr.offset = 0;
832*ec779b8eSAndroid Build Coastguard Worker     mfr.length = fileLength;
833*ec779b8eSAndroid Build Coastguard Worker     mfr.command = mRequest.getOperationCode();
834*ec779b8eSAndroid Build Coastguard Worker     mfr.transaction_id = mRequest.getTransactionID();
835*ec779b8eSAndroid Build Coastguard Worker 
836*ec779b8eSAndroid Build Coastguard Worker     // then transfer the file
837*ec779b8eSAndroid Build Coastguard Worker     int ret = mHandle->sendFile(mfr);
838*ec779b8eSAndroid Build Coastguard Worker     if (ret < 0) {
839*ec779b8eSAndroid Build Coastguard Worker         ALOGE("Mtp send file got error %s", strerror(errno));
840*ec779b8eSAndroid Build Coastguard Worker         if (errno == ECANCELED) {
841*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_TRANSACTION_CANCELLED;
842*ec779b8eSAndroid Build Coastguard Worker         } else {
843*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_GENERAL_ERROR;
844*ec779b8eSAndroid Build Coastguard Worker         }
845*ec779b8eSAndroid Build Coastguard Worker     } else {
846*ec779b8eSAndroid Build Coastguard Worker         result = MTP_RESPONSE_OK;
847*ec779b8eSAndroid Build Coastguard Worker     }
848*ec779b8eSAndroid Build Coastguard Worker 
849*ec779b8eSAndroid Build Coastguard Worker     auto end = std::chrono::steady_clock::now();
850*ec779b8eSAndroid Build Coastguard Worker     std::chrono::duration<double> diff = end - start;
851*ec779b8eSAndroid Build Coastguard Worker     ALOGV("Sent a file over MTP. Time: %f s, Size: %" PRIu64 ", Rate: %f bytes/s",
852*ec779b8eSAndroid Build Coastguard Worker             diff.count(), finalsize, ((double) finalsize) / diff.count());
853*ec779b8eSAndroid Build Coastguard Worker     closeObjFd(mfr.fd, filePath);
854*ec779b8eSAndroid Build Coastguard Worker     return result;
855*ec779b8eSAndroid Build Coastguard Worker }
856*ec779b8eSAndroid Build Coastguard Worker 
doGetThumb()857*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetThumb() {
858*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
859*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
860*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
861*ec779b8eSAndroid Build Coastguard Worker     size_t thumbSize;
862*ec779b8eSAndroid Build Coastguard Worker     void* thumb = mDatabase->getThumbnail(handle, thumbSize);
863*ec779b8eSAndroid Build Coastguard Worker     if (thumb) {
864*ec779b8eSAndroid Build Coastguard Worker         // send data
865*ec779b8eSAndroid Build Coastguard Worker         mData.setOperationCode(mRequest.getOperationCode());
866*ec779b8eSAndroid Build Coastguard Worker         mData.setTransactionID(mRequest.getTransactionID());
867*ec779b8eSAndroid Build Coastguard Worker         mData.writeData(mHandle, thumb, thumbSize);
868*ec779b8eSAndroid Build Coastguard Worker         free(thumb);
869*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_OK;
870*ec779b8eSAndroid Build Coastguard Worker     } else {
871*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
872*ec779b8eSAndroid Build Coastguard Worker     }
873*ec779b8eSAndroid Build Coastguard Worker }
874*ec779b8eSAndroid Build Coastguard Worker 
doGetPartialObject(MtpOperationCode operation)875*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
876*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
877*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
878*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
879*ec779b8eSAndroid Build Coastguard Worker     uint64_t offset;
880*ec779b8eSAndroid Build Coastguard Worker     uint32_t length;
881*ec779b8eSAndroid Build Coastguard Worker     offset = mRequest.getParameter(2);
882*ec779b8eSAndroid Build Coastguard Worker     if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
883*ec779b8eSAndroid Build Coastguard Worker         // MTP_OPERATION_GET_PARTIAL_OBJECT_64 takes 4 arguments
884*ec779b8eSAndroid Build Coastguard Worker         if (mRequest.getParameterCount() < 4)
885*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_INVALID_PARAMETER;
886*ec779b8eSAndroid Build Coastguard Worker 
887*ec779b8eSAndroid Build Coastguard Worker         // android extension with 64 bit offset
888*ec779b8eSAndroid Build Coastguard Worker         uint64_t offset2 = mRequest.getParameter(3);
889*ec779b8eSAndroid Build Coastguard Worker         offset = offset | (offset2 << 32);
890*ec779b8eSAndroid Build Coastguard Worker         length = mRequest.getParameter(4);
891*ec779b8eSAndroid Build Coastguard Worker     } else {
892*ec779b8eSAndroid Build Coastguard Worker         // MTP_OPERATION_GET_PARTIAL_OBJECT takes 3 arguments
893*ec779b8eSAndroid Build Coastguard Worker         if (mRequest.getParameterCount() < 3)
894*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_INVALID_PARAMETER;
895*ec779b8eSAndroid Build Coastguard Worker 
896*ec779b8eSAndroid Build Coastguard Worker         // standard GetPartialObject
897*ec779b8eSAndroid Build Coastguard Worker         length = mRequest.getParameter(3);
898*ec779b8eSAndroid Build Coastguard Worker     }
899*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer pathBuf;
900*ec779b8eSAndroid Build Coastguard Worker     int64_t fileLength;
901*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format;
902*ec779b8eSAndroid Build Coastguard Worker     int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
903*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
904*ec779b8eSAndroid Build Coastguard Worker         return result;
905*ec779b8eSAndroid Build Coastguard Worker     if (offset + length > (uint64_t)fileLength)
906*ec779b8eSAndroid Build Coastguard Worker         length = fileLength - offset;
907*ec779b8eSAndroid Build Coastguard Worker 
908*ec779b8eSAndroid Build Coastguard Worker     const char* filePath = (const char *)pathBuf;
909*ec779b8eSAndroid Build Coastguard Worker     ALOGV("sending partial %s %" PRIu64 " %" PRIu32, filePath, offset, length);
910*ec779b8eSAndroid Build Coastguard Worker     mtp_file_range  mfr;
911*ec779b8eSAndroid Build Coastguard Worker     mfr.fd = open(filePath, O_RDONLY);
912*ec779b8eSAndroid Build Coastguard Worker     if (mfr.fd < 0) {
913*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
914*ec779b8eSAndroid Build Coastguard Worker     }
915*ec779b8eSAndroid Build Coastguard Worker     mfr.offset = offset;
916*ec779b8eSAndroid Build Coastguard Worker     mfr.length = length;
917*ec779b8eSAndroid Build Coastguard Worker     mfr.command = mRequest.getOperationCode();
918*ec779b8eSAndroid Build Coastguard Worker     mfr.transaction_id = mRequest.getTransactionID();
919*ec779b8eSAndroid Build Coastguard Worker     mResponse.setParameter(1, length);
920*ec779b8eSAndroid Build Coastguard Worker 
921*ec779b8eSAndroid Build Coastguard Worker     // transfer the file
922*ec779b8eSAndroid Build Coastguard Worker     int ret = mHandle->sendFile(mfr);
923*ec779b8eSAndroid Build Coastguard Worker     ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
924*ec779b8eSAndroid Build Coastguard Worker     result = MTP_RESPONSE_OK;
925*ec779b8eSAndroid Build Coastguard Worker     if (ret < 0) {
926*ec779b8eSAndroid Build Coastguard Worker         if (errno == ECANCELED)
927*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_TRANSACTION_CANCELLED;
928*ec779b8eSAndroid Build Coastguard Worker         else
929*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_GENERAL_ERROR;
930*ec779b8eSAndroid Build Coastguard Worker     }
931*ec779b8eSAndroid Build Coastguard Worker     closeObjFd(mfr.fd, filePath);
932*ec779b8eSAndroid Build Coastguard Worker     return result;
933*ec779b8eSAndroid Build Coastguard Worker }
934*ec779b8eSAndroid Build Coastguard Worker 
doSendObjectInfo()935*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doSendObjectInfo() {
936*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer path;
937*ec779b8eSAndroid Build Coastguard Worker     uint16_t temp16;
938*ec779b8eSAndroid Build Coastguard Worker     uint32_t temp32;
939*ec779b8eSAndroid Build Coastguard Worker 
940*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 2)
941*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
942*ec779b8eSAndroid Build Coastguard Worker     MtpStorageID storageID = mRequest.getParameter(1);
943*ec779b8eSAndroid Build Coastguard Worker     MtpStorage* storage = getStorage(storageID);
944*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle parent = mRequest.getParameter(2);
945*ec779b8eSAndroid Build Coastguard Worker     if (!storage)
946*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_STORAGE_ID;
947*ec779b8eSAndroid Build Coastguard Worker 
948*ec779b8eSAndroid Build Coastguard Worker     // special case the root
949*ec779b8eSAndroid Build Coastguard Worker     if (parent == MTP_PARENT_ROOT) {
950*ec779b8eSAndroid Build Coastguard Worker         path.set(storage->getPath());
951*ec779b8eSAndroid Build Coastguard Worker         parent = 0;
952*ec779b8eSAndroid Build Coastguard Worker     } else {
953*ec779b8eSAndroid Build Coastguard Worker         int64_t length;
954*ec779b8eSAndroid Build Coastguard Worker         MtpObjectFormat format;
955*ec779b8eSAndroid Build Coastguard Worker         int result = mDatabase->getObjectFilePath(parent, path, length, format);
956*ec779b8eSAndroid Build Coastguard Worker         if (result != MTP_RESPONSE_OK)
957*ec779b8eSAndroid Build Coastguard Worker             return result;
958*ec779b8eSAndroid Build Coastguard Worker         if (format != MTP_FORMAT_ASSOCIATION)
959*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_INVALID_PARENT_OBJECT;
960*ec779b8eSAndroid Build Coastguard Worker     }
961*ec779b8eSAndroid Build Coastguard Worker 
962*ec779b8eSAndroid Build Coastguard Worker     // read only the fields we need
963*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // storage ID
964*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
965*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format = temp16;
966*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;  // protection status
967*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
968*ec779b8eSAndroid Build Coastguard Worker     mSendObjectFileSize = temp32;
969*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb format
970*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb compressed size
971*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb pix width
972*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb pix height
973*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image pix width
974*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image pix height
975*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image bit depth
976*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // parent
977*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
978*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
979*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // sequence number
980*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer name, created, modified;
981*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getString(name)) return MTP_RESPONSE_INVALID_PARAMETER;    // file name
982*ec779b8eSAndroid Build Coastguard Worker     if (name.isEmpty()) {
983*ec779b8eSAndroid Build Coastguard Worker         ALOGE("empty name");
984*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
985*ec779b8eSAndroid Build Coastguard Worker     }
986*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getString(created)) return MTP_RESPONSE_INVALID_PARAMETER;      // date created
987*ec779b8eSAndroid Build Coastguard Worker     if (!mData.getString(modified)) return MTP_RESPONSE_INVALID_PARAMETER;     // date modified
988*ec779b8eSAndroid Build Coastguard Worker     // keywords follow
989*ec779b8eSAndroid Build Coastguard Worker 
990*ec779b8eSAndroid Build Coastguard Worker     int type = storage->getType();
991*ec779b8eSAndroid Build Coastguard Worker     if (type == MTP_STORAGE_REMOVABLE_RAM) {
992*ec779b8eSAndroid Build Coastguard Worker         std::string str = android::base::Trim(name);
993*ec779b8eSAndroid Build Coastguard Worker         name.set(str.c_str());
994*ec779b8eSAndroid Build Coastguard Worker     }
995*ec779b8eSAndroid Build Coastguard Worker     ALOGV("name: %s format: 0x%04X (%s)\n", (const char*)name, format,
996*ec779b8eSAndroid Build Coastguard Worker           MtpDebug::getFormatCodeName(format));
997*ec779b8eSAndroid Build Coastguard Worker     time_t modifiedTime;
998*ec779b8eSAndroid Build Coastguard Worker     if (!parseDateTime(modified, modifiedTime))
999*ec779b8eSAndroid Build Coastguard Worker         modifiedTime = 0;
1000*ec779b8eSAndroid Build Coastguard Worker 
1001*ec779b8eSAndroid Build Coastguard Worker     if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0) ||
1002*ec779b8eSAndroid Build Coastguard Worker         (strchr(name, '/') != NULL)) {
1003*ec779b8eSAndroid Build Coastguard Worker         char errMsg[80];
1004*ec779b8eSAndroid Build Coastguard Worker 
1005*ec779b8eSAndroid Build Coastguard Worker         snprintf(errMsg, sizeof(errMsg), "Invalid name: %s", (const char *) name);
1006*ec779b8eSAndroid Build Coastguard Worker         ALOGE("%s (b/130656917)", errMsg);
1007*ec779b8eSAndroid Build Coastguard Worker         android_errorWriteWithInfoLog(SN_EVENT_LOG_ID, "130656917", -1, errMsg,
1008*ec779b8eSAndroid Build Coastguard Worker                                       strlen(errMsg));
1009*ec779b8eSAndroid Build Coastguard Worker 
1010*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1011*ec779b8eSAndroid Build Coastguard Worker     }
1012*ec779b8eSAndroid Build Coastguard Worker     if (path[path.size() - 1] != '/')
1013*ec779b8eSAndroid Build Coastguard Worker         path.append("/");
1014*ec779b8eSAndroid Build Coastguard Worker     path.append(name);
1015*ec779b8eSAndroid Build Coastguard Worker 
1016*ec779b8eSAndroid Build Coastguard Worker     // check space first
1017*ec779b8eSAndroid Build Coastguard Worker     if (mSendObjectFileSize > storage->getFreeSpace())
1018*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_STORAGE_FULL;
1019*ec779b8eSAndroid Build Coastguard Worker     uint64_t maxFileSize = storage->getMaxFileSize();
1020*ec779b8eSAndroid Build Coastguard Worker     // check storage max file size
1021*ec779b8eSAndroid Build Coastguard Worker     if (maxFileSize != 0) {
1022*ec779b8eSAndroid Build Coastguard Worker         // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
1023*ec779b8eSAndroid Build Coastguard Worker         // is >= 0xFFFFFFFF
1024*ec779b8eSAndroid Build Coastguard Worker         if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
1025*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_OBJECT_TOO_LARGE;
1026*ec779b8eSAndroid Build Coastguard Worker     }
1027*ec779b8eSAndroid Build Coastguard Worker 
1028*ec779b8eSAndroid Build Coastguard Worker     ALOGV("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
1029*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, format,
1030*ec779b8eSAndroid Build Coastguard Worker             parent, storageID);
1031*ec779b8eSAndroid Build Coastguard Worker     ALOGD("handle: %d, parent: %d, storageID: %08X", handle, parent, storageID);
1032*ec779b8eSAndroid Build Coastguard Worker     if (handle == kInvalidObjectHandle) {
1033*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1034*ec779b8eSAndroid Build Coastguard Worker     }
1035*ec779b8eSAndroid Build Coastguard Worker 
1036*ec779b8eSAndroid Build Coastguard Worker     if (format == MTP_FORMAT_ASSOCIATION) {
1037*ec779b8eSAndroid Build Coastguard Worker         int ret = makeFolder((const char *)path);
1038*ec779b8eSAndroid Build Coastguard Worker         if (ret)
1039*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_GENERAL_ERROR;
1040*ec779b8eSAndroid Build Coastguard Worker 
1041*ec779b8eSAndroid Build Coastguard Worker         // SendObject does not get sent for directories, so call endSendObject here instead
1042*ec779b8eSAndroid Build Coastguard Worker         mDatabase->endSendObject(handle, MTP_RESPONSE_OK);
1043*ec779b8eSAndroid Build Coastguard Worker     }
1044*ec779b8eSAndroid Build Coastguard Worker     mSendObjectFilePath = path;
1045*ec779b8eSAndroid Build Coastguard Worker     // save the handle for the SendObject call, which should follow
1046*ec779b8eSAndroid Build Coastguard Worker     mSendObjectHandle = handle;
1047*ec779b8eSAndroid Build Coastguard Worker     mSendObjectFormat = format;
1048*ec779b8eSAndroid Build Coastguard Worker     mSendObjectModifiedTime = modifiedTime;
1049*ec779b8eSAndroid Build Coastguard Worker 
1050*ec779b8eSAndroid Build Coastguard Worker     mResponse.setParameter(1, storageID);
1051*ec779b8eSAndroid Build Coastguard Worker     mResponse.setParameter(2, parent);
1052*ec779b8eSAndroid Build Coastguard Worker     mResponse.setParameter(3, handle);
1053*ec779b8eSAndroid Build Coastguard Worker 
1054*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
1055*ec779b8eSAndroid Build Coastguard Worker }
1056*ec779b8eSAndroid Build Coastguard Worker 
doMoveObject()1057*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doMoveObject() {
1058*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
1059*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1060*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 3)
1061*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1062*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle objectHandle = mRequest.getParameter(1);
1063*ec779b8eSAndroid Build Coastguard Worker     MtpStorageID storageID = mRequest.getParameter(2);
1064*ec779b8eSAndroid Build Coastguard Worker     MtpStorage* storage = getStorage(storageID);
1065*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle parent = mRequest.getParameter(3);
1066*ec779b8eSAndroid Build Coastguard Worker     if (!storage)
1067*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_STORAGE_ID;
1068*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer path;
1069*ec779b8eSAndroid Build Coastguard Worker     MtpResponseCode result;
1070*ec779b8eSAndroid Build Coastguard Worker 
1071*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer fromPath;
1072*ec779b8eSAndroid Build Coastguard Worker     int64_t fileLength;
1073*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format;
1074*ec779b8eSAndroid Build Coastguard Worker     MtpObjectInfo info(objectHandle);
1075*ec779b8eSAndroid Build Coastguard Worker     result = mDatabase->getObjectInfo(objectHandle, info);
1076*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
1077*ec779b8eSAndroid Build Coastguard Worker         return result;
1078*ec779b8eSAndroid Build Coastguard Worker     result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format);
1079*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
1080*ec779b8eSAndroid Build Coastguard Worker         return result;
1081*ec779b8eSAndroid Build Coastguard Worker 
1082*ec779b8eSAndroid Build Coastguard Worker     // special case the root
1083*ec779b8eSAndroid Build Coastguard Worker     if (parent == 0) {
1084*ec779b8eSAndroid Build Coastguard Worker         path.set(storage->getPath());
1085*ec779b8eSAndroid Build Coastguard Worker     } else {
1086*ec779b8eSAndroid Build Coastguard Worker         int64_t parentLength;
1087*ec779b8eSAndroid Build Coastguard Worker         MtpObjectFormat parentFormat;
1088*ec779b8eSAndroid Build Coastguard Worker         result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat);
1089*ec779b8eSAndroid Build Coastguard Worker         if (result != MTP_RESPONSE_OK)
1090*ec779b8eSAndroid Build Coastguard Worker             return result;
1091*ec779b8eSAndroid Build Coastguard Worker         if (parentFormat != MTP_FORMAT_ASSOCIATION)
1092*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_INVALID_PARENT_OBJECT;
1093*ec779b8eSAndroid Build Coastguard Worker     }
1094*ec779b8eSAndroid Build Coastguard Worker 
1095*ec779b8eSAndroid Build Coastguard Worker     if (path[path.size() - 1] != '/')
1096*ec779b8eSAndroid Build Coastguard Worker         path.append("/");
1097*ec779b8eSAndroid Build Coastguard Worker     path.append(info.mName);
1098*ec779b8eSAndroid Build Coastguard Worker 
1099*ec779b8eSAndroid Build Coastguard Worker     result = mDatabase->beginMoveObject(objectHandle, parent, storageID);
1100*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
1101*ec779b8eSAndroid Build Coastguard Worker         return result;
1102*ec779b8eSAndroid Build Coastguard Worker 
1103*ec779b8eSAndroid Build Coastguard Worker     if (info.mStorageID == storageID) {
1104*ec779b8eSAndroid Build Coastguard Worker         ALOGV("Moving file from %s to %s", (const char*)fromPath, (const char*)path);
1105*ec779b8eSAndroid Build Coastguard Worker         if (renameTo(fromPath, path)) {
1106*ec779b8eSAndroid Build Coastguard Worker             PLOG(ERROR) << "rename() failed from " << fromPath << " to " << path;
1107*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_GENERAL_ERROR;
1108*ec779b8eSAndroid Build Coastguard Worker         }
1109*ec779b8eSAndroid Build Coastguard Worker     } else {
1110*ec779b8eSAndroid Build Coastguard Worker         ALOGV("Moving across storages from %s to %s", (const char*)fromPath, (const char*)path);
1111*ec779b8eSAndroid Build Coastguard Worker         if (format == MTP_FORMAT_ASSOCIATION) {
1112*ec779b8eSAndroid Build Coastguard Worker             int ret = makeFolder((const char *)path);
1113*ec779b8eSAndroid Build Coastguard Worker             ret += copyRecursive(fromPath, path);
1114*ec779b8eSAndroid Build Coastguard Worker             if (ret) {
1115*ec779b8eSAndroid Build Coastguard Worker                 result = MTP_RESPONSE_GENERAL_ERROR;
1116*ec779b8eSAndroid Build Coastguard Worker             } else {
1117*ec779b8eSAndroid Build Coastguard Worker                 deletePath(fromPath);
1118*ec779b8eSAndroid Build Coastguard Worker             }
1119*ec779b8eSAndroid Build Coastguard Worker         } else {
1120*ec779b8eSAndroid Build Coastguard Worker             if (copyFile(fromPath, path)) {
1121*ec779b8eSAndroid Build Coastguard Worker                 result = MTP_RESPONSE_GENERAL_ERROR;
1122*ec779b8eSAndroid Build Coastguard Worker             } else {
1123*ec779b8eSAndroid Build Coastguard Worker                 deletePath(fromPath);
1124*ec779b8eSAndroid Build Coastguard Worker             }
1125*ec779b8eSAndroid Build Coastguard Worker         }
1126*ec779b8eSAndroid Build Coastguard Worker     }
1127*ec779b8eSAndroid Build Coastguard Worker 
1128*ec779b8eSAndroid Build Coastguard Worker     // If the move failed, undo the database change
1129*ec779b8eSAndroid Build Coastguard Worker     mDatabase->endMoveObject(info.mParent, parent, info.mStorageID, storageID, objectHandle,
1130*ec779b8eSAndroid Build Coastguard Worker             result == MTP_RESPONSE_OK);
1131*ec779b8eSAndroid Build Coastguard Worker 
1132*ec779b8eSAndroid Build Coastguard Worker     return result;
1133*ec779b8eSAndroid Build Coastguard Worker }
1134*ec779b8eSAndroid Build Coastguard Worker 
doCopyObject()1135*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doCopyObject() {
1136*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
1137*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1138*ec779b8eSAndroid Build Coastguard Worker     MtpResponseCode result = MTP_RESPONSE_OK;
1139*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 3)
1140*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1141*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle objectHandle = mRequest.getParameter(1);
1142*ec779b8eSAndroid Build Coastguard Worker     MtpStorageID storageID = mRequest.getParameter(2);
1143*ec779b8eSAndroid Build Coastguard Worker     MtpStorage* storage = getStorage(storageID);
1144*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle parent = mRequest.getParameter(3);
1145*ec779b8eSAndroid Build Coastguard Worker     if (!storage)
1146*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_STORAGE_ID;
1147*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer path;
1148*ec779b8eSAndroid Build Coastguard Worker 
1149*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer fromPath;
1150*ec779b8eSAndroid Build Coastguard Worker     int64_t fileLength;
1151*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format;
1152*ec779b8eSAndroid Build Coastguard Worker     MtpObjectInfo info(objectHandle);
1153*ec779b8eSAndroid Build Coastguard Worker     result = mDatabase->getObjectInfo(objectHandle, info);
1154*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
1155*ec779b8eSAndroid Build Coastguard Worker         return result;
1156*ec779b8eSAndroid Build Coastguard Worker     result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format);
1157*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
1158*ec779b8eSAndroid Build Coastguard Worker         return result;
1159*ec779b8eSAndroid Build Coastguard Worker 
1160*ec779b8eSAndroid Build Coastguard Worker     // special case the root
1161*ec779b8eSAndroid Build Coastguard Worker     if (parent == 0) {
1162*ec779b8eSAndroid Build Coastguard Worker         path.set(storage->getPath());
1163*ec779b8eSAndroid Build Coastguard Worker     } else {
1164*ec779b8eSAndroid Build Coastguard Worker         int64_t parentLength;
1165*ec779b8eSAndroid Build Coastguard Worker         MtpObjectFormat parentFormat;
1166*ec779b8eSAndroid Build Coastguard Worker         result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat);
1167*ec779b8eSAndroid Build Coastguard Worker         if (result != MTP_RESPONSE_OK)
1168*ec779b8eSAndroid Build Coastguard Worker             return result;
1169*ec779b8eSAndroid Build Coastguard Worker         if (parentFormat != MTP_FORMAT_ASSOCIATION)
1170*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_INVALID_PARENT_OBJECT;
1171*ec779b8eSAndroid Build Coastguard Worker     }
1172*ec779b8eSAndroid Build Coastguard Worker 
1173*ec779b8eSAndroid Build Coastguard Worker     // check space first
1174*ec779b8eSAndroid Build Coastguard Worker     if ((uint64_t) fileLength > storage->getFreeSpace())
1175*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_STORAGE_FULL;
1176*ec779b8eSAndroid Build Coastguard Worker 
1177*ec779b8eSAndroid Build Coastguard Worker     if (path[path.size() - 1] != '/')
1178*ec779b8eSAndroid Build Coastguard Worker         path.append("/");
1179*ec779b8eSAndroid Build Coastguard Worker     path.append(info.mName);
1180*ec779b8eSAndroid Build Coastguard Worker 
1181*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mDatabase->beginCopyObject(objectHandle, parent, storageID);
1182*ec779b8eSAndroid Build Coastguard Worker     if (handle == kInvalidObjectHandle) {
1183*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1184*ec779b8eSAndroid Build Coastguard Worker     }
1185*ec779b8eSAndroid Build Coastguard Worker 
1186*ec779b8eSAndroid Build Coastguard Worker     ALOGV("Copying file from %s to %s", (const char*)fromPath, (const char*)path);
1187*ec779b8eSAndroid Build Coastguard Worker     if (format == MTP_FORMAT_ASSOCIATION) {
1188*ec779b8eSAndroid Build Coastguard Worker         int ret = makeFolder((const char *)path);
1189*ec779b8eSAndroid Build Coastguard Worker         ret += copyRecursive(fromPath, path);
1190*ec779b8eSAndroid Build Coastguard Worker         if (ret) {
1191*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_GENERAL_ERROR;
1192*ec779b8eSAndroid Build Coastguard Worker         }
1193*ec779b8eSAndroid Build Coastguard Worker     } else {
1194*ec779b8eSAndroid Build Coastguard Worker         if (copyFile(fromPath, path)) {
1195*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_GENERAL_ERROR;
1196*ec779b8eSAndroid Build Coastguard Worker         }
1197*ec779b8eSAndroid Build Coastguard Worker     }
1198*ec779b8eSAndroid Build Coastguard Worker 
1199*ec779b8eSAndroid Build Coastguard Worker     mDatabase->endCopyObject(handle, result);
1200*ec779b8eSAndroid Build Coastguard Worker     mResponse.setParameter(1, handle);
1201*ec779b8eSAndroid Build Coastguard Worker     return result;
1202*ec779b8eSAndroid Build Coastguard Worker }
1203*ec779b8eSAndroid Build Coastguard Worker 
doSendObject()1204*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doSendObject() {
1205*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
1206*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1207*ec779b8eSAndroid Build Coastguard Worker     MtpResponseCode result = MTP_RESPONSE_OK;
1208*ec779b8eSAndroid Build Coastguard Worker     mode_t mask;
1209*ec779b8eSAndroid Build Coastguard Worker     int ret, initialData;
1210*ec779b8eSAndroid Build Coastguard Worker     bool isCanceled = false;
1211*ec779b8eSAndroid Build Coastguard Worker     struct stat sstat = {};
1212*ec779b8eSAndroid Build Coastguard Worker 
1213*ec779b8eSAndroid Build Coastguard Worker     auto start = std::chrono::steady_clock::now();
1214*ec779b8eSAndroid Build Coastguard Worker 
1215*ec779b8eSAndroid Build Coastguard Worker     if (mSendObjectHandle == kInvalidObjectHandle) {
1216*ec779b8eSAndroid Build Coastguard Worker         ALOGE("Expected SendObjectInfo before SendObject");
1217*ec779b8eSAndroid Build Coastguard Worker         result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
1218*ec779b8eSAndroid Build Coastguard Worker         goto done;
1219*ec779b8eSAndroid Build Coastguard Worker     }
1220*ec779b8eSAndroid Build Coastguard Worker 
1221*ec779b8eSAndroid Build Coastguard Worker     // read the header, and possibly some data
1222*ec779b8eSAndroid Build Coastguard Worker     ret = mData.read(mHandle);
1223*ec779b8eSAndroid Build Coastguard Worker     if (ret < MTP_CONTAINER_HEADER_SIZE) {
1224*ec779b8eSAndroid Build Coastguard Worker         result = MTP_RESPONSE_GENERAL_ERROR;
1225*ec779b8eSAndroid Build Coastguard Worker         goto done;
1226*ec779b8eSAndroid Build Coastguard Worker     }
1227*ec779b8eSAndroid Build Coastguard Worker     initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1228*ec779b8eSAndroid Build Coastguard Worker 
1229*ec779b8eSAndroid Build Coastguard Worker     if (mSendObjectFormat == MTP_FORMAT_ASSOCIATION) {
1230*ec779b8eSAndroid Build Coastguard Worker         if (initialData != 0)
1231*ec779b8eSAndroid Build Coastguard Worker             ALOGE("Expected folder size to be 0!");
1232*ec779b8eSAndroid Build Coastguard Worker         mSendObjectHandle = kInvalidObjectHandle;
1233*ec779b8eSAndroid Build Coastguard Worker         mSendObjectFormat = 0;
1234*ec779b8eSAndroid Build Coastguard Worker         mSendObjectModifiedTime = 0;
1235*ec779b8eSAndroid Build Coastguard Worker         return result;
1236*ec779b8eSAndroid Build Coastguard Worker     }
1237*ec779b8eSAndroid Build Coastguard Worker 
1238*ec779b8eSAndroid Build Coastguard Worker     mtp_file_range  mfr;
1239*ec779b8eSAndroid Build Coastguard Worker     mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
1240*ec779b8eSAndroid Build Coastguard Worker     if (mfr.fd < 0) {
1241*ec779b8eSAndroid Build Coastguard Worker         result = MTP_RESPONSE_GENERAL_ERROR;
1242*ec779b8eSAndroid Build Coastguard Worker         goto done;
1243*ec779b8eSAndroid Build Coastguard Worker     }
1244*ec779b8eSAndroid Build Coastguard Worker     fchown(mfr.fd, getuid(), FILE_GROUP);
1245*ec779b8eSAndroid Build Coastguard Worker     // set permissions
1246*ec779b8eSAndroid Build Coastguard Worker     mask = umask(0);
1247*ec779b8eSAndroid Build Coastguard Worker     fchmod(mfr.fd, FILE_PERM);
1248*ec779b8eSAndroid Build Coastguard Worker     umask(mask);
1249*ec779b8eSAndroid Build Coastguard Worker 
1250*ec779b8eSAndroid Build Coastguard Worker     if (initialData > 0) {
1251*ec779b8eSAndroid Build Coastguard Worker         ret = write(mfr.fd, mData.getData(), initialData);
1252*ec779b8eSAndroid Build Coastguard Worker     }
1253*ec779b8eSAndroid Build Coastguard Worker 
1254*ec779b8eSAndroid Build Coastguard Worker     if (ret < 0) {
1255*ec779b8eSAndroid Build Coastguard Worker         ALOGE("failed to write initial data");
1256*ec779b8eSAndroid Build Coastguard Worker         result = MTP_RESPONSE_GENERAL_ERROR;
1257*ec779b8eSAndroid Build Coastguard Worker     } else {
1258*ec779b8eSAndroid Build Coastguard Worker         mfr.offset = initialData;
1259*ec779b8eSAndroid Build Coastguard Worker         if (mSendObjectFileSize == 0xFFFFFFFF) {
1260*ec779b8eSAndroid Build Coastguard Worker             // tell driver to read until it receives a short packet
1261*ec779b8eSAndroid Build Coastguard Worker             mfr.length = 0xFFFFFFFF;
1262*ec779b8eSAndroid Build Coastguard Worker         } else {
1263*ec779b8eSAndroid Build Coastguard Worker             mfr.length = mSendObjectFileSize - initialData;
1264*ec779b8eSAndroid Build Coastguard Worker         }
1265*ec779b8eSAndroid Build Coastguard Worker 
1266*ec779b8eSAndroid Build Coastguard Worker         mfr.command = 0;
1267*ec779b8eSAndroid Build Coastguard Worker         mfr.transaction_id = 0;
1268*ec779b8eSAndroid Build Coastguard Worker 
1269*ec779b8eSAndroid Build Coastguard Worker         // transfer the file
1270*ec779b8eSAndroid Build Coastguard Worker         ret = mHandle->receiveFile(mfr, mfr.length == 0 &&
1271*ec779b8eSAndroid Build Coastguard Worker                 initialData == MTP_BUFFER_SIZE - MTP_CONTAINER_HEADER_SIZE);
1272*ec779b8eSAndroid Build Coastguard Worker         if ((ret < 0) && (errno == ECANCELED)) {
1273*ec779b8eSAndroid Build Coastguard Worker             isCanceled = true;
1274*ec779b8eSAndroid Build Coastguard Worker         }
1275*ec779b8eSAndroid Build Coastguard Worker     }
1276*ec779b8eSAndroid Build Coastguard Worker 
1277*ec779b8eSAndroid Build Coastguard Worker     if (mSendObjectModifiedTime) {
1278*ec779b8eSAndroid Build Coastguard Worker         struct timespec newTime[2];
1279*ec779b8eSAndroid Build Coastguard Worker         newTime[0].tv_nsec = UTIME_NOW;
1280*ec779b8eSAndroid Build Coastguard Worker         newTime[1].tv_sec = mSendObjectModifiedTime;
1281*ec779b8eSAndroid Build Coastguard Worker         newTime[1].tv_nsec = 0;
1282*ec779b8eSAndroid Build Coastguard Worker         if (futimens(mfr.fd, newTime) < 0) {
1283*ec779b8eSAndroid Build Coastguard Worker             ALOGW("changing modified time failed, %s", strerror(errno));
1284*ec779b8eSAndroid Build Coastguard Worker         }
1285*ec779b8eSAndroid Build Coastguard Worker     }
1286*ec779b8eSAndroid Build Coastguard Worker 
1287*ec779b8eSAndroid Build Coastguard Worker     fstat(mfr.fd, &sstat);
1288*ec779b8eSAndroid Build Coastguard Worker     closeObjFd(mfr.fd, mSendObjectFilePath);
1289*ec779b8eSAndroid Build Coastguard Worker 
1290*ec779b8eSAndroid Build Coastguard Worker     if (ret < 0) {
1291*ec779b8eSAndroid Build Coastguard Worker         ALOGE("Mtp receive file got error %s", strerror(errno));
1292*ec779b8eSAndroid Build Coastguard Worker         unlink(mSendObjectFilePath);
1293*ec779b8eSAndroid Build Coastguard Worker         if (isCanceled)
1294*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_TRANSACTION_CANCELLED;
1295*ec779b8eSAndroid Build Coastguard Worker         else
1296*ec779b8eSAndroid Build Coastguard Worker             result = MTP_RESPONSE_GENERAL_ERROR;
1297*ec779b8eSAndroid Build Coastguard Worker     }
1298*ec779b8eSAndroid Build Coastguard Worker 
1299*ec779b8eSAndroid Build Coastguard Worker done:
1300*ec779b8eSAndroid Build Coastguard Worker     // reset so we don't attempt to send the data back
1301*ec779b8eSAndroid Build Coastguard Worker     mData.reset();
1302*ec779b8eSAndroid Build Coastguard Worker 
1303*ec779b8eSAndroid Build Coastguard Worker     mDatabase->endSendObject(mSendObjectHandle, result == MTP_RESPONSE_OK);
1304*ec779b8eSAndroid Build Coastguard Worker     mSendObjectHandle = kInvalidObjectHandle;
1305*ec779b8eSAndroid Build Coastguard Worker     mSendObjectFormat = 0;
1306*ec779b8eSAndroid Build Coastguard Worker     mSendObjectModifiedTime = 0;
1307*ec779b8eSAndroid Build Coastguard Worker 
1308*ec779b8eSAndroid Build Coastguard Worker     auto end = std::chrono::steady_clock::now();
1309*ec779b8eSAndroid Build Coastguard Worker     std::chrono::duration<double> diff = end - start;
1310*ec779b8eSAndroid Build Coastguard Worker     uint64_t finalsize = sstat.st_size;
1311*ec779b8eSAndroid Build Coastguard Worker     ALOGV("Got a file over MTP. Time: %fs, Size: %" PRIu64 ", Rate: %f bytes/s",
1312*ec779b8eSAndroid Build Coastguard Worker             diff.count(), finalsize, ((double) finalsize) / diff.count());
1313*ec779b8eSAndroid Build Coastguard Worker     return result;
1314*ec779b8eSAndroid Build Coastguard Worker }
1315*ec779b8eSAndroid Build Coastguard Worker 
doDeleteObject()1316*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doDeleteObject() {
1317*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
1318*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1319*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
1320*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1321*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
1322*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format;
1323*ec779b8eSAndroid Build Coastguard Worker     // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1324*ec779b8eSAndroid Build Coastguard Worker     // FIXME - implement deleting objects by format
1325*ec779b8eSAndroid Build Coastguard Worker 
1326*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer filePath;
1327*ec779b8eSAndroid Build Coastguard Worker     int64_t fileLength;
1328*ec779b8eSAndroid Build Coastguard Worker     int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
1329*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
1330*ec779b8eSAndroid Build Coastguard Worker         return result;
1331*ec779b8eSAndroid Build Coastguard Worker 
1332*ec779b8eSAndroid Build Coastguard Worker     // Don't delete the actual files unless the database deletion is allowed
1333*ec779b8eSAndroid Build Coastguard Worker     result = mDatabase->beginDeleteObject(handle);
1334*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
1335*ec779b8eSAndroid Build Coastguard Worker         return result;
1336*ec779b8eSAndroid Build Coastguard Worker 
1337*ec779b8eSAndroid Build Coastguard Worker     bool success = deletePath((const char *)filePath);
1338*ec779b8eSAndroid Build Coastguard Worker 
1339*ec779b8eSAndroid Build Coastguard Worker     mDatabase->endDeleteObject(handle, success);
1340*ec779b8eSAndroid Build Coastguard Worker     return success ? result : MTP_RESPONSE_PARTIAL_DELETION;
1341*ec779b8eSAndroid Build Coastguard Worker }
1342*ec779b8eSAndroid Build Coastguard Worker 
doGetObjectPropDesc()1343*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetObjectPropDesc() {
1344*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 2)
1345*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1346*ec779b8eSAndroid Build Coastguard Worker     MtpObjectProperty propCode = mRequest.getParameter(1);
1347*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format = mRequest.getParameter(2);
1348*ec779b8eSAndroid Build Coastguard Worker     ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
1349*ec779b8eSAndroid Build Coastguard Worker                                         MtpDebug::getFormatCodeName(format));
1350*ec779b8eSAndroid Build Coastguard Worker     MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
1351*ec779b8eSAndroid Build Coastguard Worker     if (!property)
1352*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
1353*ec779b8eSAndroid Build Coastguard Worker     property->write(mData);
1354*ec779b8eSAndroid Build Coastguard Worker     delete property;
1355*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
1356*ec779b8eSAndroid Build Coastguard Worker }
1357*ec779b8eSAndroid Build Coastguard Worker 
doGetDevicePropDesc()1358*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doGetDevicePropDesc() {
1359*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
1360*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1361*ec779b8eSAndroid Build Coastguard Worker     MtpDeviceProperty propCode = mRequest.getParameter(1);
1362*ec779b8eSAndroid Build Coastguard Worker     ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
1363*ec779b8eSAndroid Build Coastguard Worker     MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1364*ec779b8eSAndroid Build Coastguard Worker     if (!property)
1365*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1366*ec779b8eSAndroid Build Coastguard Worker     property->write(mData);
1367*ec779b8eSAndroid Build Coastguard Worker     delete property;
1368*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
1369*ec779b8eSAndroid Build Coastguard Worker }
1370*ec779b8eSAndroid Build Coastguard Worker 
doSendPartialObject()1371*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doSendPartialObject() {
1372*ec779b8eSAndroid Build Coastguard Worker     if (!hasStorage())
1373*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1374*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 4)
1375*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1376*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
1377*ec779b8eSAndroid Build Coastguard Worker     uint64_t offset = mRequest.getParameter(2);
1378*ec779b8eSAndroid Build Coastguard Worker     uint64_t offset2 = mRequest.getParameter(3);
1379*ec779b8eSAndroid Build Coastguard Worker     offset = offset | (offset2 << 32);
1380*ec779b8eSAndroid Build Coastguard Worker     uint32_t length = mRequest.getParameter(4);
1381*ec779b8eSAndroid Build Coastguard Worker 
1382*ec779b8eSAndroid Build Coastguard Worker     ObjectEdit* edit = getEditObject(handle);
1383*ec779b8eSAndroid Build Coastguard Worker     if (!edit) {
1384*ec779b8eSAndroid Build Coastguard Worker         ALOGE("object not open for edit in doSendPartialObject");
1385*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1386*ec779b8eSAndroid Build Coastguard Worker     }
1387*ec779b8eSAndroid Build Coastguard Worker 
1388*ec779b8eSAndroid Build Coastguard Worker     // can't start writing past the end of the file
1389*ec779b8eSAndroid Build Coastguard Worker     if (offset > edit->mSize) {
1390*ec779b8eSAndroid Build Coastguard Worker         ALOGD("writing past end of object, offset: %" PRIu64 ", edit->mSize: %" PRIu64,
1391*ec779b8eSAndroid Build Coastguard Worker             offset, edit->mSize);
1392*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1393*ec779b8eSAndroid Build Coastguard Worker     }
1394*ec779b8eSAndroid Build Coastguard Worker 
1395*ec779b8eSAndroid Build Coastguard Worker     const char* filePath = (const char *)edit->mPath;
1396*ec779b8eSAndroid Build Coastguard Worker     ALOGV("receiving partial %s %" PRIu64 " %" PRIu32, filePath, offset, length);
1397*ec779b8eSAndroid Build Coastguard Worker 
1398*ec779b8eSAndroid Build Coastguard Worker     // read the header, and possibly some data
1399*ec779b8eSAndroid Build Coastguard Worker     int ret = mData.read(mHandle);
1400*ec779b8eSAndroid Build Coastguard Worker     if (ret < MTP_CONTAINER_HEADER_SIZE)
1401*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1402*ec779b8eSAndroid Build Coastguard Worker     int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1403*ec779b8eSAndroid Build Coastguard Worker 
1404*ec779b8eSAndroid Build Coastguard Worker     if (initialData > 0) {
1405*ec779b8eSAndroid Build Coastguard Worker         ret = pwrite(edit->mFD, mData.getData(), initialData, offset);
1406*ec779b8eSAndroid Build Coastguard Worker         offset += initialData;
1407*ec779b8eSAndroid Build Coastguard Worker         length -= initialData;
1408*ec779b8eSAndroid Build Coastguard Worker     }
1409*ec779b8eSAndroid Build Coastguard Worker 
1410*ec779b8eSAndroid Build Coastguard Worker     bool isCanceled = false;
1411*ec779b8eSAndroid Build Coastguard Worker     if (ret < 0) {
1412*ec779b8eSAndroid Build Coastguard Worker         ALOGE("failed to write initial data");
1413*ec779b8eSAndroid Build Coastguard Worker     } else {
1414*ec779b8eSAndroid Build Coastguard Worker         mtp_file_range  mfr;
1415*ec779b8eSAndroid Build Coastguard Worker         mfr.fd = edit->mFD;
1416*ec779b8eSAndroid Build Coastguard Worker         mfr.offset = offset;
1417*ec779b8eSAndroid Build Coastguard Worker         mfr.length = length;
1418*ec779b8eSAndroid Build Coastguard Worker         mfr.command = 0;
1419*ec779b8eSAndroid Build Coastguard Worker         mfr.transaction_id = 0;
1420*ec779b8eSAndroid Build Coastguard Worker 
1421*ec779b8eSAndroid Build Coastguard Worker         // transfer the file
1422*ec779b8eSAndroid Build Coastguard Worker         ret = mHandle->receiveFile(mfr, mfr.length == 0 &&
1423*ec779b8eSAndroid Build Coastguard Worker                 initialData == MTP_BUFFER_SIZE - MTP_CONTAINER_HEADER_SIZE);
1424*ec779b8eSAndroid Build Coastguard Worker         if ((ret < 0) && (errno == ECANCELED)) {
1425*ec779b8eSAndroid Build Coastguard Worker             isCanceled = true;
1426*ec779b8eSAndroid Build Coastguard Worker         }
1427*ec779b8eSAndroid Build Coastguard Worker     }
1428*ec779b8eSAndroid Build Coastguard Worker     if (ret < 0) {
1429*ec779b8eSAndroid Build Coastguard Worker         mResponse.setParameter(1, 0);
1430*ec779b8eSAndroid Build Coastguard Worker         if (isCanceled)
1431*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_TRANSACTION_CANCELLED;
1432*ec779b8eSAndroid Build Coastguard Worker         else
1433*ec779b8eSAndroid Build Coastguard Worker             return MTP_RESPONSE_GENERAL_ERROR;
1434*ec779b8eSAndroid Build Coastguard Worker     }
1435*ec779b8eSAndroid Build Coastguard Worker 
1436*ec779b8eSAndroid Build Coastguard Worker     // reset so we don't attempt to send this back
1437*ec779b8eSAndroid Build Coastguard Worker     mData.reset();
1438*ec779b8eSAndroid Build Coastguard Worker     mResponse.setParameter(1, length);
1439*ec779b8eSAndroid Build Coastguard Worker     uint64_t end = offset + length;
1440*ec779b8eSAndroid Build Coastguard Worker     if (end > edit->mSize) {
1441*ec779b8eSAndroid Build Coastguard Worker         edit->mSize = end;
1442*ec779b8eSAndroid Build Coastguard Worker     }
1443*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
1444*ec779b8eSAndroid Build Coastguard Worker }
1445*ec779b8eSAndroid Build Coastguard Worker 
doTruncateObject()1446*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doTruncateObject() {
1447*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 3)
1448*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1449*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
1450*ec779b8eSAndroid Build Coastguard Worker     ObjectEdit* edit = getEditObject(handle);
1451*ec779b8eSAndroid Build Coastguard Worker     if (!edit) {
1452*ec779b8eSAndroid Build Coastguard Worker         ALOGE("object not open for edit in doTruncateObject");
1453*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1454*ec779b8eSAndroid Build Coastguard Worker     }
1455*ec779b8eSAndroid Build Coastguard Worker 
1456*ec779b8eSAndroid Build Coastguard Worker     uint64_t offset = mRequest.getParameter(2);
1457*ec779b8eSAndroid Build Coastguard Worker     uint64_t offset2 = mRequest.getParameter(3);
1458*ec779b8eSAndroid Build Coastguard Worker     offset |= (offset2 << 32);
1459*ec779b8eSAndroid Build Coastguard Worker     if (ftruncate(edit->mFD, offset) != 0) {
1460*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1461*ec779b8eSAndroid Build Coastguard Worker     } else {
1462*ec779b8eSAndroid Build Coastguard Worker         edit->mSize = offset;
1463*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_OK;
1464*ec779b8eSAndroid Build Coastguard Worker     }
1465*ec779b8eSAndroid Build Coastguard Worker }
1466*ec779b8eSAndroid Build Coastguard Worker 
doBeginEditObject()1467*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doBeginEditObject() {
1468*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
1469*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1470*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
1471*ec779b8eSAndroid Build Coastguard Worker     if (getEditObject(handle)) {
1472*ec779b8eSAndroid Build Coastguard Worker         ALOGE("object already open for edit in doBeginEditObject");
1473*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1474*ec779b8eSAndroid Build Coastguard Worker     }
1475*ec779b8eSAndroid Build Coastguard Worker 
1476*ec779b8eSAndroid Build Coastguard Worker     MtpStringBuffer path;
1477*ec779b8eSAndroid Build Coastguard Worker     int64_t fileLength;
1478*ec779b8eSAndroid Build Coastguard Worker     MtpObjectFormat format;
1479*ec779b8eSAndroid Build Coastguard Worker     int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1480*ec779b8eSAndroid Build Coastguard Worker     if (result != MTP_RESPONSE_OK)
1481*ec779b8eSAndroid Build Coastguard Worker         return result;
1482*ec779b8eSAndroid Build Coastguard Worker 
1483*ec779b8eSAndroid Build Coastguard Worker     int fd = open((const char *)path, O_RDWR | O_EXCL);
1484*ec779b8eSAndroid Build Coastguard Worker     if (fd < 0) {
1485*ec779b8eSAndroid Build Coastguard Worker         ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1486*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1487*ec779b8eSAndroid Build Coastguard Worker     }
1488*ec779b8eSAndroid Build Coastguard Worker 
1489*ec779b8eSAndroid Build Coastguard Worker     addEditObject(handle, path, fileLength, format, fd);
1490*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
1491*ec779b8eSAndroid Build Coastguard Worker }
1492*ec779b8eSAndroid Build Coastguard Worker 
doEndEditObject()1493*ec779b8eSAndroid Build Coastguard Worker MtpResponseCode MtpServer::doEndEditObject() {
1494*ec779b8eSAndroid Build Coastguard Worker     if (mRequest.getParameterCount() < 1)
1495*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_INVALID_PARAMETER;
1496*ec779b8eSAndroid Build Coastguard Worker     MtpObjectHandle handle = mRequest.getParameter(1);
1497*ec779b8eSAndroid Build Coastguard Worker     ObjectEdit* edit = getEditObject(handle);
1498*ec779b8eSAndroid Build Coastguard Worker     if (!edit) {
1499*ec779b8eSAndroid Build Coastguard Worker         ALOGE("object not open for edit in doEndEditObject");
1500*ec779b8eSAndroid Build Coastguard Worker         return MTP_RESPONSE_GENERAL_ERROR;
1501*ec779b8eSAndroid Build Coastguard Worker     }
1502*ec779b8eSAndroid Build Coastguard Worker 
1503*ec779b8eSAndroid Build Coastguard Worker     commitEdit(edit);
1504*ec779b8eSAndroid Build Coastguard Worker     removeEditObject(handle);
1505*ec779b8eSAndroid Build Coastguard Worker     return MTP_RESPONSE_OK;
1506*ec779b8eSAndroid Build Coastguard Worker }
1507*ec779b8eSAndroid Build Coastguard Worker 
1508*ec779b8eSAndroid Build Coastguard Worker }  // namespace android
1509