<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Android gesture lock pattern recovery</title>
<script>
<![CDATA[

positions = {
    1:{x:0,y:0},
    2:{x:30,y:0},
    3:{x:60,y:0},
    4:{x:0,y:30},
    5:{x:30,y:30},
    6:{x:60,y:30},
    7:{x:0,y:60},
    8:{x:30,y:60},
    9:{x:60,y:60}
}

function update_svg(nrs) {
    for(var i = 1 ; i<=9 ; i++ ) {
        var b = document.getElementById("nr" + i);
        b.setAttribute('style', 'visibility: hidden;');
    }
    for(var i = 1 ; i<=nrs.length ; i++ ) {
        var b = document.getElementById("nr" + i);
        b.setAttribute('transform', "translate(" + positions[nrs[i-1]].x + "," + positions[nrs[i-1]].y + ")");
        b.setAttribute('style', 'visibility: visible;');
    }
}

function ajax_get_hash(hash){
  url = "gestures/" + hash.charAt(0) + "/" + hash.charAt(1) + "/" + hash.charAt(2) + "/" + hash.charAt(3);
  var request = new XMLHttpRequest();

  request.open("GET", url, true);

  request.onreadystatechange = function() {
    var done = 4, ok = 200;

    if (request.readyState == done && request.status == ok) {
      if (request.responseText) {
        show_pattern(hash.substring(0,4), request.responseText);
      }
    }
  };
  request.send(null);
};

function show_pattern(prefix, data) {
    window.hashes[prefix] = eval('(' + data + ')');
    check_update_svg();
};

function hleton32(value) {
    return value.substring(6,8) + value.substring(4,6) + value.substring(2,4) + value.substring(0,2);
}


function hash_change(e) {
     start_hash = window.hash_norm.value;
     if(window.hash_p1.value.length == 8) {
         window.hash_norm.value = hleton32(window.hash_p1.value) + window.hash_norm.value.substring(8,40);
     } else {
         window.hash_norm.value = "        " + window.hash_norm.value.substring(8,40);
     }
     if(window.hash_p2.value.length == 8) {
         window.hash_norm.value = window.hash_norm.value.substring(0,8) + hleton32(window.hash_p2.value) + window.hash_norm.value.substring(16,40);
     } else {
         window.hash_norm.value = window.hash_norm.value.substring(0,8) + "        " + window.hash_norm.value.substring(16,40);
     }
     if(window.hash_p3.value.length == 8) {
         window.hash_norm.value = window.hash_norm.value.substring(0,16) + hleton32(window.hash_p3.value) + window.hash_norm.value.substring(24,40);
     } else {
         window.hash_norm.value = window.hash_norm.value.substring(0,16) + "        " + window.hash_norm.value.substring(24,40);
     }
     if(window.hash_p4.value.length == 8) {
         window.hash_norm.value = window.hash_norm.value.substring(0,24) + hleton32(window.hash_p4.value) + window.hash_norm.value.substring(32,40);
     } else {
         window.hash_norm.value = window.hash_norm.value.substring(0,24) + "        " + window.hash_norm.value.substring(32,40);
     }
     if(window.hash_p5.value.length == 8) {
         window.hash_norm.value = window.hash_norm.value.substring(0,32) + hleton32(window.hash_p5.value);
     } else {
         window.hash_norm.value = window.hash_norm.value.substring(0,32) + "        ";
     }
     if(window.hash_norm.value != start_hash) {
         check_update_svg();
     }
};

function check_update_svg() {
    var hp1 = window.hash_norm.value.substring(0,4);
    var hp2 = window.hash_norm.value.substring(4,40);
    if (window.hashes[hp1] == undefined) {
        ajax_get_hash(window.hash_norm.value);
    } else if (window.hashes[hp1][hp2] == undefined) {
        matchlen = 0;
        var uniq = undefined;
        for (hash in window.hashes[hp1]) {
            while(hp2.substring(0,matchlen+1) == hash.substring(0,matchlen+1)){
                matchlen++;
                uniq = undefined;
            }
            if(hp2.substring(0,matchlen) == hash.substring(0,matchlen)) {
                if (uniq == undefined) {
                    uniq = [hash];
                } else {
                    uniq = uniq + [hash];
                }
            }
        }
        if(uniq.length == 1) {
            update_svg(window.hashes[hp1][uniq]);
        }
    } else {
        update_svg(window.hashes[hp1][hp2]);
    }
};

window.onload = function(){
    window.hashes = {};

    window.hash_norm = document.getElementById("hash_norm");

    window.hash_p1 = document.getElementById("hash_p1");
    window.hash_p2 = document.getElementById("hash_p2");
    window.hash_p3 = document.getElementById("hash_p3");
    window.hash_p4 = document.getElementById("hash_p4");
    window.hash_p5 = document.getElementById("hash_p5");

    window.hash_p1.addEventListener('change', hash_change, false);
    window.hash_p2.addEventListener('change', hash_change, false);
    window.hash_p3.addEventListener('change', hash_change, false);
    window.hash_p4.addEventListener('change', hash_change, false);
    window.hash_p5.addEventListener('change', hash_change, false);

    window.hash_p1.addEventListener('keyup', hash_change, false);
    window.hash_p2.addEventListener('keyup', hash_change, false);
    window.hash_p3.addEventListener('keyup', hash_change, false);
    window.hash_p4.addEventListener('keyup', hash_change, false);
    window.hash_p5.addEventListener('keyup', hash_change, false);
}
]]>
</script>
</head>
<body>
<p>
This is a small guide to recover the gesture pattern of your android phone.
</p>
<p>
You'll need (temporary) root access to the filesystem of your phone. This can be done by booting a recovery image using the Android SDK. Installing the SDK is out of the scope of this guide, but there is plenty of help available on the internet to do this.<br/>
First, connect the phone to the computer using the USB cable.<br/>
If you have rooted your phone, you can get the gesture file directly, otherwise, boot a recovery image with the SDK fastboot tool.<br/>
It is a bit tricky to find a working recovery image on the internet, but you should be able to find cm-hero-recovery.img (for the HTC Hero, other phones have other recovery images) somewhere.<br/>
Get your phone into "Fastboot USB" mode, this is different for every phone (on + back on the HTC Hero).<br/>
The command to boot the recovery image is:
<pre>fastboot boot cm-hero-recovery.img</pre>
When the recovery image is loaded, you can use adb to get the gesture key.<br/>
<pre>adb shell mount /data</pre>
This will mount the /data directory (the recovery image might have skipped this step).<br/>
<pre>adb shell od -x /data/system/gesture.key</pre>
This will display the unlock hash (the first block on each line is not part of the hash).<br/>
<table border="0" style="border: 0px;">
<tr><td>0000000</td><td><input id="hash_p1"/></td><td><input id="hash_p2"/></td><td><input id="hash_p3"/></td><td><input id="hash_p4"/></td></tr>
<tr><td>0000020</td><td><input id="hash_p5"/></td><td colspan="3" style="text-align: center;">Paste the parts here to get te unlock gesture.</td></tr>
</table>
</p>
<p>
<input type="hidden" id="hash_norm" value="                                        " size="25"/>
This is your unlock gesture:<br/>
<s:svg xmlns:s="http://www.w3.org/2000/svg" width="150pt" height="150pt" viewBox="0 0 90 90" version="1.1">
    <s:g id="nr1" style="visibility: hidden;" transform="translate(0,0)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">1</s:text>
    </s:g>
    <s:g id="nr2" style="visibility: hidden;" transform="translate(30,0)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">2</s:text>
    </s:g>
    <s:g id="nr3" style="visibility: hidden;" transform="translate(60,0)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">3</s:text>
    </s:g>
    <s:g id="nr4" style="visibility: hidden;" transform="translate(0,30)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">4</s:text>
    </s:g>
    <s:g id="nr5" style="visibility: hidden;" transform="translate(30,30)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">5</s:text>
    </s:g>
    <s:g id="nr6" style="visibility: hidden;" transform="translate(60,30)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">6</s:text>
    </s:g>
    <s:g id="nr7" style="visibility: hidden;" transform="translate(0,60)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">7</s:text>
    </s:g>
    <s:g id="nr8" style="visibility: hidden;" transform="translate(30,60)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">8</s:text>
    </s:g>
    <s:g id="nr9" style="visibility: hidden;" transform="translate(60,60)">
        <s:circle cx="15" cy="15" r="10" />
        <s:text x="12" y="19" style="fill: white;">9</s:text>
    </s:g>
</s:svg>

</p>
</body>
</html>

