Taking a deep dive into MTP: Part 3

In the previous post I looked at connecting to an Android device over WebUSB and opening an MTP session.

Let's have a look at how to see what objects (directories, files etc.) are available on the device. Before we get started, let's define some of the operation and response codes that we'll need, as well as some possible container types:

const CODE = {
  OPEN_SESSION: { value: 0x1002, name: 'OpenSession' },
  CLOSE_SESSION: { value: 0x1003, name: 'CloseSession' },
  GET_OBJECT_HANDLES: { value: 0x1007, name: 'GetObjectHandles'},
  OK: { value: 0x2001, name: 'OK'},
  INVALID_PARAMETER: { value: 0x201D, name: 'Invalid parameter'},
  INVALID_OBJECTPROP_FORMAT: { value: 0xA802, name: 'Invalid_ObjectProp_Format'},
  GET_OBJECT_PROP_VALUE: { value: 0x9803, name: 'GetObjectPropValue' },
};

onst TYPE = [
  'undefined',
  'Command Block',
  'Data Block',
  'Response Block',
  'Event Block'
];

We also need to be able to parse container packets:

const parseContainerPacket = (bytes, length) => {
  const fields = {
    type : TYPE[bytes.getUint16(4, true)],
    code : getName(CODE, bytes.getUint16(6, true)),
    transactionID : bytes.getUint32(8, true),
    payload : [],
  };

  for (let i = 12; i < length; i += 4) {
    fields.payload.push(bytes.getUint32(i, true));
  }

  return fields;
};

The getName() helper function looks like this:

const getName = (list, idx) => {
  for (let i in list) {
    if (list[i].value === idx) {
      return list[i].name;
    }
  }
  return 'unknown';
};

To get the object handles, we do the following:

  const getObjectHandles = {
    type: 1, // command block
    code: CODE.GET_OBJECT_HANDLES.value,
    payload: [0xFFFFFFFF, 0, 0xFFFFFFFF], // get all
  };
  data = buildContainerPacket(getObjectHandles, 4);
  result = await device.transferOut(0x01, data);
  console.log('result:', result);
  let { payload } = await receiveData();

Now we have an array of object handles. We can use the object handle to retrieve the filename, using the “Object File Name” object property code 0xDC07:

const getFilename = {
    type: 1,
    code: CODE.GET_OBJECT_PROP_VALUE.value,
    payload: [0x2B, 0xDC07], // object handle and object property code
  };
  result = await device.transferOut(0x01, buildContainerPacket(getFilename));
  console.log('result:', result);
  let { payload } = await receiveData();

The payload is a 16-bit unicode string containing the file name. Nice!


I’m publishing this as part of 100 Days To Offload. You can join in yourself by visiting https://100daystooffload.com.

#100DaysToOffload #day51 #mtp