Partial support for Samsung SGH-D600

Marvin Schmidt <[email protected]> Sun, 05 Jul 2009 22:04:43 +0200
Newsgroups gmane.comp.mobile.bitpim.devel
Message-ID <[email protected]>
Hello,

quite some time ago I started implementing support for the Samsung
SGH-D600, unfortunately that phone died and I had to get a new one,
that's why I couldn't finish that project.
I still got all of my work, but I'm afraid I can't comment too precisely
on it, but I figured maybe someone would like to continue my work,
that's why I'm sending what I got here.


The first thing I had to do was changing

	return ': '.join(resp[0].split(': ')[1:])

in the __send_at_and_get function in src/phone_detect.py to

        return resp[0]

in order to get my phone recognized at all. I wish I could tell you more
about why it's necessary, but it's been too long ago and I'm not able to
test it anymore.


The second patch adds two parameters to the sendpbcommand in
src/phones/com_samsung_packet.py. I think to remember doing this because
the phone sends big amounts splitted in multiple packets. If a packet
ends in "#OK#\r\n" one would have to send "##>\r\n" to keep to transfer
going. Doing that in the subclass was awfully slow, that's why i moved
it to the commport class to be able to react faster. Unfortunately I'm
missing the changes i did to commport.py :-/


Besides those 2 things it's just the p_samsungsghd600.p, the
com_samsungsghd600.py and the entry in src/phones/__init__.py


I got some mails from people, who would like to see support for the
Samsung SGH-D600 in BitPim an since i can't really do much about it I
hope some who has the phone takes it from here.
If any information about the phone are needed, I can probably cram out
the references/logs/etc. I used, just ask me, i'll do my best to help.


Best regards,
Marvin Schmidt

------------------------------------------------------------------------------

_______________________________________________
BitPim-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bitpim-devel
001-phone-detection.patch (text/x-patch, 517 B)
Index: src/phone_detect.py
===================================================================
--- src/phone_detect.py	(revision 4748)
+++ src/phone_detect.py	(working copy)
@@ -104,7 +104,8 @@
     def __send_at_and_get(self, comm, cmd):
         try:
             resp=comm.sendatcommand(cmd)
-            return ': '.join(resp[0].split(': ')[1:])
+            #return ': '.join(resp[0].split(': ')[1:])
+            return resp[0]
         except:
             return None
     def __get_manufacturer(self, comm):
002-samsung-packet.patch (text/x-patch, 1.3 KB)
Index: src/phones/com_samsung_packet.py
===================================================================
--- src/phones/com_samsung_packet.py	(revision 4748)
+++ src/phones/com_samsung_packet.py	(working copy)
@@ -147,7 +147,7 @@
         response=self.comm.sendatcommand("#PMODE=0")
         return True
         
-    def sendpbcommand(self, request, responseclass, ignoreerror=False, fixup=None):
+    def sendpbcommand(self, request, responseclass, ignoreerror=False, fixup=None, getasone=False, autocontinue=True):
         """Similar to the sendpbcommand in com_sanyo and com_lg, except that
         a list of responses is returned, one per line of information returned
         from the phone"""
@@ -158,7 +158,13 @@
         data=buffer.getvalue()
 
         try:
-            response_lines=self.comm.sendatcommand(data, ignoreerror=ignoreerror)
+            response_lines=self.comm.sendatcommand(data, ignoreerror=ignoreerror, autocontinue=autocontinue)
+            if getasone:
+                #self.log('getasone: ' + str(";".join(["%s" % (v) for v in response_lines])))
+                temp="\x0D\x0A".join(response_lines)
+                response_lines[:] = []
+                response_lines.append(temp)
+
         except commport.ATError:
             self.comm.success=False
             self.mode=self.MODENONE
003-init-entry.patch (text/x-patch, 680 B)
Index: src/phones/__init__.py
===================================================================
--- src/phones/__init__.py	(revision 4748)
+++ src/phones/__init__.py	(working copy)
@@ -372,6 +372,11 @@
                             'brand': b_samsung,
                             'helpid': None,
                             },
