diff --git a/modules/classsearch b/modules/classsearch index 2e8323e..bddc79e 100755 --- a/modules/classsearch +++ b/modules/classsearch @@ -188,7 +188,7 @@ class classsearch(Module): if class_name is None and method_name is None: log.e(TAG, "You need to specify a class_name to search for!") - exit(2) + return -2 db_dir = prop.get_prop("Local", "db-dir") diff --git a/modules/frameworkdb b/modules/frameworkdb index f313729..3c00603 100755 --- a/modules/frameworkdb +++ b/modules/frameworkdb @@ -166,6 +166,21 @@ class frameworkdb(Module): # End XZ Compression # Zip Stuff + @classmethod + def file_in_zip(cls, zip_file, file_name): + + """Determine if file in ZIP""" + + cmd = ("unzip -t %s %s" % (zip_file, file_name)).split(' ') + + p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=False) + p.wait() + + if p.returncode == 0: + return True + else: + return False + @classmethod def extract_from_zip_to(cls, zip_file, extract_path, file_name=None): @@ -188,119 +203,125 @@ class frameworkdb(Module): # End Zip Stuff # Unpack related - def do_unpack(self, local_db): - - """Perform the unpack""" - - frameworks_dir = prop.get_prop("Local", "framework-dir") - sdk = prop.get_prop("Info", "sdk") - fwdb = FrameworkDb.FrameworkDb(local_db) - - for framework in fwdb.getFrameworks(): - - log.i(TAG, "Unframeworking '%s'..." % framework) + def do_unpack_fw(self, fwdb, framework, sdk, frameworks_dir): - framework_jar = "%s/%s.jar" % (frameworks_dir, framework) - framework_odex = "%s/%s.odex" % (frameworks_dir, framework) + """Do actual unpack""" + framework_jar = "%s/%s.jar" % (frameworks_dir, framework) + framework_odex = "%s/%s.odex" % (frameworks_dir, framework) - out_dir = "%s/%s" % (UNFRAMEWORKS_DIR, framework) - out_framework_dex = "%s/classes.dex" % out_dir - out_framework_meta = "%s/META-INF" % out_dir + out_dir = "%s/%s" % (UNFRAMEWORKS_DIR, framework) + out_framework_dex = "%s/classes.dex" % out_dir + out_framework_meta = "%s/META-INF" % out_dir - # There should be a JAR, but there might not be. - if isfile(framework_jar): + # There should be a JAR, but might not be + if isfile(framework_jar): - log.d(TAG, "Doing JAR mode") + log.d(TAG, "Trying JAR mode") + # There is a JAR, but is there a 'classs.dex' inside? + if self.file_in_zip(framework_jar, 'classes.dex'): - # There is a JAR, so unzip first. + log.d(TAG, "classes.dex found, trying to unpack") + # Unzip the classes.dex if self.extract_from_zip_to(framework_jar, out_dir) != 0: log.e(TAG, "Unable to extact ZIP '%s'" % framework_jar) # Mark Failed if self.reporting: self.report.add_failed(framework) - continue - - # Now, check for ODEX, and handle that. - if isfile(framework_odex): - - log.d(TAG, "Found ODEX files for this framework") - out, err, rtn = baksmali("-a %s -x -o %s -d %s %s" % - (sdk, out_dir, frameworks_dir, framework_odex)) - + # Unzip success, do unpack! + else: + log.d(TAG, "Unpacking DEX framework") + out, err, rtn = baksmali("-a %s -o %s %s" + % (sdk, out_dir, + out_framework_dex)) + # Error if rtn != 0: - log.e(TAG, "Error unpacking framework ODEX (%d)" % rtn) + log.e(TAG, "Error unpacking framework DEX (%d)" % rtn) - # Mark Failed + # Mark failure if self.reporting: self.report.add_failed(framework) - continue - - # Mark complete and add to report. - fwdb.markUnpacked(framework) - if self.reporting: - self.report.add_completed(framework) - - # Cleanup - rmtree(out_framework_meta, ignore_errors=True) - - # No ODEX, use classes.dex. - elif isfile(out_framework_dex): + # Success! + else: + fwdb.markUnpacked(framework) + rmtree(out_framework_meta, ignore_errors=True) + self.rmfile(out_framework_dex) + if self.reporting: + self.report.add_completed(framework) - log.d(TAG, "No ODEX, looking for classes.dex") - out, err, rtn = baksmali("-a %s -o %s %s" % - (sdk, out_dir, out_framework_dex)) + # JAR, no classes.dex, ODEX? + elif isfile(framework_odex): - if rtn != 0: - log.e(TAG, "Error unpacking framework DEX (%d)" % rtn) + log.d(TAG, "Unpacking ODEX framework") + out, err, rtn = baksmali("-a %s -x -o %s -d %s %s" % + (sdk, out_dir, frameworks_dir, framework_odex)) - # Mark Failed - if self.reporting: - self.report.add_failed(framework) - continue + # Mark failure + if rtn != 0: + log.e(TAG, "Error unpacking framework ODEX (%d)" % rtn) - # Mark complete and add to report. + # Mark Failed + if self.reporting: + self.report.add_failed(framework) + # Success! + else: fwdb.markUnpacked(framework) + rmtree(out_framework_meta, ignore_errors=True) if self.reporting: self.report.add_completed(framework) - # Cleanup - self.rmfile(out_framework_dex) - rmtree(out_framework_meta, ignore_errors=True) - - # No classes.dex or ODEX? - else: - log.w(TAG, "No ODEX or classes.dex found?") - continue + # None of the above, another error/continue else: - # Without a JAR, we're forced to just use the ODEX. - if not isfile(framework_odex): - log.e(TAG, "No JAR or ODEX, how is this even possible?") + log.w(TAG, "No ODEX or classes.dex in JAR!") - # Mark Failed - if self.reporting: - self.report.add_failed(framework) - continue + # Mark Failed + fwdb.markUnpacked(framework) + if self.reporting: + self.report.add_completed(framework) - log.d(TAG, "Found ODEX files for this framework") - out, err, rtn = baksmali("-a %s -x -o %s -d %s %s" % - (sdk, out_dir, frameworks_dir, framework_odex)) + # Ok, no JAR. How about an ODEX? + elif isfile(framework_odex): - if rtn != 0: - log.e(TAG, "Error unpacking framework (%d)" % rtn) + log.d(TAG, "Unpacking ODEX framework") + out, err, rtn = baksmali("-a %s -x -o %s -d %s %s" % + (sdk, out_dir, frameworks_dir, framework_odex)) - # Mark Failed - if self.reporting: - self.report.add_failed(framework) - continue + # Mark failure + if rtn != 0: + log.e(TAG, "Error unpacking framework ODEX (%d)" % rtn) - # Mark complete and add to report. + # Mark Failed + if self.reporting: + self.report.add_failed(framework) + # Success! + else: fwdb.markUnpacked(framework) + rmtree(out_framework_meta, ignore_errors=True) if self.reporting: self.report.add_completed(framework) - # Cleanup$ - rmtree(out_framework_meta, ignore_errors=True) + # Neither + else: + log.e(TAG, "No JAR or ODEX!") + + # Mark Failed + if self.reporting: + self.report.add_failed(framework) + + return 0 + + def do_unpack(self, local_db): + + """Perform the unpack""" + + frameworks_dir = prop.get_prop("Local", "framework-dir") + sdk = prop.get_prop("Info", "sdk") + fwdb = FrameworkDb.FrameworkDb(local_db) + + for framework in fwdb.getFrameworks(): + + log.i(TAG, "Unframeworking '%s'..." % framework) + self.do_unpack_fw(fwdb, framework, sdk, frameworks_dir) # If reporting is enabled, show it. if self.reporting: diff --git a/modules/frameworkdexdb b/modules/frameworkdexdb index 0553d6e..21d7eca 100755 --- a/modules/frameworkdexdb +++ b/modules/frameworkdexdb @@ -207,20 +207,33 @@ class frameworkdexdb(Module): jar_name = "%s/%s.jar" % (self.frameworks_dir, base_name) out_name = "%s/%s.db" % (self.frameworkdexdbs_dir, base_name) + # ART-runtime if vm_type[:3] == "ART": # Need to handle the ELF BS. if os.path.isfile(odex_name): log.d(TAG, "ART ODEX mode selected.") - rtn |= self.process_framework(odex_name, out_name) + rtn = self.process_framework(odex_name, out_name) else: log.w(TAG, "No ODEX file found for package '%s'" - % base_name) - elif os.path.isfile(odex_name): - log.d(TAG, "ODEX mode selected.") - rtn |= self.process_framework(odex_name, out_name) + % base_name) + # Dalvik run-time else: - log.d(TAG, "JAR mode selected.") - rtn |= self.process_framework(jar_name, out_name) + # First try the JAR + if os.path.isfile(jar_name): + + log.d(TAG, "Trying JAR first...") + rtn = self.process_framework(jar_name, out_name) + if rtn != 0: + # Well, might as well try ODEX. + if os.path.isfile(odex_name): + log.d(TAG, "Failing to ODEX mode") + rtn = self.process_framework(odex_name, out_name) + # No JAR, go for the ODEX + elif os.path.isfile(odex_name): + log.d(TAG, "ODEX mode selected.") + rtn = self.process_framework(odex_name, out_name) + else: + log.e(TAG, "JAR nor ODEX exists!") return rtn diff --git a/modules/permissions b/modules/permissions index fb2fa09..82d6808 100755 --- a/modules/permissions +++ b/modules/permissions @@ -14,305 +14,356 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Permission searching and lookups -from pydtf.dtfglobals import DTF_PACKAGES -from pydtf import dtfconfig -from pydtf import dtflog as log +"""Permission searching and lookups""" + +import dtf.logging as log +import dtf.properties as prop +from dtf.globals import DTF_PACKAGES_DIR +from dtf.module import Module + +import os +import os.path -import sqlite3 import AppDb from argparse import ArgumentParser -from os.path import isfile -from sys import exit, argv -__VERSION__ = "1.2" +SYSAPPS_DB_NAME = 'sysapps.db' -# dtf Python Module declaration -dtf_module = {"About" : "Permission searching and lookups.", - "Author" : "Jake Valletta (jakev)", - "Health" : "beta", - "Version" : __VERSION__} +TAG = "permissions" -PROJECT_DB = "" -AOSP_DB = "" +class permissions(Module): -# Additional logging settings. Uncomment for more debug. -TAG = "permissions" + """Module class for interacting with system applications""" -# Debug -#log.LOG_LEVEL_STDOUT = 5 + about = 'Permissions searching and lookups.' + author = 'Jake Valletta (jakev)' + health = 'beta' + name = 'permissions' + version = '1.2' -def usage(): - print "Permissions dtf Module Version %s" % __VERSION__ - print "" - print "Submodules:" - print " appuses List applications that use a permission." - print " diff Compare permissions to another DB." - print " list List permission information." - print " lookup Lookup where a permission is used." - print "" - exit(0) - -# Lookup the property associated with AOSP Databases -def getAospDirProp(): - try: - return dtfconfig.get_prop("Local", "aosp-data-dir") + "/sysapps.db" - except: - return None - -def getAppsUsingPermission(appdb, permission): - - sql = ('SELECT a.project_name ' - 'FROM apps a ' - 'JOIN app_uses_permissions aup ' - 'ON aup.application_id=a.id ' - "WHERE aup.permission_id=%s " - 'ORDER BY a.project_name' % permission._id) - - cur = appdb.app_db.cursor() - - cur.execute(sql) + def usage(self): - print ("Applications requesting access to '%s'..." % - (permission)) + """Usage message""" - for row in cur.fetchall(): - application_name = row[0] - print " %s" % application_name + print "Permissions dtf module v%s" % self.version + print "" + print "Submodules:" + print " appuses List applications that use a permission." + print " diff Compare permissions to another DB." + print " list List permission information." + print " lookup Lookup where a permission is used." + print "" + return 0 -def getComponentsUsingPermission(appdb, permission): + @classmethod + def determine_diff_database(cls, args): - activity_list = list() - service_list = list() - receiver_list = list() - read_provider_list = list() - write_provider_list = list() + """Determine which diffing sysappdb to use""" - for app in appdb.getApps(): + args_diff_dir = args.diff_dir - for activity in appdb.getAppActivities(app._id): + # First check if we no arg is set + if args_diff_dir == None: - if activity.permission == None: continue + # First check for our argument. + try: + prop_diff_dir = prop.get_prop('Local', 'diff-data-dir') + except prop.PropertyError: + prop_diff_dir = None - if activity.permission.name == permission.name: - activity_list.append((app.project_name, activity)) + if prop_diff_dir != None: + diff_sysapps_db = "%s/.dbs/%s" % (prop_diff_dir, + SYSAPPS_DB_NAME) - for service in appdb.getAppServices(app._id): + if not os.path.isfile(diff_sysapps_db): + log.e(TAG, "Diff DB '%s' doesn't exist!" % + diff_sysapps_db) + return None + else: + return diff_sysapps_db + # Not set + else: + # Make sure that the AOSP_DB actually exists. + if not AppDb.isAOSPDataInstalled(): + log.e(TAG, "Missing diffing data for this API!") + return None - if service.permission == None: continue + diff_sysapps_db = ("%s/aosp-data-%s/.dbs/%s" % + (DTF_PACKAGES_DIR, + prop.get_prop("Info", "sdk"), + SYSAPPS_DB_NAME)) - if service.permission.name == permission.name: - service_list.append((app.project_name, service)) + return diff_sysapps_db - for receiver in appdb.getAppReceivers(app._id): + # It is set, use it. + else: + diff_sysapps_db = "%s/.dbs/%s" % (args_diff_dir, SYSAPPS_DB_NAME) - if receiver.permission == None: continue - - if receiver.permission.name == permission.name: - receiver_list.append((app.project_name, receiver)) + if not os.path.isfile(diff_sysapps_db): + log.e(TAG, "Diff DB '%s' doesn't exist!" % + diff_sysapps_db) + return None + else: + return diff_sysapps_db - for provider in appdb.getAppProviders(app._id): + @classmethod + def get_apps_using_permission(cls, appdb, permission): - if provider.permission is None and provider.read_permission is None: - read_permission = None - elif provider.permission is None and provider.read_permission is not None: - read_permission = provider.read_permission - elif provider.permission is not None and provider.read_permission is None: - read_permission = provider.permission - elif provider.permission is not None and provider.read_permission is not None: - read_permission = provider.read_permission + """Get apps that request access to a permissions""" - # Same thing for write_permission. - if provider.permission is None and provider.write_permission is None: - write_permission = None - elif provider.permission is None and provider.write_permission is not None: - write_permission = provider.write_permission - elif provider.permission is not None and provider.write_permission is None: - write_permission = provider.permission - elif provider.permission is not None and provider.write_permission is not None: - write_permission = provider.write_permission - - if read_permission != None and read_permission.name == permission.name: - read_provider_list.append((app.project_name, provider)) + sql = ('SELECT a.project_name ' + 'FROM apps a ' + 'JOIN app_uses_permissions aup ' + 'ON aup.application_id=a.id ' + "WHERE aup.permission_id=%s " + 'ORDER BY a.project_name' % permission._id) + + cur = appdb.app_db.cursor() + + cur.execute(sql) + + print ("Applications requesting access to '%s'..." % + (permission)) + + for row in cur.fetchall(): + application_name = row[0] + print " %s" % application_name + + @classmethod + def get_components_using_permission(cls, appdb, permission): - if write_permission != None and write_permission.name == permission.name: - write_provider_list.append((app.project_name, provider)) + """Get components that use a permission""" - print ("Components that require the permission '%s'..." % - (permission)) + activity_list = list() + service_list = list() + receiver_list = list() + read_provider_list = list() + write_provider_list = list() - # Show - print "Activities:" - for project_name, activity in activity_list: - print " %s (%s)" % (activity.name, project_name) - print "Services:" - for project_name, service in service_list: - print " %s (%s)" % (service.name, project_name) - print "Receievers:" - for project_name, receiver in receiver_list: - print " %s (%s)" % (receiver.name, project_name) - print "Providers (readable):" - for project_name, provider in read_provider_list: - print " %s (%s)" % (provider.name, project_name) - print "Providers (writable):" - for project_name, provider in write_provider_list: - print " %s (%s)" % (provider.name, project_name) + for app in appdb.getApps(): + for activity in appdb.getAppActivities(app._id): -def cmdAppUses(): + if activity.permission == None: continue - global PROJECT_DB + if activity.permission.name == permission.name: + activity_list.append((app.project_name, activity)) - parser = ArgumentParser(prog='permissions appuses', - description='List apps that request a permission.') - parser.add_argument('permission_name', metavar="permission_name", - type=str, default=None, - help='The permission to check.') + for service in appdb.getAppServices(app._id): - args = parser.parse_args() + if service.permission == None: continue - permission_name = args.permission_name + if service.permission.name == permission.name: + service_list.append((app.project_name, service)) - appdb = AppDb.AppDb(PROJECT_DB) + for receiver in appdb.getAppReceivers(app._id): - permission = appdb.resolvePermissionByName(permission_name) + if receiver.permission == None: continue - if permission == None: - log.e(TAG, "Permission '%s' not found!" % permission_name) - return -1 + if receiver.permission.name == permission.name: + receiver_list.append((app.project_name, receiver)) - getAppsUsingPermission(appdb, permission) + for prov in appdb.getAppProviders(app._id): -def cmdDiff(): + if (prov.permission is None and + prov.read_permission is None): + read_permission = None + elif (prov.permission is None and + prov.read_permission is not None): + read_permission = prov.read_permission + elif (prov.permission is not None and + prov.read_permission is None): + read_permission = prov.permission + elif (prov.permission is not None and + prov.read_permission is not None): + read_permission = prov.read_permission - global AOSP_DB - global PROJECT_DB + # Same thing for write_permission. + if (prov.permission is None and + prov.write_permission is None): + write_permission = None + elif (prov.permission is None and + prov.write_permission is not None): + write_permission = prov.write_permission + elif (prov.permission is not None and + prov.write_permission is None): + write_permission = prov.permission + elif (prov.permission is not None and + prov.write_permission is not None): + write_permission = prov.write_permission - parser = ArgumentParser(prog='permissions diff', - description='Diff the permissions against another device.') - parser.add_argument('--aosp-db', metavar="aosp_db", type=str, default=None, - help='Use the specified AOSP database.') + if (read_permission != None and + read_permission.name == permission.name): + read_provider_list.append((app.project_name, prov)) - args = parser.parse_args() + if (write_permission != None and + write_permission.name == permission.name): + write_provider_list.append((app.project_name, prov)) - if args.aosp_db == None: + print ("Components that require the permission '%s'..." % + (permission)) - prop_aosp_db = getAospDirProp() - if prop_aosp_db != None: - aosp_db = prop_aosp_db - if not isfile(aosp_db): - print "[ERROR] File '%s' not found. Exiting." % aosp_db - return -3 - else: - aosp_db = AOSP_DB + # Show + print "Activities:" + for project_name, activity in activity_list: + print " %s (%s)" % (activity.name, project_name) + print "Services:" + for project_name, service in service_list: + print " %s (%s)" % (service.name, project_name) + print "Receievers:" + for project_name, receiver in receiver_list: + print " %s (%s)" % (receiver.name, project_name) + print "Providers (readable):" + for project_name, provider in read_provider_list: + print " %s (%s)" % (provider.name, project_name) + print "Providers (writable):" + for project_name, provider in write_provider_list: + print " %s (%s)" % (provider.name, project_name) + + return 0 + + def cmd_appuses(self, args): + + """List apps that use permission""" + + parser = ArgumentParser(prog='permissions appuses', + description='List apps that request a permission.') + parser.add_argument('permission_name', metavar="permission_name", + type=str, default=None, + help='The permission to check.') + + parsed_args = parser.parse_args(args) - # Make sure that the AOSP_DB actually exists. - if not AppDb.isAOSPDataInstalled(): - print "[ERROR] AOSP data for this API level is not installed. Please confirm it is!" - return -2 - else: - aosp_db = args.aosp_db - if not isfile(aosp_db): - print "[ERROR] File '%s' not found. Exiting." % aosp_db - return -3 + db_dir = prop.get_prop('Local', 'db-dir') + local_sysapps_db_name = "%s/%s/%s" % (prop.TOP, db_dir, + SYSAPPS_DB_NAME) - log.d(TAG, "Using AOSP DB '%s'" % aosp_db) + permission_name = parsed_args.permission_name - local_db = AppDb.AppDb(PROJECT_DB) - diff_db = AppDb.AppDb(aosp_db) + appdb = AppDb.AppDb(local_sysapps_db_name) - local_permissions = local_db.getPermissions() - diff_permission_names = [ x.name for x in diff_db.getPermissions()] + permission = appdb.resolvePermissionByName(permission_name) - for local_permission in local_permissions: + if permission == None: + log.e(TAG, "Permission '%s' not found!" % permission_name) + return -1 - name = local_permission.name + return self.get_apps_using_permission(appdb, permission) - if name in diff_permission_names: continue - - app_id = local_permission.application_id - app = local_db.getAppById(app_id) + def cmd_diff(self, args): - protection_level = local_permission.protection_level - project_name = app.project_name - package_name = app.package_name + """Diff permissions""" - print "%s|%s|%s (%s)" % (name, protection_level, - project_name, package_name) + parser = ArgumentParser(prog='permissions diff', + description='Diff permissions against another device.') + parser.add_argument('--diff-dir', metavar="diff_dir", type=str, + default=None, help='Use the specified AOSP database.') -def cmdList(): + parsed_args = parser.parse_args(args) - global PROJECT_DB + db_dir = prop.get_prop('Local', 'db-dir') + local_sysapps_db_name = "%s/%s/%s" % (prop.TOP, db_dir, + SYSAPPS_DB_NAME) - appdb = AppDb.AppDb(PROJECT_DB) + diff_db = self.determine_diff_database(parsed_args) + if diff_db is None: + log.e(TAG, "Unable to determine diff DB!") + return -1 - for permission in appdb.getPermissions(): + log.d(TAG, "Using AOSP DB '%s'" % diff_db) - app_id = permission.application_id - app = appdb.getAppById(app_id) + local_appdb = AppDb.AppDb(local_sysapps_db_name) + diff_appdb = AppDb.AppDb(diff_db) - name = permission.name - protection_level = permission.protection_level - project_name = app.project_name - package_name = app.package_name + local_permissions = local_appdb.getPermissions() + diff_permission_names = [x.name for x in diff_appdb.getPermissions()] - print "%s|%s|%s (%s)" % (name, protection_level, - project_name, package_name) -def cmdLookup(): + for local_permission in local_permissions: - global PROJECT_DB + name = local_permission.name - parser = ArgumentParser(prog='permissions lookup', - description='List components protected by a permission.') - parser.add_argument('permission_name', metavar="permission_name", - type=str, default=None, - help='The permission to check.') + if name in diff_permission_names: + continue - args = parser.parse_args() + app_id = local_permission.application_id + app = local_appdb.getAppById(app_id) - permission_name = args.permission_name + protection_level = local_permission.protection_level + project_name = app.project_name + package_name = app.package_name - appdb = AppDb.AppDb(PROJECT_DB) + print "%s|%s|%s (%s)" % (name, protection_level, + project_name, package_name) + return 0 - permission = appdb.resolvePermissionByName(permission_name) + @classmethod + def cmd_list(cls): - if permission == None: - log.e(TAG, "Permission '%s' not found!" % permission_name) - return -1 + """List permissions""" - getComponentsUsingPermission(appdb, permission) + db_dir = prop.get_prop('Local', 'db-dir') + local_sysapps_db_name = "%s/%s/%s" % (prop.TOP, db_dir, + SYSAPPS_DB_NAME) -def main(argv): + appdb = AppDb.AppDb(local_sysapps_db_name) - global PROJECT_DB - global AOSP_DB + for permission in appdb.getPermissions(): - PROJECT_DB = dtfconfig.get_prop("Local", "db-dir") + "/sysapps.db" - AOSP_DB = ("%s/aosp-data-%s/dbs/sysapps.db" % - (DTF_PACKAGES, dtfconfig.get_prop("Info", "sdk"))) + app_id = permission.application_id + app = appdb.getAppById(app_id) - # We can pop the program name off - argv.pop(0) + name = permission.name + protection_level = permission.protection_level + project_name = app.project_name + package_name = app.package_name - # Get the mode - mode = argv[0] + print "%s|%s|%s (%s)" % (name, protection_level, + project_name, package_name) + return 0 - if mode == 'appuses': - return cmdAppUses() - elif mode == 'diff': - return cmdDiff() - elif mode == 'list': - return cmdList() - elif mode == 'lookup': - return cmdLookup() - else: - return usage() + def cmd_lookup(self, args): -if __name__ == '__main__': + """Lookup a permission""" - if len(argv) < 2: - exit(usage()) - else: - exit(main(argv)) + parser = ArgumentParser(prog='permissions lookup', + description='List components protected by a permission.') + parser.add_argument('permission_name', metavar="permission_name", + type=str, default=None, + help='The permission to check.') + parsed_args = parser.parse_args(args) + + db_dir = prop.get_prop('Local', 'db-dir') + local_sysapps_db_name = "%s/%s/%s" % (prop.TOP, db_dir, + SYSAPPS_DB_NAME) + + permission_name = parsed_args.permission_name + + appdb = AppDb.AppDb(local_sysapps_db_name) + + permission = appdb.resolvePermissionByName(permission_name) + if permission == None: + log.e(TAG, "Permission '%s' not found!" % permission_name) + return -1 + + return self.get_components_using_permission(appdb, permission) + + def execute(self, args): + + """Main class executor""" + + if len(args) == 0: + return self.usage() + mode = args.pop(0) + + if mode == 'appuses': + return self.cmd_appuses(args) + elif mode == 'diff': + return self.cmd_diff(args) + elif mode == 'list': + return self.cmd_list() + elif mode == "lookup": + return self.cmd_lookup(args) + else: + return self.usage() diff --git a/modules/sysservicedb b/modules/sysservicedb index f121d86..896e657 100755 --- a/modules/sysservicedb +++ b/modules/sysservicedb @@ -1,484 +1,602 @@ #!/usr/bin/env python # DTF Core Content # Copyright 2013-2015 Jake Valletta (@jake_valletta) -# +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # -# System Service Manipulation +"""System Service Manipulation""" + +from argparse import ArgumentParser + +import os.path import re import sqlite3 -from argparse import ArgumentParser -from pydtf.dtfadb import DtfAdb -from pydtf.dtfglobals import DTF_PACKAGES -from pydtf import dtfconfig -from pydtf import dtflog as log -from pydtf.dtfcore import launch_module -from os.path import isfile -from sys import argv, exit + +from dtf.adb import DtfAdb +from dtf.globals import DTF_PACKAGES_DIR +from dtf.module import Module +from dtf.packages import launch_module + +import dtf.properties as prop +import dtf.logging as log import AppDb -__VERSION__ = "1.0" +TAG = "sysservicedb" + +SYSSERVICES_DB_NAME = 'services.db' -# dtf Python Module declaration -dtf_module = { "About" : "Create and manage system service database.", - "Author" : "Jake Valletta (jakev)", - "Health" : "stable", - "Version" : __VERSION__ } +class sysservicedb(Module): -__VERSION__ = "1.0" + """Module class manipulating system service database""" -PROJECT_DB = '' -AOSP_DB = '' + about = 'Create and manage system service database.' + author = 'Jake Valletta (jakev)' + health = 'stable' + name = 'sysservicedb' + version = '1.0' -# Debugging -#log.LOG_LEVEL_STDOUT = 5 - -TAG="sysservicedb" + def usage(self): -# Lookup the property associated with AOSP Databases -def getAospDirProp(): - try: - return dtfconfig.get_prop("Local", "aosp-data-dir") + "/services.db" - except: - return None + """Usage message""" -def usage(): - print "ServiceDb dtf Module Version %s" % __VERSION__ - print "" - print "Submodules:" - print " create Create the system service database." - print " diff Diff System Service(s) against local db." - print " dump Dumps details about a system service." - print "" - exit(0) + print "ServiceDb dtf Module Version %s" % self.version + print "" + print "Submodules:" + print " create Create the system service database." + print " diff Diff System Service(s) against local db." + print " dump Dumps details about a system service." + print " list List registered system services." + print "" + return 0 + @classmethod + def determine_diff_database(cls, args): -def dropTables(con): + """Determine which diffing sysservicedb to use""" - log.d(TAG, "Dropping tables...") + args_diff_dir = args.diff_dir - with con: + # First check if we no arg is set + if args_diff_dir == None: - cur = con.cursor() - cur.execute('DROP TABLE IF EXISTS services') - cur.execute('DROP TABLE IF EXISTS transactions') + # First check for our argument. + try: + prop_diff_dir = prop.get_prop('Local', 'diff-data-dir') + except prop.PropertyError: + prop_diff_dir = None - return 1 + if prop_diff_dir != None: + diff_services_db = "%s/.dbs/%s" % (prop_diff_dir, + SYSSERVICES_DB_NAME) -def createTables(con): + if not os.path.isfile(diff_services_db): + log.e(TAG, "Diff DB '%s' doesn't exist!" % + diff_services_db) + return None + else: + return diff_services_db + # Not set + else: + # Make sure that the AOSP_DB actually exists. + if not AppDb.isAOSPDataInstalled(): + log.e(TAG, "Missing diffing data for this API!") + return None - log.d(TAG, "Creating tables...") + diff_services_db = ("%s/aosp-data-%s/.dbs/%s" % + (DTF_PACKAGES_DIR, + prop.get_prop("Info", "sdk"), + SYSSERVICES_DB_NAME)) - with con: - cur = con.cursor() + return diff_services_db - sql = ('CREATE TABLE IF NOT EXISTS services(' - 'id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'name TEXT UNIQUE NOT NULL, ' - 'project TEXT)') - cur.execute(sql) + # It is set, use it. + else: + diff_services_db = "%s/.dbs/%s" % (args_diff_dir, + SYSSERVICES_DB_NAME) - sql = ('CREATE TABLE IF NOT EXISTS transactions(' - 'id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'number INTEGER, ' - 'method_name TEXT, ' - 'arguments TEXT, ' - 'returns TEXT, ' - 'service_id INTEGER,' - 'FOREIGN KEY(service_id) REFERENCES services(id))') - cur.execute(sql) - - con.commit() - return 1 + if not os.path.isfile(diff_services_db): + log.e(TAG, "Diff DB '%s' doesn't exist!" % + diff_services_db) + return None + else: + return diff_services_db -def add_transaction(id, binder_number, binder_interface, - param, return_type): - - with con: - cur = con.cursor() - sql = ('INSERT INTO transactions(' - 'number, method_name, arguments, returns, service_id) ' - 'VALUES(?, ?, ?, ?, ?, ?)') + # Create related + @classmethod + def drop_tables(cls, con): - con.executemany(sql, services) + """Drop tables in DB""" + log.d(TAG, "Dropping tables...") -def parse_transactions(con): + with con: - log.i(TAG, "Parsing Binder transactions") - - with con: - cur = con.cursor() + cur = con.cursor() + cur.execute('DROP TABLE IF EXISTS services') + cur.execute('DROP TABLE IF EXISTS transactions') - sql = "select * from services" - cur.execute(sql) - rows = cur.fetchall() + return 0 - for row in rows: - service_id, service, project = row - if project is None: continue + @classmethod + def create_tables(cls, con): - transactions = list() - log.i(TAG, "Parsing transactions for service '%s'" % service) - out, err, rtn = launch_module("classsearch", " --framework -ef %s" % project) - if out == ['']: - log.d(TAG, "Unable to find source files, native maybe?") - continue - - base_path = out[0].replace(".smali","") - stub_file_path = base_path + "$Stub.smali" - proxy_file_path = base_path + "$Stub$Proxy.smali" + """Create new tables in DB""" - # First do the Stub.smali - if not isfile(stub_file_path): - log.w(TAG, "Unable to find stub paths for server '%s'" - % service) - continue - smali_f = open(stub_file_path, "r") + log.d(TAG, "Creating tables...") - binder_list = list() + with con: + cur = con.cursor() - for line in smali_f.read().split("\n"): + sql = ('CREATE TABLE IF NOT EXISTS services(' + 'id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'name TEXT UNIQUE NOT NULL, ' + 'project TEXT)') - if line.find(".field static final TRANSACTION_") != -1: + cur.execute(sql) - reduced = line[32:].replace(":I = ","|").split("|") - binder_interface = reduced[0] - binder_number = int(reduced[1], 16) + sql = ('CREATE TABLE IF NOT EXISTS transactions(' + 'id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'number INTEGER, ' + 'method_name TEXT, ' + 'arguments TEXT, ' + 'returns TEXT, ' + 'service_id INTEGER,' + 'FOREIGN KEY(service_id) REFERENCES services(id))') - binder_list.append( (binder_number, binder_interface) ) + cur.execute(sql) + con.commit() + return 0 - log.d(TAG, "Found %d transactions" % len(binder_list)) + @classmethod + def parse_transactions(cls, con): - # Now lets do the Stub.Proxy.smali - proxy_f = open(proxy_file_path, "r") - data = proxy_f.read() + """Parse the transactions""" - for binder in binder_list: + log.i(TAG, "Parsing Binder transactions") - param_names = list() + with con: + cur = con.cursor() - binder_number = binder[0] - binder_interface = binder[1] - regex = re.compile(".method public "+binder_interface+".*?\.prologue", re.DOTALL) + sql = "SELECT id, name, project FROM services" + cur.execute(sql) + rows = cur.fetchall() - try: - prologue_block = re.findall(regex, data)[0].split("\n") - except IndexError: - print "[Warning] Binder interface for '%s' not found, skipping" % binder_interface + for row in rows: + service_id, service, project = row + if project is None: continue - sig = prologue_block[0].replace(".method public "+binder_interface, "") + transactions = list() + log.i(TAG, "Parsing transactions for service '%s'" % service) - params = sig.split(')')[0].replace('(', "") - return_type = sig.split(')')[1] + cmd_args = ['--framework', '-e', '-f', project] - if params != "": - for line in prologue_block: - if line.find(" .parameter ") != -1: - param_names.append(line.split('"')[1]) + out, rtn = launch_module("classsearch", cmd_args, redirect=True) - transactions.append((binder_number, binder_interface, params, - return_type, service_id)) + if out == '': + log.w(TAG, "Unable to find source files, native maybe?") + continue - cur = con.cursor() - sql = ('INSERT INTO transactions(' - 'number, method_name, arguments, returns, service_id) ' - 'VALUES(?, ?, ?, ?, ?)') + full_path = out.split("\n")[0] - con.executemany(sql, transactions) - # End with. - con.commit() + base_path = full_path.replace(".smali", "") + stub_file_path = base_path + "$Stub.smali" + proxy_file_path = base_path + "$Stub$Proxy.smali" -# Diffing Stuff -def diffService(project_con, diff_con, service_name, name_only=False): + # First do the Stub.smali + if not os.path.isfile(stub_file_path): + log.w(TAG, "Unable to find stub paths for server '%s'" + % service) + continue + smali_f = open(stub_file_path, "r") - log.d(TAG, "Diffing service '%s'" % service_name) + binder_list = list() + for line in smali_f.read().split("\n"): - with project_con: - p_cur = project_con.cursor() - sql = "SELECT * FROM services WHERE name='%s' LIMIT 1" % service_name - p_cur.execute(sql) - p_row = p_cur.fetchall() - if p_row == []: - print "[ERROR] Service '%s' does not exist in project database." % service_name - return -3 + if line.find(".field static final TRANSACTION_") != -1: - service_id = p_row[0][0] - project_name = p_row[0][2] - - with diff_con: - d_cur = diff_con.cursor() - sql = "SELECT * FROM services WHERE name='%s' LIMIT 1" % service_name - d_cur.execute(sql) - d_row = d_cur.fetchall() - # New service - if d_row == []: - print "Service %s (%s) [NEW]" % (service_name, project_name) - if not name_only: - return dumpBinder(project_con, service_id) - else: - return 0 - # Old, potentially modified - else: - print "Service %s (%s)" % (service_name, project_name) - if not name_only: - return diffTransactions(project_con, diff_con, service_name) - else: - return 0 -def diffTransactions(project_con, diff_con, service_name): + reduced = line[32:].replace(":I = ", "|").split("|") + binder_interface = reduced[0] + binder_number = int(reduced[1], 16) - sql = ('SELECT t.number,t.method_name,t.arguments,t.returns ' - 'FROM transactions t ' - 'JOIN services s ON t.service_id=s.id ' - "WHERE s.name='%s' " - 'ORDER BY t.number' % service_name) + binder_list.append((binder_number, binder_interface)) - project_transactions = list() - with project_con: - p_cur = project_con.cursor() - p_cur.execute(sql) + log.d(TAG, "Found %d transactions" % len(binder_list)) + # Now lets do the Stub.Proxy.smali + proxy_f = open(proxy_file_path, "r") + data = proxy_f.read() - rows = p_cur.fetchall() - for row in rows: - project_transactions.append(row) + for binder in binder_list: - diff_transactions = list() - with diff_con: - d_cur = diff_con.cursor() - d_cur.execute(sql) + param_names = list() - #print d_cur.fetchall() + binder_number = binder[0] + binder_interface = binder[1] + regex = re.compile(".method public " + binder_interface + + ".*?\.prologue", re.DOTALL) - rows = d_cur.fetchall() - for row in rows: - diff_transactions.append(row) + try: + prologue_block = re.findall(regex, data)[0].split("\n") + except IndexError: + cmd = ("Binder interface for '%s' not found, skipping" + % binder_interface) + log.w(TAG, cmd) + continue - diff_names = [i[1] for i in diff_transactions] - project_names = [i[1] for i in project_transactions] + sig = prologue_block[0].replace(".method public " + + binder_interface, "") - for name in project_names: - if name in diff_names: continue - else: - number, _, arguments, returns = [ i for i in project_transactions if i[1] is name ][0] - print " %s %s(%s)" % (number, name, arguments) - print " Returns: %s" % returns - - return 0 - -# Dumping Stuff -def dumpBinder(project_con, service_id): - - with project_con as con: - cur = con.cursor() - sql = ('SELECT * FROM transactions ' - 'WHERE service_id=%s ' - 'ORDER BY number' % service_id) - cur.execute(sql) - - rows = cur.fetchall() - for row in rows: - transaction_id = row[1] - transaction_name = row[2] - transaction_args = row[3] - transaction_rtn = row[4] - print " %s %s(%s)" % (transaction_id, transaction_name, transaction_args) - print " Returns: %s" % transaction_rtn + params = sig.split(')')[0].replace('(', "") + return_type = sig.split(')')[1] + + if params != "": + for line in prologue_block: + if line.find(" .parameter ") != -1: + param_names.append(line.split('"')[1]) + + transactions.append((binder_number, binder_interface, + params, return_type, service_id)) + cur = con.cursor() + sql = ('INSERT INTO transactions(' + 'number, method_name, arguments, returns, service_id) ' + 'VALUES(?, ?, ?, ?, ?)') + + con.executemany(sql, transactions) + # End with. + con.commit() return 0 + # End create related -def cmdCreate(): + # Dump related + @classmethod + def dump_binder(cls, project_con, service_id): - global PROJECT_DB + """Dump binder transactions""" - services = list() + with project_con as con: + cur = con.cursor() - con = sqlite3.connect(PROJECT_DB) + sql = ('SELECT number, method_name, arguments, returns ' + 'FROM transactions ' + "WHERE service_id=%s " + 'ORDER BY number' % service_id) - log.i(TAG, "Creating system service database") + cur.execute(sql) - if not dropTables(con): - print "[ERROR] Error dropping tables. Exiting." - exit(-5) + rows = cur.fetchall() + for row in rows: + transaction_id = row[0] + transaction_name = row[1] + transaction_args = row[2] + transaction_rtn = row[3] - if not createTables(con): - print "[ERROR] Database creation failed. Exiting." - exit(-6) + print (" %s %s(%s)" % + (transaction_id, transaction_name, transaction_args)) + print " Returns: %s" % transaction_rtn - adb = DtfAdb() + return 0 + # End dump related - log.i(TAG, "Waiting for device to be connected...") - adb.wait_for_device() - adb.shell_command("service list") + # Diffing Stuff + @classmethod + def format_trans(cls, transactions, name): - log.i(TAG, "Adding system services") - for line in adb.get_output(): - if line[0:6] == "Found ": continue - if line == "": continue - items = line.split("\t")[1].split(' ') + """Format a transaction""" - service_name = items[0].replace(':', '') - project_name = items[1].replace('[', '').replace(']','') + return [i for i in transactions if i[1] is name][0] - if project_name == '': project_name = None + def diff_service(self, project_con, diff_con, + service_name, name_only=False): - services.append((service_name, project_name)) + """Diff a service""" - with con: + log.d(TAG, "Diffing service '%s'" % service_name) - cur = con.cursor() - sql = 'INSERT INTO services(name, project) VALUES(?, ?)' - con.executemany(sql, services) + with project_con: + p_cur = project_con.cursor() - con.commit() - log.i(TAG, "Service table added") + sql = ("SELECT id, name " + "FROM services " + "WHERE name='%s' LIMIT 1" % service_name) - # Do binder transactions - parse_transactions(con) + p_cur.execute(sql) + p_row = p_cur.fetchall() + if p_row == []: + log.e(TAG, "Service '%s' does not exist in project database." % + service_name) + return -1 -def cmdDiff(args): + service_id = p_row[0][0] + project_name = p_row[0][1] - global PROJECT_DB - global AOSP_DB + with diff_con: + d_cur = diff_con.cursor() - parser = ArgumentParser(prog='servicedb diff', - description='Get differences of a system service(s).') - parser.add_argument('service_name', metavar="service_name", type=str, nargs='?', default=None, - help='The system service to check.') - parser.add_argument('--all', dest='all', action='store_const', const=True, default=False, - help='Run against all system services.') - parser.add_argument('--diff-db', metavar="diff_db", type=str, nargs='?', default=None, - help='Diff against a custom services.db (override the AOSP db).') - parser.add_argument('--name-only', dest='name_only', action='store_const', const=True, default=False, - help='Print just the name of the service (and if it is new).') + sql = ("SELECT id FROM services WHERE name='%s' LIMIT 1" + % service_name) - args = parser.parse_args() + d_cur.execute(sql) + d_row = d_cur.fetchall() - all_mode = args.all - service_name = args.service_name - if service_name is None and not all_mode: - print "[ERROR] A service name is required (or --all)" - return -1 + # New service + if d_row == []: + print "Service %s (%s) [NEW]" % (service_name, project_name) + if not name_only: + return self.dump_binder(project_con, service_id) + else: + return 0 + # Old, potentially modified + else: + print "Service %s (%s)" % (service_name, project_name) + if not name_only: + return self.diff_transactions(project_con, diff_con, + service_name) + else: + return 0 - # Check if a argument is provided. - if args.diff_db == None: - prop_aosp_db = getAospDirProp() - if prop_aosp_db != None: - diff_db = prop_aosp_db - if not isfile(diff_db): - print "[ERROR] File '%s' not found. Exiting." % aosp_db - return -3 - else: - diff_db = AOSP_DB + def diff_transactions(self, project_con, diff_con, service_name): - # Make sure that the AOSP_DB actually exists. - if not AppDb.isAOSPDataInstalled(): - print "[ERROR] AOSP data for this API level is not installed. Please confirm it is!" - return -2 - else: - diff_db = args.diff_db + """Diff the transactions""" - log.d(TAG, "Using diff db of '%s'" % diff_db) + sql = ('SELECT t.number,t.method_name,t.arguments,t.returns ' + 'FROM transactions t ' + 'JOIN services s ON t.service_id=s.id ' + "WHERE s.name='%s' " + 'ORDER BY t.number' % service_name) - if not isfile(PROJECT_DB): - print "[ERROR] Project services database does not exist. Exiting." - return -3 + project_transactions = list() + with project_con: + p_cur = project_con.cursor() + p_cur.execute(sql) - project_con = sqlite3.connect(PROJECT_DB) - diff_con = sqlite3.connect(diff_db) + rows = p_cur.fetchall() + for row in rows: + project_transactions.append(row) - # Diff all system services. - if all_mode: - log.d(TAG, "Diffing all system services") - with project_con as con: - cur = con.cursor() - sql = "SELECT name FROM services ORDER BY name" - cur.execute(sql) - rows = cur.fetchall() + diff_transactions = list() + with diff_con: + d_cur = diff_con.cursor() + d_cur.execute(sql) - rtn = 0 + rows = d_cur.fetchall() for row in rows: - service_name = row[0] - rtn |= diffService(project_con, diff_con, service_name, name_only=args.name_only) - return rtn + diff_transactions.append(row) + + diff_names = [i[1] for i in diff_transactions] + project_names = [i[1] for i in project_transactions] - # Do a single service diff - else: - return diffService(project_con, diff_con, service_name) + for name in project_names: -def cmdDump(args): + if name in diff_names: + continue + else: + (number, _, arguments, returns) = self.format_trans( + project_transactions, + name) + + print " %s %s(%s)" % (number, name, arguments) + print " Returns: %s" % returns + + return 0 + # End diff related + + def cmd_create(self): + + """Create command""" + + db_dir = prop.get_prop('Local', 'db-dir') + local_sysservices_db_name = "%s/%s/%s" % (prop.TOP, db_dir, + SYSSERVICES_DB_NAME) + + services = list() + + con = sqlite3.connect(local_sysservices_db_name) + + log.i(TAG, "Creating system service database") + + if self.drop_tables(con) != 0: + log.e(TAG, "Error dropping tables. Exiting.") + return -1 + + if self.create_tables(con) != 0: + log.e(TAG, "Database creation failed. Exiting.") + return -2 + + adb = DtfAdb() + + log.i(TAG, "Waiting for device to be connected...") + adb.wait_for_device() + adb.shell_command("service list") + + log.i(TAG, "Adding system services") + for line in adb.get_output(): + if line[0:6] == "Found ": + continue + if line == "": + continue + + items = line.split("\t")[1].split(' ') + service_name = items[0].replace(':', '') + project_name = items[1].replace('[', '').replace(']', '') + + if project_name == '': + project_name = None + + services.append((service_name, project_name)) + + with con: + cur = con.cursor() + sql = 'INSERT INTO services(name, project) VALUES(?, ?)' + cur.executemany(sql, services) + + con.commit() + log.i(TAG, "Service table added") + + # Do binder transactions + return self.parse_transactions(con) + + def cmd_diff(self, args): - global PROJECT_DB + """Diff command""" - # Pop 'dump' - args.pop(0) + parser = ArgumentParser(prog='servicedb diff', + description='Compare a system service(s).') + parser.add_argument('service_name', metavar="service_name", + type=str, nargs='?', default=None, + help='The system service to check.') + parser.add_argument('--all', dest='all', action='store_const', + const=True, default=False, + help='Run against all system services.') + parser.add_argument('--diff-dir', metavar="diff_dir", type=str, + default=None, + help='Diff against data in dir.') + parser.add_argument('--name-only', dest='name_only', + action='store_const', const=True, default=False, + help='Print only service name (and if it is new).') - if len(args) == 0: - print "[ERROR] You must provide a service to dump" - return -1 + parsed_args = parser.parse_args(args) - service_name = args.pop() + all_mode = parsed_args.all + service_name = parsed_args.service_name + name_only = parsed_args.name_only - if not isfile(PROJECT_DB): - print "[ERROR] Project services database does not exist. Exiting." - return -2 + if service_name is None and not all_mode: + log.e(TAG, "A service name is required (or --all)!") + return -1 - con = sqlite3.connect(PROJECT_DB) - with con: - cur = con.cursor() - sql = "SELECT * FROM services WHERE name='%s' LIMIT 1" % service_name - cur.execute(sql) - row = cur.fetchall() - if row == []: - print "[ERROR] Service '%s' does not exist in project database." % service_name + db_dir = prop.get_prop('Local', 'db-dir') + local_sysservices_db_name = "%s/%s/%s" % (prop.TOP, db_dir, + SYSSERVICES_DB_NAME) + + diff_db = self.determine_diff_database(parsed_args) + + if diff_db is None: + log.e(TAG, "Unable to determine diff DB!") + return -2 + log.d(TAG, "Using diff db of '%s'" % diff_db) + + if not os.path.isfile(local_sysservices_db_name): + log.e(TAG, "Project services database does not exist. Exiting.") return -3 - service_id = row[0][0] - project_name = row[0][2] + project_con = sqlite3.connect(local_sysservices_db_name) + diff_con = sqlite3.connect(diff_db) + + # Diff all system services. + if all_mode: + log.d(TAG, "Diffing all system services") + with project_con as con: + cur = con.cursor() + sql = "SELECT name FROM services ORDER BY name" + cur.execute(sql) + rows = cur.fetchall() + + rtn = 0 + for row in rows: + service_name = row[0] + rtn |= self.diff_service(project_con, diff_con, + service_name, + name_only=name_only) + return rtn + + # Do a single service diff + else: + return self.diff_service(project_con, diff_con, service_name) - print "Service %s (%s)" % (service_name, project_name) - return dumpBinder(con, service_id) + def cmd_dump(self, args): -def main(argv): + """Dump command""" - global PROJECT_DB - global AOSP_DB + if len(args) == 0: + log.e(TAG, "You must provide a service to dump") + return -1 - PROJECT_DB=dtfconfig.get_prop("Local", "db-dir") + "/services.db" - AOSP_DB = "%s/aosp-data-%s/dbs/services.db" % (DTF_PACKAGES, - dtfconfig.get_prop("Info", "sdk")) - # We can pop the program name off - argv.pop(0) + service_name = args.pop() - # Get the mode - mode = argv[0] + db_dir = prop.get_prop('Local', 'db-dir') + local_sysservices_db_name = "%s/%s/%s" % (prop.TOP, db_dir, + SYSSERVICES_DB_NAME) + + if not os.path.isfile(local_sysservices_db_name): + log.e(TAG, "Project services database does not exist. Exiting.") + return -2 + + con = sqlite3.connect(local_sysservices_db_name) + with con: + cur = con.cursor() - if mode == "create": - return cmdCreate() - elif mode == "diff": - return cmdDiff(argv) - elif mode == "dump": - return cmdDump(argv) - else: - return usage() + sql = ('SELECT id, project ' + 'FROM services ' + "WHERE name='%s' " + 'LIMIT 1' % service_name) + + cur.execute(sql) + row = cur.fetchall() + if row == []: + log.e(TAG, "Service '%s' does not exist in project database." + % service_name) + return -3 + + service_id = row[0][0] + project_name = row[0][1] + + print "Service %s (%s)" % (service_name, project_name) + return self.dump_binder(con, service_id) -if __name__ == '__main__': + def cmd_list(self): - if len(argv) < 2: - exit(usage()) - else: - exit(main(argv)) + """List command""" + + db_dir = prop.get_prop('Local', 'db-dir') + local_sysservices_db_name = "%s/%s/%s" % (prop.TOP, db_dir, + SYSSERVICES_DB_NAME) + + if not os.path.isfile(local_sysservices_db_name): + log.e(TAG, "Project services database does not exist. Exiting.") + return -2 + + con = sqlite3.connect(local_sysservices_db_name) + with con: + cur = con.cursor() + sql = "SELECT name, project FROM services ORDER BY name" + + for svc in cur.execute(sql): + service_name = svc[0] + project_name = svc[1] + + print "Service %s (%s)" % (service_name, project_name) + return 0 + + def execute(self, args): + + """The module entry point""" + + if len(args) == 0: + return self.usage() + mode = args.pop(0) + + if mode == 'create': + return self.cmd_create() + elif mode == 'diff': + return self.cmd_diff(args) + elif mode == 'dump': + return self.cmd_dump(args) + elif mode == "list": + return self.cmd_list() + else: + return self.usage()