#!/usr/bin/env python
# -*- coding: utf-8 -*-

import threading
from threading import Lock
from Queue import Queue
import jtrace
import datetime
import time
import sys
import struct
import urlparse

MAX_NR_HITS = 10

JDWP_REQ_TIMEOUT = 60

# hoook suspend policy
SUSPEND_NONE              = 0
SUSPEND_EVENTTHREAD       = 1
SUSPEND_ALL               = 2

# hook modifier
MODKIND_COUNT             = 1
MODKIND_THREADONLY        = 2
MODKIND_CLASSMATCH        = 5
MODKIND_LOCATIONONLY      = 7

TYPE_CLASS                = 1

# request error
ERR_NONE                  = 0
ERR_NOT_IMPLEMENTED       = 99

# type names
TAG_INT                   = 73
TAG_OBJECT                = 76
TAG_BOOLEAN               = 90
TAG_CLASS_OBJECT          = 99
TAG_STRING                = 115
TAG_THREAD_GROUP          = 103
TAG_CLASS_LOADER          = 108
TAG_THREAD                = 116

CONTENT_API_LIST = ['android.content.ContentResolver.insert', 'android.content.ContentResolver.delete', 'android.content.ContentResolver.update', 'android.content.ContentResolver.query']
REQUEST_LOCATION_API_LIST = ['android.location.LocationManager.requestSingleUpdate', 'android.location.LocationManager.requestLocationUpdates']

class RequestError(Exception):
    """
    Class used to handle request error
    """
    def __init__(self, code):
        Exception.__init__(self, 'request failed, code %s' % code)
        self.code = code

class Hook:
    """
    Hook is the class to present a hook for a function call
    """
    def __init__(self, sess, api_name, ident, func=None, queue=None, args=(), event_kind=40):
        self.sess = sess
        if queue is not None:
            self.queue = queue
        elif func is None:
            self.queue = queue or Queue()
        self.api_name = api_name
        self.func = func
        self.ident = ident
        self.args = args
        self.event_kind = event_kind
        self.disabled = False
        self.nr_hits = 0
        with self.sess.ectl:
            self.sess.emap[ident] = self
    @property
    def conn(self):
        """
        Get connection for this VM
        """
        return self.sess.conn

    def __str__(self):
        """
        str representation the object
        """
        return ('<%s> %s' % (str(self.ident), str(self.event_kind)))

    def put(self, data):
        """
        Call the callbacks, e.g. report_hit in app.py
        """
        if self.func is not None:
            return self.func(data + self.args)
        else:
            return self.queue.put(data + self.args)

    def get(self, block=False, timeout=None):
        """
        Get an item from the queue
        """
        return self.queue.get(block, timeout)

    def clear(self):
        """
        Send cmd to clear the hook
        """
        self.disabled = True
        conn = self.conn
        # print "clear kind: ", self.event_kind
        # print "celar ident: ", self.ident
        body = struct.pack(">BI", self.event_kind, self.ident)
        code, unknown = conn.request(0x0f02, body)
        if code != ERR_NONE:
            print "clear ", self.ident, " failed - code = ", code
        else:
            pass
            #print "clear ", self.ident, " ok"

unpack_impl = [None, ] * 256

def register_unpack_impl(ek, fn):
    unpack_impl[ek] = fn

def unpack_event_location(sess, buf):
    idx = 0
    # Unpack request id
    rid = struct.unpack(">I", buf[idx: idx + 4])[0]
    idx += 4
    # Unpack Thread
    tid = sess.unformat(sess.conn.threadIDSize, buf[idx: idx + sess.conn.threadIDSize])
    idx += sess.conn.threadIDSize
    # Unpack location
    # type tag
    type_tag = struct.unpack(">B", buf[idx: idx + 1])[0]
    idx += 1
    # classID
    cid = sess.unformat(sess.conn.referenceTypeIDSize, buf[idx: idx + sess.conn.referenceTypeIDSize])
    idx += sess.conn.referenceTypeIDSize
    # methodID
    mid = sess.unformat(sess.conn.methodIDSize, buf[idx: idx + sess.conn.methodIDSize])
    idx += sess.conn.methodIDSize
    # location
    loc = struct.unpack(">Q", buf[idx: idx + 8])[0]
    idx += 8
    return (rid, tid, type_tag, cid, mid, loc)

