Module apk
[hide private]
[frames] | no frames]

Source Code for Module apk

  1  # This file is part of Androguard. 
  2  # 
  3  # Copyright (C) 2010, Anthony Desnos <desnos at t0t0.org> 
  4  # All rights reserved. 
  5  # 
  6  # Androguard is free software: you can redistribute it and/or modify 
  7  # it under the terms of the GNU Lesser General Public License as published by 
  8  # the Free Software Foundation, either version 3 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # Androguard is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU Lesser General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU Lesser General Public License 
 17  # along with Androguard.  If not, see <http://www.gnu.org/licenses/>. 
 18   
 19  import bytecode 
 20   
 21  import misc 
 22  from bytecode import SV, SVs 
 23  from dvm_permissions import DVM_PERMISSIONS 
 24   
 25  import sys, re, types, string, zipfile, StringIO 
 26  from collections import namedtuple 
 27  from struct import pack, unpack, calcsize 
 28  from xml.dom import minidom 
 29   
 30  try : 
 31      import chilkat 
 32      ZIPMODULE = 0 
 33      # UNLOCK : change it with your valid key ! 
 34      CHILKAT_KEY = "testme" 
 35  except ImportError : 
 36      ZIPMODULE = 1 
 37   
 38  ################################################### CHILKAT ZIP FORMAT ##################################################### 