+	      'SGH-D600': { 'module': 'com_samsungsghd600',
+	      		    'brand': b_samsung,
+	      		    #'carrier': [c_vzw],
+	      		    'helpid': None,
+	      		   },
               'SPH-A460': { 'module': 'com_samsungspha460',
                             'brand': b_samsung,
                             'helpid': helpids.ID_PHONE_SAMSUNGOTHERS,
p_samsungsghd600.py (text/x-python, 181.1 KB) - not displayed
com_samsungsghd600.py (text/x-python, 23.7 KB)
### BITPIM
###
### Copyright (C) 2009 Marvin Schmidt <[email protected]>
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of the BitPim license as detailed in the LICENSE file.
###

### This file is probably based in parts upon com_samsungscha670.py


"""Communicate with a Samsung SGH-D600"""

# lib modules
import re
import sha
import time

# my modules
import common
import commport
#import com_brew
import com_samsung
import com_samsung_packet
import com_phone
import conversions
import fileinfo
import nameparser
import p_samsungsghd600
import prototypes
import sms
import cStringIO

import bpcalendar

numbertypetab=('cell', 'home','office','fax','other')

#class Phone(com_etsi.Phone):
class Phone(com_samsung_packet.Phone):
    "Talk to the Samsung SGH-D600 Cell Phone"

    desc="SGH-D600"
    serialsname='sghd600'
    protocolclass=p_samsungsghd600
    parent_phone=com_samsung_packet.Phone


    def __init__(self, logtarget, commport):
        "Calls all the constructors and sets initial modes"
        com_samsung_packet.Phone.__init__(self, logtarget, commport)
        self.numbertypetab=numbertypetab
        self.mode=self.MODENONE

    getringtones=None

    def listfiles(self, dir=''):
        results={}

        bla=self.comm.sendatcommand("+FSCD=\"" + dir + "\"");
        self.log("Listing files in dir: '/"+dir+"'")
        dir="/"+dir

        req=self.protocolclass.filelistrequest()
        if len(dir):
               req.dir=dir
        else:
            req.dir="/"
        res=self.sendpbcommand(req, self.protocolclass.filelistresponse)

        for entry in res:
            results[entry.filename]={ 'name': entry.filename, 'type': 'file', 'size': entry.size, 'date': (0, "") }

        return results


    def listsubdirs(self, dir='', recurse=0):
        results={}
        self.log("Listing own subdirs in dir: '"+dir+"'")

        bla=self.comm.sendatcommand("+FSCD=\"" + dir + "\"");

        req=self.protocolclass.dirlistrequest()
        if len(dir):
               req.dir=dir
        else:
            req.dir="/"
        res=self.sendpbcommand(req, self.protocolclass.dirlistresponse)

        for entry in res:
            subdir=entry.name
            if len(dir):
                subdir=dir+"/"+subdir
            else:
                subdir="/"+subdir
            results[subdir]={ 'name': subdir, 'type': 'directory' }

        #if recurse:
            #for k,_subdir in results.items():
                #results.update(self.listsubdirs(_subdir['name'], recurse-1))

        return results


    def getfilecontents(self, file, use_cache=False):
        """
        if use_cache:
            node=self.statfile(file)
            if node and file_cache.hit(file, node['date'][0], node['size']):
                self.log('Reading from cache: '+file)
                _data=file_cache.data(file)
                if _data:
                    return _data
                self.log('Cache file corrupted and discarded')

        """
        start=time.time()
        self.log("Getting file contents '"+file+"'")
        desc="Reading "+file

        req=self.protocolclass.fsinforequest()
        res=self.sendpbcommand(req, self.protocolclass.fsinforesponse)

        """
        req=self.protocolclass.filerequest()
        req.filename=file
        res=self.sendpbcommand(req, self.protocolclass.fileresponse, autocontinue=False)
        """
        try:
            response_lines=self.comm.sendatcommand("+FSFR=-1,\"" + file + "\"", ignoreerror=False, autocontinue=False)
        except commport.ATError:
            self.comm.success=False
            self.mode=self.MODENONE
            self.raisecommsdnaexception("manipulating the phonebook")

        self.comm.success=True

        self.log('response_lines: ' + str(response_lines))

        data=cStringIO.StringIO()

        if len(response_lines) > 1:
            self.log("short way")
            res=self.protocolclass.fileresponse()
            line=response_lines.pop(0)
            buffer=prototypes.buffer(line)
            res.readfrombuffer(buffer, logtitle="Samsung phonebook response")
            filesize=res.size

            for line in response_lines:
                res=self.protocolclass.downloadpacket()
                buffer=prototypes.buffer(line)
                res.readfrombuffer(buffer, logtitle="Samsung phonebook response")

                data.write(res.data)

            data=data.getvalue()
            return data;

        self.log("long way")
        res=self.protocolclass.fileresponse()
        buffer=prototypes.buffer(response_lines[0][:-4])
        res.readfrombuffer(buffer, logtitle="Samsung phonebook response")
        filesize=res.size

        #req=self.protocolclass.continuerequest()
        #res=self.sendpbcommand(req, self.protocolclass.download)
        try:
            charsread=0
            tries=0
            self.comm.write("##>\r\n")

            res=""
            while True:
                b=self.comm.ser.inWaiting()
                if b:
                    read=self.comm.read(b,0)
                    if read.find("OK\r")>=0 or (res.find("ERROR\r")>=0):
                        break
                    if read.find("#OK#\r\n"):
                        res=read[:-6]
                        self.log("myres: " + str(read[:-6]))

                        #dl=self.protocolclass.downloadpacket()
                        #buffer=prototypes.buffer(read[:-6])
                        #dl.readfrombuffer(buffer, logtitle="Samsung phonebook response")

                        self.comm.write("##>\r\n")
                    continue
                r=self.comm.read(1,0)
                if len(r):
                    res=res+r
                    continue
                break

            data.write(res)

            return data.getvalue()
            """
            while True:
                b=self.comm.ser.inWaiting()

                if b == 0:
                    #if tries > 3:
                    #    self.log("3 tries...")
                    #    break
                    #tries+=1
                    continue

                data=self.comm._read(b, True)
                if data.endswith("#OK#\r\n"):
                    #res=self.protocolclass.downloadpacket()
                    #moo=data[2:]
                    #moo=moo[:-6]
                    #buffer=prototypes.buffer(moo)
                    #res.readfrombuffer(buffer, logtitle="Samsung phonebook response")

                    charsread+=512
                    self.progress(charsread, filesize, 'Reading file...')

                    self.comm.write("##>\r\n")

            """
            #self.log('readahead: ' + str(self.comm.readahead[:-6]))
        except commport.ATError:
            self.comm.success=False
            self.mode=self.MODENONE
            self.raisecommsdnaexception("manipulating the phonebook")

        #line=self.getcleanline()
        #self.log("getfile cleanline: " + str(line))
        """
        if line==fullline:
            line=self.getcleanline()
        while line!="OK" and line:
            if line=="ERROR":
                if not ignoreerror:
                    raise ATError
            elif line.endswith("#OK#"):
                if autocontinue:
                    res.append(line[:-4])
                    self.write(str("##>\r\n"))
                else:
                    res.append(line)
                    break
                try:
                    self.readatresponse(ignoreerror)
                except CommTimeout:
                    raise
            else:
                res.append(line)
            line=self.getcleanline()

        self.log("res: " + str(res))
        """
        """
         * first packet looks like this:
         * +FSFR: 0,"test.jpg",106,"",0,513,0,"","",""#OK#
         *
         * @param 1		int			?
         * @param 2		qval		filename
         * @param 3		int			?
         * @param 4		qval		?
         * @param 5		int			?
         * @param 6		int			size
         * @param 7		int			?
         * @param 8		qval		?
         * @param 9		qval		?
         * @param 10	qval		?
         *
         *
         * after sending "##>\r\n":
         * +FSFR:    512,0,-587442163,<data>
         *
         * @param 1		int			packet size
         * @param 2		int			packet number
         * @param 3		int			?
         *
        """


        #while (res.endswith("#OK#"))
        #    self.comm.write("##>\r\n")
        #    res=self.comm._read()

        res=[]

        #line=self.comm.getcleanline()
        #self.log("mycleanline: " + str(line))
        """
        if line==fullline:
            line=self.getcleanline()
            self.log("cleanline: " + str(line))
        while line!="OK" and line:
            if line=="ERROR":
                if not ignoreerror:
                    raise ATError
            elif line.endswith("#OK#"):
                if autocontinue:
                    res.append(line[:-4])
                    self.write(str("##>\r\n"))
                else:
                    res.append(line)
                    break
                try:
                    self.readatresponse(ignoreerror)
                except CommTimeout:
                    raise
            else:
                res.append(line)
            line=self.getcleanline()
            self.log("cleanline: " + str(line))

        self.log('res: ' + str(res))
        """
        #data=cStringIO.StringIO()
        """
        req=p_brew.readfilerequest()
        req.filename=file

        res=self.sendbrewcommand(req, p_brew.readfileresponse)

        filesize=res.filesize
        data.write(res.data)

        counter=0
        while res.thereismore:
            counter+=1
            if counter>0xff:
                counter=0x01
            if counter%5==0:
                self.progress(data.tell(), filesize, desc)
            req=p_brew.readfileblockrequest()
            req.blockcounter=counter
            res=self.sendbrewcommand(req, p_brew.readfileblockresponse)
            data.write(res.data)

        self.progress(1,1,desc)

        data=data.getvalue()

        # give the download speed if we got a non-trivial amount of data
        end=time.time()
        if end-start>3:
            self.log("Read "+`filesize`+" bytes at "+`int(filesize/(end-start))`+" bytes/second")

        if filesize!=len(data):
            self.log("expected size "+`filesize`+"  actual "+`len(data)`)
            self.raisecommsexception("Brew file read is incorrect size", common.CommsDataCorruption)
        if use_cache and node:
            file_cache.add(file, node.get('date', [0])[0], data)
        """
        #return data

    def getfundamentals(self, results):
        """Gets information fundamental to interoperating with the phone and UI.

        Currently this is:

          - 'uniqueserial'     a unique serial number representing the phone
          - 'groups'           the phonebook groups
          - 'wallpaper-index'  map index numbers to names
          - 'ringtone-index'   map index numbers to ringtone names

        This method is called before we read the phonebook data or before we
        write phonebook data.
        """
        self.comm.setbaudrate(115200)

        # use a hash of ESN and other stuff (being paranoid)
        self.log("Retrieving fundamental phone information")
        self.log("Reading phone serial number")
        results['uniqueserial']=sha.new(self.get_esn()).hexdigest()

        req=self.protocolclass.inforequest()
        res=self.sendpbcommand(req, self.protocolclass.inforesponse)

        """ static groups, dunno how to read """
        groups={}
        groups[0] = {'name': 'unassigned'}
        groups[1] = {'name': 'family'}
        groups[2] = {'name': 'office'}
        groups[3] = {'name': 'friends'}
        groups[4] = {'name': 'others'}

        results['groups']=groups
        self.log("Fundamentals retrieved")
        return results

    def getwallpapers(self, result):
        wallpapers = {}
        return wallpapers

    def getcalendar(self, result):
        entries = {}
        self.log("Getting calendar entries")

        # get number of entries
        req=self.protocolclass.organizerinforequest()
        res=self.sendpbcommand(req, self.protocolclass.organizerinforesponse)

        events = res[0].entries

        req=self.protocolclass.eventrequest()
        cal_cnt=0
        for slot in range(events):
            req.slot=slot
            res=self.sendpbcommand(req,self.protocolclass.eventresponse)
            if len(res) > 0:
                self.progress(slot+1, events, res[0].eventname)

                # build a calendar entry
                entry=bpcalendar.CalendarEntry()

                # start time date
                entry.start=(res[0].start_year, res[0].start_month, res[0].start_day, res[0].start_hour, res[0].start_minute)

                if res[0].end_year:
                    # valid end time
                    entry.end=(res[0].end_year, res[0].end_month, res[0].end_day, res[0].end_hour, res[0].end_minute)
                else:
                    entry.end=entry.start

                # description[location]
                entry.desc_loc=res[0].eventname

                try:
                    alarm=self.__cal_alarm_values[res[0].alarm]
                except:
                    alarm=None
                entry.alarm=alarm

                # update calendar dict
                entries[entry.id]=entry
                cal_cnt += 1

        result['calendar']=entries
        return result


    def getphonebook(self, result):
        pbook={}
        count=0

        #req=self.protocolclass.phonebookmemoryrequest()
        #res=self.sendpbcommand(req, self.protocolclass.phonebookmemoryresponse)
        self.comm.sendatcommand("+CPBS=\"ME\"", ignoreerror=False)

        # get count of phone book entries
        req=self.protocolclass.phonebookinforequest()
        res=self.sendpbcommand(req, self.protocolclass.phonebookinforesponse)
        entries=res[0].usedslots

        req=self.protocolclass.phonebookslotrequest()
        name=""
        surname=""
        for slot in range(1, entries+1):
            req.slot=slot
            res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse) #, fixup=self.pblinerepair)
            if len(res) > 0:
                name=res[0].entry.name
                name=name[1:len(name)-1]
                surname=res[0].entry.surname
                surname=surname[1:len(surname)-1]
                self.log('Slot #' + `slot` + ": " + name + ' ' + surname)

                entry=self.extractphonebookentry(res[0].entry, result)
                pbook[count]=entry
                count+=1
            else:
                name=""
                surname=""

            self.progress(slot, entries, 'Reading entry %(slot)d: %(name)s'%{ 'slot': slot, 'name': surname + ', ' + name })

        result['phonebook']=pbook
        cats=[]
        for i in result['groups']:
            if result['groups'][i]['name']!='Unassigned':
                cats.append(result['groups'][i]['name'])
        result['categories']=cats
        print "returning keys",result.keys()

        return pbook


    def extractphonebookentry(self, entry, fundamentals):
        res={}

        res['serials']=[ {'sourcetype': self.serialsname,
                          'slot': entry.slot,
                          'sourceuniqueid': fundamentals['uniqueserial']} ]
        # only one name
        #res['names']=[ {'first': entry.name, 'last': entry.surname} ]
        res['names']=[ {'full': entry.name[1:len(entry.name)-1].decode("utf8") + ' ' + entry.surname[1:len(entry.surname)-1].decode("utf8")} ]

        # only one category
        cat=fundamentals['groups'].get(entry.group, {'name': "Unassigned"})['name']
        if cat!="Unassigned":
            res['categories']=[ {'category': cat} ]
        # only one email
        if len(entry.email):
            res['emails']=[ {'email': entry.email} ]
        # only one url
        if len(entry.url):
            res['urls']=[ {'url': entry.url} ]
        # separate the following processing into methods so subclass can
        # customize them
        self._extractphonebook_numbers(entry, fundamentals, res)
        #self._extractphonebook_ringtone(entry, fundamentals, res)
        #self._extractphonebook_wallpaper(entry, fundamentals, res)

        # We don't have a place to put these
        # print entry.name, entry.birthday
        # print entry.name, entry.timestamp

        return res


    def _extractphonebook_numbers(self, entry, fundamentals, res):
        """Extract and build phone numbers"""
        res['numbers']=[]
        secret=0

        #speeddialtype=entry.speeddial
        numberindex=0
        for type in self.numbertypetab:
            if len(entry.numbers[numberindex].number):
                numhash={'number': entry.numbers[numberindex].number, 'type': type }
                #if entry.numbers[numberindex].secret==1:
                #    secret=1
                #if speeddialtype==numberindex:
                #    numhash['speeddial']=entry.uslot
                res['numbers'].append(numhash)

            numberindex+=1

        # Field after each number is secret flag.  Setting secret on
        # phone sets secret flag for every defined phone number
        #res['flags']=[ {'secret': secret} ]

    def getsms(self, result):
        storagetypes=("ME", "SM")
        sims={}

        for storage in storagetypes:
            req=self.protocolclass.select_message_storage_req()
            req.storage=storage
            res=self.sendpbcommand(req, self.protocolclass.select_message_storage_resp)

            self.log('storage: ' + req.storage + ': ' + str(res[0].used) + ' used / ' + str(res[0].total) + ' total')

            self.comm.sendatcommand("+CMGF=1", ignoreerror=False)
            self.comm.sendatcommand("+CSDH=1", ignoreerror=False)

            req=self.protocolclass.message_read_req()
            usedslots = res[0].used

            for slot in range(1, usedslots+1):
                req.slot = slot

                self.progress(slot, usedslots+1, 'Reading memory %(storage)s, message %(slot)d'%{ 'storage': storage, 'slot': slot })

                try:
                    res2=self.sendpbcommand(req, self.protocolclass.message_read_resp, getasone=True)
                #except:
                #    pass

                    _sms=sms.SMSEntry()
                    if res2[0].msg_type==self.protocolclass.SMS_MSG_REC_UNREAD or \
                       res2[0].msg_type==self.protocolclass.SMS_MSG_REC_READ:
                        # unread/read inbox
                        _sms._from=res2[0].sender
                        _sms.folder=sms.SMSEntry.Folder_Inbox
                        _sms.read=res2[0].msg_type==self.protocolclass.SMS_MSG_REC_READ
                    elif res2[0].msg_type==self.protocolclass.SMS_MSG_STO_SENT:
                        # outbox
                        #_sms.add_recipient(res[0].address)
                        _sms.folder=sms.SMSEntry.Folder_Sent
                    elif res2[0].msg_type==self.protocolclass.SMS_MSG_STO_UNSENT:
                        # saved
                        _sms.folder=sms.SMSEntry.Folder_Saved
                        #_sms.add_recipient(res[0].address)
                    else:
                        self.log('Unknown message type: %s' % res2[0].msg_type)
                        _sms=None

                    if _sms:
                        if res2[0].timestamp:
                            _sms.datetime=res2[0].timestamp
                        _sms.text=str(res2[0].rest).decode("utf8")
                        #_sms.text=res2[0].rest
                        result['sms'] = []
                        sims[_sms.id]=_sms
                except:
                    pass
                #except:
                #    if __debug__:
                #        raise

            result['canned_msg']=[]
            result['sms']=sims

        return result

    def get_firmware_version(self):
        req=self.protocolclass.firmwareinforeq()
        res=self.sendpbcommand(req, self.protocolclass.firmwareinforesp)
        return res[0].firmware


    def get_signal_quality(self):
        req=self.protocolclass.signalreq()
        res=self.sendpbcommand(req, self.protocolclass.signalresp)
        #return res[0].signal
        if res[0].signal is not None:
            return str(100*int(res[0].signal)/31)+'%'