def unpack_vm_death(sess, buf):
    rid = struct.unpack(">I", buf[0:4])[0]
    print "VM DEATH", rid
    return (rid, )

# Single Step
register_unpack_impl(1, unpack_event_location)
# Breakpoint
register_unpack_impl(2, unpack_event_location)
# MothodEntry
register_unpack_impl(40, unpack_event_location)
# MothodExit
register_unpack_impl(41, unpack_event_location)
# VM DEATH
register_unpack_impl(99, unpack_vm_death)

class Session(object):
    """
    A session for the trace
    """
    def __init__(self, conn):
        self.conn = conn
        self.emap = {}
        self.ectl = Lock()
        self.evtq = Queue()
        self.stop_flag = threading.Event()
        # Set the callback for the cmd 64:100
        # Event Command Set（64）：Composite Command (100)
        conn.set_vm_request_cb(0x4064, self.evtq)
        self.ethd = threading.Thread(
            name='Session', target=self.run  # Create a thread with name "Session" to run the code run
        )
        self.ethd.daemon = 1  # Kill child thread when main thread is terminated
        self.ethd.start()
        self.args = ()

        self.request_ids = []

        self.all_methods = {}
        self.all_threads = {}
        self.all_fields = {}
        self.main_thread_id = None
        self.hooks = []

    def get_thread_name_by_thread_id(self, thread_id):
        """
        Get thread name from a thread id
        """
        # print "Get thread_name for thread_id", thread_id
        tid = self.format(self.conn.threadIDSize, thread_id)
        code, buf = self.conn.request(0x0B01, tid, JDWP_REQ_TIMEOUT)
        if code != ERR_NONE:
            raise RequestError(code)
        sz = 4
        nr = self.unformat(sz, buf[0:sz])
        return buf[sz:sz+nr]

    def allthreads(self):
        """
        Get all the threads in the VM
        """
        code, buf = self.conn.request(0x0104, '', JDWP_REQ_TIMEOUT)
        if code != ERR_NONE:
            raise RequestError(code)
        idx = 0
        nr_threads = struct.unpack(">I", buf[idx:idx+4])[0]
        idx += 4
        self.all_threads = {}
        for i in range(0, nr_threads):
            sz = self.conn.threadIDSize
            thread_id = self.unformat(sz, buf[idx:idx+sz])
            idx += sz
            thread_name = self.get_thread_name_by_thread_id(thread_id)
            self.all_threads[thread_id] = thread_name
            if thread_name == u"main":
                self.main_thread_id = thread_id

    def allclasses(self):
        """
        Get all the classes in the VM
        """
        try:
            getattr(self, "all_classes")
        except:
            code, buf = self.conn.request(0x0114, timeout=JDWP_REQ_TIMEOUT)
            if code == ERR_NONE:
                # print "CMD=0114 OK"
                formats = [ ('C', "refTypeTag"),
                            (self.conn.referenceTypeIDSize, "refTypeId"),
                            ('S', "signature"),
                            ('S', "genericSignature"),
                            ('I', "status")]
                self.all_classes = self.parse_entries(buf, formats)
                return self.all_classes
            else:
                print "CMD=0114 FAIL", code
                raise RequestError(code)

    def get_class_by_name(self, name):
        """
        Find a class by class name
        """
        for entry in self.all_classes:
            if entry["signature"].lower() == name.lower() :
                return entry
        return None

    def get_class_by_refid(self, refID):
        """
        Find a class by refID
        """
        for entry in self.all_classes:
            if entry["refTypeId"] == refID :
                return entry
        return None

    def get_variable_table(self, refID, methodID):
        """
        Find variable table of a method
        """
        body = self.format(self.conn.referenceTypeIDSize, refID)
        body += self.format(self.conn.methodIDSize, methodID)
        code, buf = self.conn.request(0x0605, body, timeout=JDWP_REQ_TIMEOUT)
        if code == ERR_NONE:
            argCnt = self.unformat(4, buf[:4])
            formats = [
                ("L", "codeIndex"),
                ("S", "name"),
                ("S", "signature"),
                ("S", "genericSignature"),
                ("I", "length"),
                ("I", "slot")
            ]
            return self.parse_entries(buf[4:], formats)
        else:
            print "CMD 0605 FAIL", code, refID, methodID
            raise RequestError(code)

    def get_value_from_stack_frame(self, threadID, frameID, value_type, slot_index):
        """
        Get a value from the stack frame
        The first argument slot_index = 1
        The second argument slot_index = 1
        """
        body = self.format(self.conn.threadIDSize, threadID)
        body += self.format(self.conn.frameIDSize, frameID)
        # get only one slot
        nr_slots = 1
        body += self.format(4, nr_slots)
        # repeated slots times
        body += self.format(4, slot_index)
        body += self.format(1, value_type)

        code, buf = self.conn.request(0x1001, body, timeout=JDWP_REQ_TIMEOUT)
        if code == ERR_NONE:
            # print "CMD 1001 OK"
            formats = [ ("Z", "value") ]
            typ, val = self.parse_entries(buf, formats)[0]['value']
            # print "get_value_from_stack_frame:", typ, val
            return (typ, val)
        else:
            print "CMD 1001 FAIL", code, threadID, frameID
            raise RequestError(code)

    def get_top_frame(self, threadID):
        """
        Get the latest stack frame of the thread
        """
        body = self.format(self.conn.threadIDSize, threadID)
        startFrame = 0
        length = 1
        body += self.format(4,  startFrame)
        body += self.format(4,  length)
        # ThreadReference Command Set (11), Frames Command (6)
        code, buf = self.conn.request(0x0B06, body, timeout=JDWP_REQ_TIMEOUT)
        if code == ERR_NONE:
            # print "CMD 0B06 OK"
            formats = [ (self.conn.frameIDSize, "frameID"),
                        (1, "typeTag"),
                        (self.conn.referenceTypeIDSize, "classID"),
                        (self.conn.methodIDSize, "methodID"),
                        (8, "index")]
            frame = self.parse_entries(buf, formats)[0]
            # print "get_top_frame: ", frame
            return frame
        else:
            print "CMD 0B06 FAIL", code, threadID
            raise RequestError(code)

    def get_fields(self, refTypeId):
        """
        Get all the fields within a class with refTypeId
        """
        # Given a class refTypeId to get all the fields in this calss
        if not self.all_fields.has_key(refTypeId):
            refId = self.format(self.conn.referenceTypeIDSize, refTypeId)
            # ReferenceType Command Set (2) FieldsWithGeneric (14)
            code, buf = self.conn.request(0x020E, refId, timeout=JDWP_REQ_TIMEOUT)
            if code == ERR_NONE:
                # print "CMD 020E OK"
                formats = [ (self.conn.fieldIDSize, "fieldID"),
                            ('S', "name"),
                            ('S', "signature"),
                            ('S', "genericSignature"),
                            ('I', "modBits")]
                self.all_fields[refTypeId] = self.parse_entries(buf, formats)
                return self.all_fields[refTypeId]
            else:
                print "CMD 020E FAIL", code
                raise RequestError(code)
        else:
            return self.all_fields[refTypeId]

    def get_field_by_name(self, refTypeId, name):
        """
        Get a field within a class with refTypeId by name
        """
        if self.all_fields.has_key(refTypeId):
            for entry in self.all_fields[refTypeId]:
                # print "refTypeId", entry
                if entry["name"] == name:
                    return entry
        return None

    def objectID2referenceTypeID(self, objectID):
        """
        Get the typeId of a object
        """
        # ObjectReference Command Set (9) ReferenceType Command (1)
        body = self.format(self.conn.objectIDSize, objectID)
        code, buf = self.conn.request(0x0901, body, timeout=JDWP_REQ_TIMEOUT)
        if code == ERR_NONE:
            # print "CMD 0901 OK"
            sz = 1
            refTypeTag = self.unformat(sz, buf[0:sz])
            sz = self.conn.referenceTypeIDSize
            typeID = self.unformat(sz, buf[1:1+sz])
            # print "objectID2referenceTypeID", objectID, refTypeTag, typeID
            return typeID
        else:
            print "CMD 0901 FAIL", code
            raise RequestError(code)

    def objectID2className(self, objectID):
        """
        Get the class name of a object
        """
        # first find the referenceTypeID: objectID -> referenceTypeID
        refID = self.objectID2referenceTypeID(objectID)
        # then find the name
        return self.get_class_by_refid(refID)['signature']

    def get_string_from_stringobject(self, objectID):
        """
        Get the string of a object which is a string type
        """
        # StringReference Command Set (10) Value Command (1)
        body = self.format(self.conn.objectIDSize, objectID)
        code, buf = self.conn.request(0x0A01, body, timeout=JDWP_REQ_TIMEOUT)
        if code == ERR_NONE:
            # print "CMD 0A01 OK"
            size = struct.unpack(">I", buf[0:4])[0]
            return buf[4:4+size]
        else:
            print "CMD 0A01 FAIL", code
            raise RequestError(code)

    def get_field_value_from_objectID(self, objectID, field_name):
        """
        Get filed valule from a object
        """
        # The value can be a objectID or a primitive type, like int
        # print "get_field_value_from_objectID: objectID=%s, field_name=%s" % (objectID, field_name)
        refID = self.objectID2referenceTypeID(objectID)
        self.get_fields(refID)
        fieldID = None
        field = self.get_field_by_name(refID, field_name)
        if field:
            fieldID = field['fieldID']
        else:
            parent_refID = self.get_class_by_name("Landroid/net/Uri$AbstractPart;")['refTypeId']
            self.get_fields(parent_refID)
            fieldID = self.get_field_by_name(parent_refID, field_name)['fieldID']
            # print "Get parent fieldID: ", field_name, fieldID

        body = self.format(self.conn.objectIDSize, objectID)
        fields = 1
        body += struct.pack(">I", fields)
        body += self.format(self.conn.fieldIDSize, fieldID)

        # ObjectReference Command Set (9) GetValues Command (2)
        # Given a objectID and a fieldID to get the value of this field
        code, buf = self.conn.request(0x0902, body, timeout=JDWP_REQ_TIMEOUT)
        if code == ERR_NONE:
            # print "CMD 0902 OK"
            formats = [ ("Z", "value") ]
            typ, val = self.parse_entries(buf, formats)[0]['value']
            # print "get_field_value_from_objectID: ", typ, val
            return (typ, val)
        else:
            print "CMD 0902 FAIL", code
            raise RequestError(code)

    def get_uri_string_from_uriobject(self, objectID):
        """
        Get the uri string from a object
        """
        uri = None
        class_name = self.objectID2className(objectID)
        #if class_name == 'android.net.Uri$StringUri':
        # print "get_uri_string_from_uriobject", class_name
        if class_name == 'Landroid/net/Uri$StringUri;':
            typ, objectIDString = self.get_field_value_from_objectID(objectID, 'uriString')
            uri = self.get_string_from_stringobject(objectIDString)
            # print "StringUri: ", uri
        #elif class_name == 'android.net.Uri$HierarchicalUri':
        elif class_name == 'Landroid/net/Uri$HierarchicalUri;':
            typ, objectIDPart = self.get_field_value_from_objectID(objectID, 'authority')
            typ, objectIDString = self.get_field_value_from_objectID(objectIDPart, 'decoded')
            uri = self.get_string_from_stringobject(objectIDString)
            # print "HierarchicalUri: ", uri
        else:
            print "Err get URI from URI object"
        if not uri:
            return None

        if uri.startswith('content://'):
            return urlparse.urlparse(uri).netloc
        else:
            return uri

    def get_methods(self, refTypeId):
        """
        Get all the methods within the class
        """
        if not self.all_methods.has_key(refTypeId):
            refId = self.format(self.conn.referenceTypeIDSize, refTypeId)
            code, buf = self.conn.request(0x020F, refId, timeout=JDWP_REQ_TIMEOUT)
            if code == ERR_NONE:
                # print "CMD 020F OK"
                formats = [ (self.conn.methodIDSize, "methodId"),
                            ('S', "name"),
                            ('S', "signature"),
                            ('S', "genericSignature"),
                            ('I', "modBits")]
                self.all_methods[refTypeId] = self.parse_entries(buf, formats)
                return self.all_methods[refTypeId]
            else:
                print "CMD 020F FAIL", code
                raise RequestError(code)
        else:
            return self.all_methods[refTypeId]

    def get_method_by_name(self, name):
        """
        Find the fucntion with the name
        """
        for refId in self.all_methods.keys():
            for entry in self.all_methods[refId]:
                if entry["name"].lower() == name.lower() :
                    return entry
        return None

    def get_methods_by_name(self, name):
        """
        Find the functions with the name
        """
        entries = []
        for refId in self.all_methods.keys():
            for entry in self.all_methods[refId]:
                if entry["name"].lower() == name.lower() :
                    entries.append(entry)
        return entries

    def get_methods_by_refid_name(self, refId, name):
        """
        Find the functions with the name within the class
        """
        entries = []
        if refId in self.all_methods:
            for entry in self.all_methods[refId]:
                if entry["name"].lower() == name.lower() :
                    entries.append(entry)
        return entries

    def do_hook(self, api_name, classID, methodID, locationIndex=0, func=None, queue=None, args=()):
        """
        Apply the hook for the api
        """
        conn = self.conn
        event_kind = 2
        nr_modifiers = 1
        suspend_method = SUSPEND_NONE
        suspend_method = SUSPEND_EVENTTHREAD
        suspend_method = SUSPEND_ALL

        body = struct.pack(">BBI", event_kind, suspend_method, nr_modifiers)
        # thread only
        # body += struct.pack(">B", MODKIND_THREADONLY)
        # body += self.format(self.conn.threadIDSize, self.main_thread_id)
        # location only
        body += struct.pack(">B", MODKIND_LOCATIONONLY)
        body += struct.pack(">B", TYPE_CLASS)
        body += self.format(self.conn.referenceTypeIDSize, classID)
        body += self.format(self.conn.methodIDSize, methodID)
        body += struct.pack(">Q", locationIndex)

        code, buf = conn.request(0x0F01, body, JDWP_REQ_TIMEOUT)
        if code != ERR_NONE:
            raise RequestError(code)
        request_id = struct.unpack(">I", buf[0:4])[0]
        # print request_id
        self.request_ids.append(request_id)
        return Hook(self, api_name, request_id, func, queue, args, event_kind=event_kind)

    def format(self, fmt, value):
        """
        Pack a value with fmt
        """
        if fmt == "L" or fmt == 8:
            return struct.pack(">Q", value)
        elif fmt == "I" or fmt == 4:
            return struct.pack(">I", value)
        elif fmt == "B" or fmt == 1:
            return struct.pack(">B", value)

        raise Exception("Unknown format")

    def unformat(self, fmt, value):
        """
        Unpack a value with fmt
        """
        if fmt == "L" or fmt == 8:
            return struct.unpack(">Q", value[:8])[0]
        elif fmt == "I" or fmt == 4:
            return struct.unpack(">I", value[:4])[0]
        elif fmt == "B" or fmt == 1:
            return struct.unpack(">B", value[:1])[0]
        else:
            raise Exception("Unknown format")
        return

    def parse_entries(self, buf, formats, explicit=True):
        """
        Parse items with the formats from the buf
        """
        entries = []
        index = 0
        if explicit:
            nb_entries = struct.unpack(">I", buf[:4])[0]
            buf = buf[4:]
        else:
            nb_entries = 1

        for i in range(nb_entries):
            data = {}
            for fmt, name in formats:
                if fmt == "L" or fmt == 8:
                    data[name] = int(struct.unpack(">Q",buf[index:index+8]) [0])
                    index += 8
                elif fmt == "I" or fmt == 4:
                    data[name] = int(struct.unpack(">I", buf[index:index+4])[0])
                    index += 4
                elif fmt == "B" or fmt == 1:
                    data[name] = int(struct.unpack(">B", buf[index:index+1])[0])
                    index += 1
                elif fmt == 'S':
                    l = struct.unpack(">I", buf[index:index+4])[0]
                    data[name] = buf[index+4:index+4+l]
                    index += 4+l
                elif fmt == 'C':
                    data[name] = ord(struct.unpack(">c", buf[index])[0])
                    index += 1
                elif fmt == 'Z':
                    t = ord(struct.unpack(">c", buf[index])[0])
                    index += 1
                    # STRING, THREAD, THREAD_GROUP, CLASS_LOADER, CLASS_OBJECT
                    if t == TAG_STRING or t == TAG_THREAD or t == TAG_THREAD_GROUP or t == TAG_CLASS_LOADER or t == TAG_CLASS_OBJECT or t == TAG_OBJECT:
                        data[name] = (t, self.unformat(self.conn.objectIDSize, buf[index:index+8]))
                        index += 8
                    # INT
                    elif t == TAG_INT:
                        data[name] = (t, struct.unpack(">I", buf[index:index+4])[0])
                        index += 4
                    # BOOLEAN
                    elif t == TAG_BOOLEAN:
                        data[name] = (t, struct.unpack(">B", buf[index:index+1])[0])
                        index += 1
                    else:
                        print "UNSUPPORTED VALUE", t

                else:
                    print "Error"

            entries.append(data)

        return entries

    def stop(self):
        """
        Stop the session
        """
        # clear all is not supported by android vm
        # self.clearall()
        self.stop_flag.set()
        self.conn.stop()

    def run(self):
        """
        Thread function for the session
        """
        while not self.stop_flag.is_set():
            self.process_event(*self.evtq.get())

    def hook(self, ident, func=None, queue=None, args=(), event_kind=40):
        """
        Return a Hook object
        """
        return Hook(self, ident, func, queue, args, event_kind=event_kind)

    def get_api_args(self, api_name, thread_id, class_id, method_id):
        api_args = ''
        parameterDist = {
            'com.android.messaging.datamodel.MessagingContentProvider':'信息',
            'com.android.contacts':'联系人',
            'sms':'短信','mms':'彩信','mms-sms':'信息',
            'call_log':'通话记录','blacklist':'联系人(黑名单)','telephony':'电话',
            'com.android.email.provider':'电子邮件',
            'com.android.browser':'上网记录',
            'media':'图片/视频/音频',
            'com.android.calendar':'日历'
        }
        invert_args = ''
        if api_name in CONTENT_API_LIST:
            # get slot index of arg "uri"
            vtab = self.get_variable_table(class_id, method_id)
            # print([x["name"] for x in vtab])
            uri_slot = [x for x in vtab if x["name"] in ["uri", "url"]][0]["slot"]
            # print("uri_slot", uri_slot)
            frame = self.get_top_frame(thread_id)
            typ, val = self.get_value_from_stack_frame(thread_id, frame['frameID'], TAG_OBJECT, uri_slot)
            if typ == TAG_OBJECT:
                api_args = "URI: "+self.get_uri_string_from_uriobject(val)
                test_args = self.get_uri_string_from_uriobject(val)
                # print("content uri: ", test_args)
                if test_args in parameterDist:
                    api_args = ""+parameterDist[test_args].decode('UTF-8')
                # else:
                #     api_args = ""
        elif api_name in REQUEST_LOCATION_API_LIST:
            try:
                # get slot index of arg "provider"
                vtab = self.get_variable_table(class_id, method_id)
                provider_slot = [x for x in vtab if x["name"] == "provider"][0]["slot"]
                # print("provider_slot", provider_slot)
                frame = self.get_top_frame(thread_id)
                typ, provider = self.get_value_from_stack_frame(thread_id, frame['frameID'], TAG_STRING, provider_slot)
                if typ == TAG_STRING:
                    provider_str = self.get_string_from_stringobject(provider)
                    api_args = u"Provider: " + provider_str
            except Exception as e:
                print(e)
        return api_args

    def process_event(self, ident, buf):
        """
        Handle events from the VM
        """
        pol, ct = struct.unpack(">cI", buf[0:5])
        buf = buf[5:]
        for i in range(0, ct):
            ek = struct.unpack(">B", buf[:1])[0]
            # print "process_event, event_kind=",ek
            buf = buf[1:]
            im = unpack_impl[ek]
            if im is None:
                raise RequestError(ek)
            evt = im(self, buf)
            rid, tid, type_tag, cid, mid, loc = evt
            hook = None
            remove_the_hook = False
            with self.ectl:
                if rid in self.emap:
                    hook = self.emap.get(rid)
                    hook.nr_hits += 1
                    if hook.nr_hits >= MAX_NR_HITS:
                        # print "removed nr_hists"
                        hook = self.emap.pop(rid)
                        remove_the_hook = True
            if hook:
                api_args = self.get_api_args(hook.api_name, tid, cid, mid)
                # Clear the hook before calling the callback which will resume the thread
                if remove_the_hook:
                    hook.clear()
                # Call the callbacks of this hook
                hook.put((api_args,))
        self.resumevm()

        time.sleep(0.01)

    def dispose(self):
        """
        Send the dispose cmnd to the VM
        [VirtualMachine Command Set ][Suspend Command (6)]
        """
        timeout = 3
        code, buf = self.conn.request(0x0106, '', timeout)
        if code != ERR_NONE:
            raise RequestError(code)
            print "dispose: error code = ", code
        else:
            print "dispose: ok    code = ", code

    def suspend(self):
        """
        Send the suspend cmd to the VM
        """
        code, buf = self.conn.request(0x0108, '', JDWP_REQ_TIMEOUT)
        if code != ERR_NONE:
            raise RequestError(code)

    def count(self):
        """
        Send the cound cmd to the VM
        """
        code, buf = self.conn.request(0x0108, '', JDWP_REQ_TIMEOUT)
        if code != ERR_NONE:
            raise RequestError(code)

    def resumevm(self):
        """
        Send the resumevm cmd to the VM
        """
        code, buf = self.conn.request(0x0109, '', JDWP_REQ_TIMEOUT)
        if code != ERR_NONE:
            raise RequestError(code)

    def resumethread(self, threadID):
        """
        Send the resumethread cmd to the VM
        """
        code, buf = self.conn.request(0x0109, '', JDWP_REQ_TIMEOUT)
        if code != ERR_NONE:
            raise RequestError(code)


    def exit(self, code=0):
        """
        Send exit cmd to the VM
        """
        conn = self.conn
        body = struct.pack(">I", code)
        code, buf = conn.request(0x010A, '', JDWP_REQ_TIMEOUT)
        if code != ERR_NONE:
            raise RequestError(code)

    def hold_events(self):
        """
        Send hold events cmd to the VM
        """
        code, buf = self.conn.request(0x010F, '', JDWP_REQ_TIMEOUT)
        if code == ERR_NOT_IMPLEMENTED:
            return
        if code != ERR_NONE:
            raise RequestError(code)

    def release_events(self):
        """
        Send release events cmd to the VM
        """
        code, buf = self.conn.request(0x0110, '', JDWP_REQ_TIMEOUT)
        if code == ERR_NOT_IMPLEMENTED:
            return
        if code != ERR_NONE:
            raise RequestError(code)

    def clearall(self):
        """
        Send clear all events cmd to the VM
        """
        code, buf = self.conn.request(0x0F03, '', JDWP_REQ_TIMEOUT)
        if code == ERR_NOT_IMPLEMENTED:
            return
        if code != ERR_NONE:
            raise RequestError(code)