39 -class ChilkatZip :
40 - def __init__(self, raw) :
41 self.files = [] 42 self.zip = chilkat.CkZip() 43 44 self.zip.UnlockComponent( CHILKAT_KEY ) 45 46 self.zip.OpenFromMemory( raw, len(raw) ) 47 48 filename = chilkat.CkString() 49 e = self.zip.FirstEntry() 50 while e != None : 51 e.get_FileName(filename) 52 self.files.append( filename.getString() ) 53 e = e.NextEntry()
54
55 - def namelist(self) :
56 return self.files
57
58 - def read(self, elem) :
59 e = self.zip.GetEntryByName( elem ) 60 s = chilkat.CkByteData() 61 62 e.Inflate( s ) 63 return s.getBytes()
64 65 ######################################################## APK FORMAT ########################################################
66 -class APK :
67 """APK manages apk file format"""
68 - def __init__(self, filename, raw=False) :
69 """ 70 @param filename : specify the path of the file, or raw data 71 @param raw : specify (boolean) if the filename is a path or raw data 72 """ 73 self.filename = filename 74 75 self.xml = {} 76 self.package = "" 77 self.androidversion = {} 78 self.permissions = [] 79 self.validAPK = False 80 81 if raw == True : 82 self.__raw = filename 83 else : 84 fd = open( filename, "rb" ) 85 self.__raw = fd.read() 86 fd.close() 87 88 if ZIPMODULE == 0 : 89 self.zip = ChilkatZip( self.__raw ) 90 else : 91 self.zip = zipfile.ZipFile( StringIO.StringIO( self.__raw ) ) 92 93 # CHECK if there is only one embedded file 94 #self._reload_apk() 95 96 for i in self.zip.namelist() : 97 if i == "AndroidManifest.xml" : 98 self.xml[i] = minidom.parseString( AXMLPrinter( self.zip.read( i ) ).getBuff() ) 99 100 self.package = self.xml[i].documentElement.getAttribute( "package" ) 101 self.androidversion["Code"] = self.xml[i].documentElement.getAttribute( "android:versionCode" ) 102 self.androidversion["Name"] = self.xml[i].documentElement.getAttribute( "android:versionName") 103 104 for item in self.xml[i].getElementsByTagName('uses-permission') : 105 self.permissions.append( str( item.getAttribute("android:name") ) ) 106 107 self.validAPK = True
108
109 - def is_valid_APK(self) :
110 return self.validAPK
111 112 #def _reload_apk(self) : 113 # if len(files) == 1 : 114 # if ".apk" in files[0] : 115 # self.__raw = self.zip.read( files[0] ) 116 # if ZIPMODULE == 0 : 117 # self.zip = ChilkatZip( self.__raw ) 118 # else : 119 # self.zip = zipfile.ZipFile( StringIO.StringIO( self.__raw ) ) 120
121 - def get_filename(self) :
122 """ 123 Return the filename of the APK 124 """ 125 return self.filename
126
127 - def get_package(self) :
128 """ 129 Return the name of the package 130 """ 131 return self.package
132
133 - def get_androidversion_code(self) :
134 """ 135 Return the android version code 136 """ 137 return self.androidversion["Code"]
138
139 - def get_androidversion_name(self) :
140 """ 141 Return the android version name 142 """ 143 return self.androidversion["Name"]
144
145 - def get_files(self) :
146 """ 147 Return the files inside the APK 148 """ 149 return self.zip.namelist()
150
151 - def get_files_types(self) :
152 """ 153 Return the files inside the APK with their types (by using python-magic) 154 """ 155 try : 156 import magic 157 except ImportError : 158 return {} 159 160 l = {} 161 m = magic.Magic() 162 for i in self.get_files() : 163 l[ i ] = m.from_buffer( self.zip.read( i ) ) 164 165 return l
166
167 - def get_raw(self) :
168 """ 169 Return raw bytes of the APK 170 """ 171 return self.__raw
172
173 - def get_dex(self) :
174 """ 175 Return the raw data of the classes dex file 176 """ 177 try : 178 return self.zip.read("classes.dex") 179 except KeyError : 180 return ""
181
182 - def get_elements(self, tag_name, attribute) :
183 """ 184 Return elements in xml files which match with the tag name and the specific attribute 185 186 @param tag_name : a string which specify the tag name 187 @param attribute : a string which specify the attribute 188 """ 189 l = [] 190 for i in self.xml : 191 for item in self.xml[i].getElementsByTagName(tag_name) : 192 value = item.getAttribute(attribute) 193 194 if len(value) > 0 and value[0] == "." : 195 value = self.package + value 196 197 l.append( str( value ) ) 198 return l
199
200 - def get_element(self, tag_name, attribute) :
201 """ 202 Return element in xml files which match with the tag name and the specific attribute 203 204 @param tag_name : a string which specify the tag name 205 @param attribute : a string which specify the attribute 206 """ 207 l = [] 208 for i in self.xml : 209 for item in self.xml[i].getElementsByTagName(tag_name) : 210 value = item.getAttribute(attribute) 211 212 if len(value) > 0 : 213 return value 214 return None
215
216 - def get_activities(self) :
217 """ 218 Return the android:name attribute of all activities 219 """ 220 return self.get_elements("activity", "android:name")
221
222 - def get_services(self) :
223 """ 224 Return the android:name attribute of all services 225 """ 226 return self.get_elements("service", "android:name")
227
228 - def get_receivers(self) :
229 """ 230 Return the android:name attribute of all receivers 231 """ 232 return self.get_elements("receiver", "android:name")
233
234 - def get_providers(self) :
235 """ 236 Return the android:name attribute of all providers 237 """ 238 return self.get_elements("provider", "android:name")
239
240 - def get_permissions(self) :
241 """ 242 Return permissions 243 """ 244 return self.permissions
245
246 - def get_details_permissions(self) :
247 """ 248 Return permissions with details 249 """ 250 l = {} 251 252 for i in self.permissions : 253 perm = i 254 pos = i.rfind(".") 255 256 if pos != -1 : 257 perm = i[pos+1:] 258 259 try : 260 l[ i ] = DVM_PERMISSIONS["MANIFEST_PERMISSION"][ perm ] 261 except KeyError : 262 l[ i ] = "Unknown permission from android reference" 263 264 return l
265
266 - def get_min_sdk_version(self) :
267 """ 268 Return the android:minSdkVersion attribute 269 """ 270 return self.get_element( "uses-sdk", "android:minSdkVersion" )
271
272 - def get_target_sdk_version(self) :
273 """ 274 Return the android:targetSdkVersion attribute 275 """ 276 return self.get_element( "uses-sdk", "android:targetSdkVersion" )
277
278 - def get_libraries(self) :
279 """ 280 Return the android:name attributes for libraries 281 """ 282 return self.get_elements( "uses-library", "android:name" )
283
284 - def show(self) :
285 print "FILES : ", self.get_files_types() 286 287 print "PERMISSIONS : ", self.get_details_permissions() 288 print "ACTIVITIES : ", self.get_activities() 289 print "SERVICES : ", self.get_services() 290 print "RECEIVERS : ", self.get_receivers() 291 print "PROVIDERS : ", self.get_providers()
292 293 ######################################################## AXML FORMAT ######################################################## 294 # Translated from http://code.google.com/p/android4me/source/browse/src/android/content/res/AXmlResourceParser.java
295 -class StringBlock :
296 - def __init__(self, buff) :
297 buff.read( 4 ) 298 299 self.chunkSize = SV( '<L', buff.read( 4 ) ) 300 self.stringCount = SV( '<L', buff.read( 4 ) ) 301 self.styleOffsetCount = SV( '<L', buff.read( 4 ) ) 302 303 # unused value ? 304 buff.read(4) # ? 305 306 self.stringsOffset = SV( '<L', buff.read( 4 ) ) 307 self.stylesOffset = SV( '<L', buff.read( 4 ) ) 308 309 self.m_stringOffsets = [] 310 self.m_styleOffsets = [] 311 self.m_strings = [] 312 self.m_styles = [] 313 314 for i in range(0, self.stringCount.get_value()) : 315 self.m_stringOffsets.append( SV( '<L', buff.read( 4 ) ) ) 316 317 for i in range(0, self.styleOffsetCount.get_value()) : 318 self.m_stylesOffsets.append( SV( '<L', buff.read( 4 ) ) ) 319 320 size = self.chunkSize.get_value() - self.stringsOffset.get_value() 321 if self.stylesOffset.get_value() != 0 : 322 size = self.stylesOffset.get_value() - self.stringsOffset.get_value() 323 324 # FIXME 325 if (size%4) != 0 : 326 pass 327 328 for i in range(0, size / 4) : 329 self.m_strings.append( SV( '=L', buff.read( 4 ) ) ) 330 331 if self.stylesOffset.get_value() != 0 : 332 size = self.chunkSize.get_value() - self.stringsOffset.get_value() 333 334 # FIXME 335 if (size%4) != 0 : 336 pass 337 338 for i in range(0, size / 4) : 339 self.m_styles.append( SV( '=L', buff.read( 4 ) ) )
340
341 - def getRaw(self, idx) :
342 if idx < 0 or self.m_stringOffsets == [] or idx >= len(self.m_stringOffsets) : 343 return None 344 345 offset = self.m_stringOffsets[ idx ].get_value() 346 length = self.getShort(self.m_strings, offset) 347 348 data = "" 349 350 while length > 0 : 351 offset += 2 352 # Unicode character 353 data += unichr( self.getShort(self.m_strings, offset) ) 354 355 # FIXME 356 if data[-1] == "&" : 357 data = data[:-1] 358 359 length -= 1 360 361 return data
362
363 - def getShort(self, array, offset) :
364 value = array[offset/4].get_value() 365 if ((offset%4)/2) == 0 : 366 return value & 0xFFFF 367 else : 368 return value >> 16
369 370 ATTRIBUTE_IX_NAMESPACE_URI = 0 371 ATTRIBUTE_IX_NAME = 1 372 ATTRIBUTE_IX_VALUE_STRING = 2 373 ATTRIBUTE_IX_VALUE_TYPE = 3 374 ATTRIBUTE_IX_VALUE_DATA = 4 375 ATTRIBUTE_LENGHT = 5 376 377 CHUNK_AXML_FILE = 0x00080003 378 CHUNK_RESOURCEIDS = 0x00080180 379 CHUNK_XML_FIRST = 0x00100100 380 CHUNK_XML_START_NAMESPACE = 0x00100100 381 CHUNK_XML_END_NAMESPACE = 0x00100101 382 CHUNK_XML_START_TAG = 0x00100102 383 CHUNK_XML_END_TAG = 0x00100103 384 CHUNK_XML_TEXT = 0x00100104 385 CHUNK_XML_LAST = 0x00100104 386 387 START_DOCUMENT = 0 388 END_DOCUMENT = 1 389 START_TAG = 2 390 END_TAG = 3 391 TEXT = 4
392 -class AXMLParser :
393 - def __init__(self, raw_buff) :
394 self.reset() 395 396 self.buff = bytecode.BuffHandle( raw_buff ) 397 398 self.buff.read(4) 399 self.buff.read(4) 400 401 self.sb = StringBlock( self.buff ) 402 403 self.m_resourceIDs = [] 404 self.m_prefixuri = {} 405 self.m_uriprefix = {} 406 self.m_prefixuriL = []
407
408 - def reset(self) :
409 self.m_event = -1 410 self.m_lineNumber = -1 411 self.m_name = -1 412 self.m_namespaceUri = -1 413 self.m_attributes = [] 414 self.m_idAttribute = -1 415 self.m_classAttribute = -1 416 self.m_styleAttribute = -1
417
418 - def next(self) :
419 self.doNext() 420 return self.m_event
421
422 - def doNext(self) :
423 if self.m_event == END_DOCUMENT : 424 return 425 426 event = self.m_event 427 428 self.reset() 429 while 1 : 430 chunkType = -1 431 432 # Fake END_DOCUMENT event. 433 if event == END_TAG : 434 pass 435 436 # START_DOCUMENT 437 if event == START_DOCUMENT : 438 chunkType = CHUNK_XML_START_TAG 439 else : 440 if self.buff.end() == True : 441 self.m_event = END_DOCUMENT 442 break 443 chunkType = SV( '<L', self.buff.read( 4 ) ).get_value() 444 445 446 if chunkType == CHUNK_RESOURCEIDS : 447 chunkSize = SV( '<L', self.buff.read( 4 ) ).get_value() 448 # FIXME 449 if chunkSize < 8 or chunkSize%4 != 0 : 450 raise("ooo") 451 452 for i in range(0, chunkSize/4-2) : 453 self.m_resourceIDs.append( SV( '<L', self.buff.read( 4 ) ) ) 454 455 continue 456 457 # FIXME 458 if chunkType < CHUNK_XML_FIRST or chunkType > CHUNK_XML_LAST : 459 raise("ooo") 460 461 # Fake START_DOCUMENT event. 462 if chunkType == CHUNK_XML_START_TAG and event == -1 : 463 self.m_event = START_DOCUMENT 464 break 465 466 self.buff.read( 4 ) #/*chunkSize*/ 467 lineNumber = SV( '<L', self.buff.read( 4 ) ).get_value() 468 self.buff.read( 4 ) #0xFFFFFFFF 469 470 if chunkType == CHUNK_XML_START_NAMESPACE or chunkType == CHUNK_XML_END_NAMESPACE : 471 if chunkType == CHUNK_XML_START_NAMESPACE : 472 prefix = SV( '<L', self.buff.read( 4 ) ).get_value() 473 uri = SV( '<L', self.buff.read( 4 ) ).get_value() 474 475 self.m_prefixuri[ prefix ] = uri 476 self.m_uriprefix[ uri ] = prefix 477 self.m_prefixuriL.append( (prefix, uri) ) 478 else : 479 self.buff.read( 4 ) 480 self.buff.read( 4 ) 481 (prefix, uri) = self.m_prefixuriL.pop() 482 #del self.m_prefixuri[ prefix ] 483 #del self.m_uriprefix[ uri ] 484 485 continue 486 487 488 self.m_lineNumber = lineNumber 489 490 if chunkType == CHUNK_XML_START_TAG : 491 self.m_namespaceUri = SV( '<L', self.buff.read( 4 ) ).get_value() 492 self.m_name = SV( '<L', self.buff.read( 4 ) ).get_value() 493 494 # FIXME 495 self.buff.read( 4 ) #flags 496 497 attributeCount = SV( '<L', self.buff.read( 4 ) ).get_value() 498 self.m_idAttribute = (attributeCount>>16) - 1 499 attributeCount = attributeCount & 0xFFFF 500 self.m_classAttribute = SV( '<L', self.buff.read( 4 ) ).get_value() 501 self.m_styleAttribute = (self.m_classAttribute>>16) - 1 502 503 self.m_classAttribute = (self.m_classAttribute & 0xFFFF) - 1 504 505 for i in range(0, attributeCount*ATTRIBUTE_LENGHT) : 506 self.m_attributes.append( SV( '<L', self.buff.read( 4 ) ).get_value() ) 507 508 for i in range(ATTRIBUTE_IX_VALUE_TYPE, len(self.m_attributes), ATTRIBUTE_LENGHT) : 509 self.m_attributes[i] = (self.m_attributes[i]>>24) 510 511 self.m_event = START_TAG 512 break 513 514 if chunkType == CHUNK_XML_END_TAG : 515 self.m_namespaceUri = SV( '<L', self.buff.read( 4 ) ).get_value() 516 self.m_name = SV( '<L', self.buff.read( 4 ) ).get_value() 517 self.m_event = END_TAG 518 break 519 520 if chunkType == CHUNK_XML_TEXT : 521 self.m_name = SV( '<L', self.buff.read( 4 ) ).get_value() 522 523 # FIXME 524 self.buff.read( 4 ) #? 525 self.buff.read( 4 ) #? 526 527 self.m_event = TEXT 528 break
529
530 - def getPrefixByUri(self, uri) :
531 try : 532 return self.m_uriprefix[ uri ] 533 except KeyError : 534 return -1
535
536 - def getPrefix(self) :
537 try : 538 return self.sb.getRaw(self.m_prefixuri[ self.m_namespaceUri ]) 539 except KeyError : 540 return ""
541
542 - def getName(self) :
543 if self.m_name == -1 or (self.m_event != START_TAG and self.m_event != END_TAG) : 544 return "" 545 546 return self.sb.getRaw(self.m_name)
547
548 - def getText(self) :
549 if self.m_name == -1 or self.m_event != TEXT : 550 return "" 551 552 return self.sb.getRaw(self.m_name)
553
554 - def getNamespacePrefix(self, pos) :
555 prefix = self.m_prefixuriL[ pos ][0] 556 return self.sb.getRaw( prefix )
557
558 - def getNamespaceUri(self, pos) :
559 uri = self.m_prefixuriL[ pos ][1] 560 return self.sb.getRaw( uri )
561
562 - def getNamespaceCount(self, pos) :
563 pass
564
565 - def getAttributeOffset(self, index) :
566 # FIXME 567 if self.m_event != START_TAG : 568 raise("Current event is not START_TAG.") 569 570 offset = index * 5 571 # FIXME 572 if offset >= len(self.m_attributes) : 573 raise("Invalid attribute index") 574 575 return offset
576
577 - def getAttributeCount(self) :
578 if self.m_event != START_TAG : 579 return -1 580 581 return len(self.m_attributes) / ATTRIBUTE_LENGHT
582
583 - def getAttributePrefix(self, index) :
584 offset = self.getAttributeOffset(index) 585 uri = self.m_attributes[offset+ATTRIBUTE_IX_NAMESPACE_URI] 586 587 prefix = self.getPrefixByUri( uri ) 588 if prefix == -1 : 589 return "" 590 591 return self.sb.getRaw( prefix )
592
593 - def getAttributeName(self, index) :
594 offset = self.getAttributeOffset(index) 595 name = self.m_attributes[offset+ATTRIBUTE_IX_NAME] 596 597 if name == -1 : 598 return "" 599 600 return self.sb.getRaw( name )
601
602 - def getAttributeValueType(self, index) :
603 offset = self.getAttributeOffset(index) 604 return self.m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE]
605
606 - def getAttributeValueData(self, index) :
607 offset = self.getAttributeOffset(index) 608 return self.m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]
609
610 - def getAttributeValue(self, index) :
611 offset = self.getAttributeOffset(index) 612 valueType = self.m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE] 613 if valueType == TYPE_STRING : 614 valueString = self.m_attributes[offset+ATTRIBUTE_IX_VALUE_STRING] 615 return self.sb.getRaw( valueString ) 616 # WIP 617 return ""
618 #int valueData=m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]; 619 #return TypedValue.coerceToString(valueType,valueData); 620 621 TYPE_ATTRIBUTE = 2 622 TYPE_DIMENSION = 5 623 TYPE_FIRST_COLOR_INT = 28 624 TYPE_FIRST_INT = 16 625 TYPE_FLOAT = 4 626 TYPE_FRACTION = 6 627 TYPE_INT_BOOLEAN = 18 628 TYPE_INT_COLOR_ARGB4 = 30 629 TYPE_INT_COLOR_ARGB8 = 28 630 TYPE_INT_COLOR_RGB4 = 31 631 TYPE_INT_COLOR_RGB8 = 29 632 TYPE_INT_DEC = 16 633 TYPE_INT_HEX = 17 634 TYPE_LAST_COLOR_INT = 31 635 TYPE_LAST_INT = 31 636 TYPE_NULL = 0 637 TYPE_REFERENCE = 1 638 TYPE_STRING = 3 639 640 RADIX_MULTS = [ 0.00390625, 3.051758E-005, 1.192093E-007, 4.656613E-010 ] 641 DIMENSION_UNITS = [ "px","dip","sp","pt","in","mm","","" ] 642 FRACTION_UNITS = [ "%","%p","","","","","","" ] 643 644 COMPLEX_UNIT_MASK = 15 645
646 -class AXMLPrinter :
647 - def __init__(self, raw_buff) :
648 self.axml = AXMLParser( raw_buff ) 649 self.xmlns = False 650 651 self.buff = "" 652 653 while 1 : 654 _type = self.axml.next() 655 # print "tagtype = ", _type 656 657 if _type == START_DOCUMENT : 658 self.buff += "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 659 elif _type == START_TAG : 660 self.buff += "<%s%s\n" % ( self.getPrefix( self.axml.getPrefix() ), self.axml.getName() ) 661 662 # FIXME : use namespace 663 if self.xmlns == False : 664 self.buff += "xmlns:%s=\"%s\"\n" % ( self.axml.getNamespacePrefix( 0 ), self.axml.getNamespaceUri( 0 ) ) 665 self.xmlns = True 666 667 for i in range(0, self.axml.getAttributeCount()) : 668 self.buff += "%s%s=\"%s\"\n" % ( self.getPrefix( self.axml.getAttributePrefix(i) ), self.axml.getAttributeName(i), self.getAttributeValue( i ) ) 669 670 self.buff += ">\n" 671 672 elif _type == END_TAG : 673 self.buff += "</%s%s>\n" % ( self.getPrefix( self.axml.getPrefix() ), self.axml.getName() ) 674 675 elif _type == TEXT : 676 self.buff += "%s\n" % self.axml.getText() 677 678 elif _type == END_DOCUMENT : 679 break
680
681 - def getBuff(self) :
682 return self.buff.encode("utf-8")
683
684 - def getPrefix(self, prefix) :
685 if prefix == None or len(prefix) == 0 : 686 return "" 687 688 return prefix + ":"
689
690 - def getAttributeValue(self, index) :
691 _type = self.axml.getAttributeValueType(index) 692 _data = self.axml.getAttributeValueData(index) 693 694 #print _type, _data 695 if _type == TYPE_STRING : 696 return self.axml.getAttributeValue( index ) 697 698 if _type == TYPE_ATTRIBUTE : 699 return "?%s%08X" % (self.getPackage(_data), _data) 700 701 if _type == TYPE_REFERENCE : 702 return "@%s%08X" % (self.getPackage(_data), _data) 703 704 # WIP 705 if _type == TYPE_FLOAT : 706 return "%f" % unpack("=f", pack("=L", _data))[0] 707 708 if _type == TYPE_INT_HEX : 709 return "0x%08X" % _data 710 711 if _type == TYPE_INT_BOOLEAN : 712 if _data == 0 : 713 return "false" 714 return "true" 715 716 if _type == TYPE_DIMENSION : 717 return "%f%s" % (self.complexToFloat(_data), DIMENSION_UNITS[_data & COMPLEX_UNIT_MASK]) 718 719 if _type == TYPE_FRACTION : 720 return "%f%s" % (self.complexToFloat(_data), FRACTION_UNITS[_data & COMPLEX_UNIT_MASK]) 721 722 if _type >= TYPE_FIRST_COLOR_INT and _type <= TYPE_LAST_COLOR_INT : 723 return "#%08X" % _data 724 725 if _type >= TYPE_FIRST_INT and _type <= TYPE_LAST_INT : 726 return "%d" % misc.long2int( _data ) 727 728 return "<0x%X, type 0x%02X>" % (_data, _type)
729
730 - def complexToFloat(self, xcomplex) :
731 return (float)(xcomplex & 0xFFFFFF00)*RADIX_MULTS[(xcomplex>>4) & 3];
732
733 - def getPackage(self, id) :
734 if id >> 24 == 1 : 735 return "android:" 736 return ""
737