class Profile(com_samsung.Profile):

    serialsname='sghd600'

    WALLPAPER_WIDTH=128
    WALLPAPER_HEIGHT=128
    MAX_WALLPAPER_BASENAME_LENGTH=19
    WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ."
##    WALLPAPER_CONVERT_FORMAT="bmp"

    usbids=( ( 0x04e8, 0x663e, 2),)

    WALLPAPER_CONVERT_FORMAT="jpg"
    MAX_RINGTONE_BASENAME_LENGTH=19
    RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ."
    RINGTONE_LIMITS= {
        'MAXSIZE': 30000
    }
    # use for auto-detection
    phone_manufacturer='SAMSUNG'
    phone_model='SAMSUNG SGH-D600'

    def __init__(self):
        com_samsung.Profile.__init__(self)

    """
    _supportedsyncs=(
        ('phonebook', 'read', None),  # all phonebook reading
        ('phonebook', 'write', 'OVERWRITE'),  # only overwriting phonebook
        ('calendar', 'read', None),   # all calendar reading
        ('calendar', 'write', 'OVERWRITE'),   # only overwriting calendar
        ('ringtone', 'read', None),   # all ringtone reading
        ('ringtone', 'write', 'OVERWRITE'),
        ('wallpaper', 'read', None),  # all wallpaper reading
        ('wallpaper', 'write', 'OVERWRITE'),
        ('memo', 'read', None),     # all memo list reading DJP
        ('memo', 'write', 'OVERWRITE'),  # all memo list writing DJP
        ('todo', 'read', None),     # all todo list reading DJP
        ('todo', 'write', 'OVERWRITE'),  # all todo list writing DJP
        ('sms', 'read', None),     # all SMS list reading DJP
        )
    """

    _supportedsyncs=(
        ('phonebook', 'read', None),  # all phonebook reading
        ('phonebook', 'write', 'OVERWRITE'),  # only overwriting phonebook
        ('calendar', 'read', None),   # all calendar reading
        ('calendar', 'write', 'OVERWRITE'),   # only overwriting calendar
        ('todo', 'read', None),     # all todo list reading
        ('todo', 'write', 'OVERWRITE'),   # only overwriting calendar
        ('wallpaper', 'read', None),
        ('wallpaper', 'write', 'OVERWRITE'),
        ('sms', 'read', None),
    )

    if __debug__:
        _supportedsyncs+=(('sms', 'write', 'OVERWRITE'),)

    def convertphonebooktophone(self, helper, data):
        return data

    __audio_ext={ 'MIDI': 'mid', 'PMD': 'pmd', 'QCP': 'pmd' }
    def QueryAudio(self, origin, currentextension, afi):
        # we don't modify any of these
        if afi.format in ("MIDI", "PMD", "QCP"):
            for k,n in self.RINGTONE_LIMITS.items():
                setattr(afi, k, n)
            return currentextension, afi
        d=self.RINGTONE_LIMITS.copy()
        d['format']='QCP'
        return ('pmd', fileinfo.AudioFileInfo(afi, **d))

    imageorigins={}
    imageorigins.update(common.getkv(com_samsung.Profile.stockimageorigins, "images"))

    imagetargets={}
    imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "wallpaper",
                                      {'width': 128, 'height': 128, 'format': "PNG"}))
    imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "fullscreen",
                                      {'width': 128, 'height': 160, 'format': "PNG"}))
    imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "pictureid",
                                      {'width': 96, 'height': 96, 'format': "JPEG"}))

    def GetImageOrigins(self):
        # Note: only return origins that you can write back to the phone
        return self.imageorigins

    def GetTargetsForImageOrigin(self, origin):
        # right now, supporting just 'images' origin
        if origin=='images':
            return self.imagetargets