<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>day51 &amp;mdash; Gerrit Niezen</title>
    <link>https://gerritniezen.com/tag:day51</link>
    <description>Maker of open-source software and hardware.</description>
    <pubDate>Mon, 20 Apr 2026 02:12:20 +0000</pubDate>
    <image>
      <url>https://i.snap.as/aMPXpIot.png</url>
      <title>day51 &amp;mdash; Gerrit Niezen</title>
      <link>https://gerritniezen.com/tag:day51</link>
    </image>
    <item>
      <title>Taking a deep dive into MTP: Part 3</title>
      <link>https://gerritniezen.com/taking-a-deep-dive-into-mtp-part-3?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[In the previous post I looked at connecting to an Android device over WebUSB and opening an MTP session.&#xA;&#xA;Let&#39;s have a look at how to see what objects (directories, files etc.) are available on the device. Before we get started, let&#39;s define some of the operation and response codes that we&#39;ll need, as well as some possible container types:&#xA;&#xA;const CODE = {&#xA;  OPENSESSION: { value: 0x1002, name: &#39;OpenSession&#39; },&#xA;  CLOSESESSION: { value: 0x1003, name: &#39;CloseSession&#39; },&#xA;  GETOBJECTHANDLES: { value: 0x1007, name: &#39;GetObjectHandles&#39;},&#xA;  OK: { value: 0x2001, name: &#39;OK&#39;},&#xA;  INVALIDPARAMETER: { value: 0x201D, name: &#39;Invalid parameter&#39;},&#xA;  INVALIDOBJECTPROPFORMAT: { value: 0xA802, name: &#39;InvalidObjectPropFormat&#39;},&#xA;  GETOBJECTPROPVALUE: { value: 0x9803, name: &#39;GetObjectPropValue&#39; },&#xA;};&#xA;&#xA;onst TYPE = [&#xA;  &#39;undefined&#39;,&#xA;  &#39;Command Block&#39;,&#xA;  &#39;Data Block&#39;,&#xA;  &#39;Response Block&#39;,&#xA;  &#39;Event Block&#39;&#xA;];&#xA;&#xA;We also need to be able to parse container packets:&#xA;&#xA;const parseContainerPacket = (bytes, length) =  {&#xA;  const fields = {&#xA;    type : TYPE[bytes.getUint16(4, true)],&#xA;    code : getName(CODE, bytes.getUint16(6, true)),&#xA;    transactionID : bytes.getUint32(8, true),&#xA;    payload : [],&#xA;  };&#xA;&#xA;  for (let i = 12; i &lt; length; i += 4) {&#xA;    fields.payload.push(bytes.getUint32(i, true));&#xA;  }&#xA;&#xA;  return fields;&#xA;};&#xA;&#xA;The getName() helper function looks like this:&#xA;&#xA;const getName = (list, idx) =  {&#xA;  for (let i in list) {&#xA;    if (list[i].value === idx) {&#xA;      return list[i].name;&#xA;    }&#xA;  }&#xA;  return &#39;unknown&#39;;&#xA;};&#xA;&#xA;To get the object handles, we do the following:&#xA;&#xA;  const getObjectHandles = {&#xA;    type: 1, // command block&#xA;    code: CODE.GETOBJECTHANDLES.value,&#xA;    payload: [0xFFFFFFFF, 0, 0xFFFFFFFF], // get all&#xA;  };&#xA;  data = buildContainerPacket(getObjectHandles, 4);&#xA;  result = await device.transferOut(0x01, data);&#xA;  console.log(&#39;result:&#39;, result);&#xA;  let { payload } = await receiveData();&#xA;&#xA;Now we have an array of object handles. We can use the object handle to retrieve the filename, using the &#34;Object File Name&#34; object property code 0xDC07:&#xA;&#xA;const getFilename = {&#xA;    type: 1,&#xA;    code: CODE.GETOBJECTPROPVALUE.value,&#xA;    payload: [0x2B, 0xDC07], // object handle and object property code&#xA;  };&#xA;  result = await device.transferOut(0x01, buildContainerPacket(getFilename));&#xA;  console.log(&#39;result:&#39;, result);&#xA;  let { payload } = await receiveData();&#xA;&#xA;The payload is a 16-bit unicode string containing the file name. Nice!&#xA;&#xA;---&#xA;I’m publishing this as part of 100 Days To Offload. You can join in yourself by visiting https://100daystooffload.com.&#xA;&#xA;#100DaysToOffload #day51 #mtp&#xA;&#xA;iComment on this post/i&#xD;&#xA;div id=&#34;cusdisthread&#34;/div]]&gt;</description>
      <content:encoded><![CDATA[<p>In the <a href="https://gerritniezen.com/taking-a-deep-dive-into-mtp-part-2">previous post</a> I looked at connecting to an Android device over WebUSB and opening an MTP session.</p>

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

<pre><code class="language-js">const CODE = {
  OPEN_SESSION: { value: 0x1002, name: &#39;OpenSession&#39; },
  CLOSE_SESSION: { value: 0x1003, name: &#39;CloseSession&#39; },
  GET_OBJECT_HANDLES: { value: 0x1007, name: &#39;GetObjectHandles&#39;},
  OK: { value: 0x2001, name: &#39;OK&#39;},
  INVALID_PARAMETER: { value: 0x201D, name: &#39;Invalid parameter&#39;},
  INVALID_OBJECTPROP_FORMAT: { value: 0xA802, name: &#39;Invalid_ObjectProp_Format&#39;},
  GET_OBJECT_PROP_VALUE: { value: 0x9803, name: &#39;GetObjectPropValue&#39; },
};

onst TYPE = [
  &#39;undefined&#39;,
  &#39;Command Block&#39;,
  &#39;Data Block&#39;,
  &#39;Response Block&#39;,
  &#39;Event Block&#39;
];
</code></pre>

<p>We also need to be able to parse container packets:</p>

<pre><code class="language-js">const parseContainerPacket = (bytes, length) =&gt; {
  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 &lt; length; i += 4) {
    fields.payload.push(bytes.getUint32(i, true));
  }

  return fields;
};
</code></pre>

<p>The <code>getName()</code> helper function looks like this:</p>

<pre><code class="language-js">const getName = (list, idx) =&gt; {
  for (let i in list) {
    if (list[i].value === idx) {
      return list[i].name;
    }
  }
  return &#39;unknown&#39;;
};
</code></pre>

<p>To get the object handles, we do the following:</p>

<pre><code class="language-js">  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(&#39;result:&#39;, result);
  let { payload } = await receiveData();
</code></pre>

<p>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 <code>0xDC07</code>:</p>

<pre><code class="language-js">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(&#39;result:&#39;, result);
  let { payload } = await receiveData();
</code></pre>

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

<hr/>

<p>I’m publishing this as part of 100 Days To Offload. You can join in yourself by visiting <a href="https://100daystooffload.com">https://100daystooffload.com</a>.</p>

<p><a href="https://gerritniezen.com/tag:100DaysToOffload" class="hashtag"><span>#</span><span class="p-category">100DaysToOffload</span></a> <a href="https://gerritniezen.com/tag:day51" class="hashtag"><span>#</span><span class="p-category">day51</span></a> <a href="https://gerritniezen.com/tag:mtp" class="hashtag"><span>#</span><span class="p-category">mtp</span></a></p>

<p><i>Comment on this post</i>
<div id="cusdis_thread" id="cusdis_thread"></div></p>
]]></content:encoded>
      <guid>https://gerritniezen.com/taking-a-deep-dive-into-mtp-part-3</guid>
      <pubDate>Wed, 24 Jun 2020 15:51:10 +0000</pubDate>
    </item>
  </channel>
</rss>