class MainActivityInvocationStrategy(RunningStrategy):
    def __init__(self, adbDevice, pathToAndroidManifest):
        self.adbDevice = adbDevice
        self.androidManifest=AndroidManifest(pathToAndroidManifest)
    
    def run(self, delay=10):
        mainActivity = self.androidManifest.getMainActivity()
        if mainActivity:
            self.adbDevice.startActivityExplicitly(package_name=self.androidManifest.getPackageName(),
                                                       activity_name=mainActivity)
            time.sleep(delay)
Beispiel #2
0
class MainActivityInvocationStrategy(RunningStrategy):
    def __init__(self, adbDevice, pathToAndroidManifest):
        self.adbDevice = adbDevice
        self.androidManifest = AndroidManifest(pathToAndroidManifest)

    def run(self, delay=10):
        mainActivity = self.androidManifest.getMainActivity()
        if mainActivity:
            self.adbDevice.startActivityExplicitly(
                package_name=self.androidManifest.getPackageName(),
                activity_name=mainActivity)
            time.sleep(delay)
Beispiel #3
0
 def initAlreadyInstrApkEnv(self, pathToInstrApk, resultsDir, pathToInstrManifestFile=None):
     if not apk_utils.checkInputApkFile(pathToInstrApk):
         logger.error("Provided file [%s] is not a valid apk file!" % pathToInstrApk)
         return
     
     if not os.path.isdir(resultsDir):
         logger.error("Provided path to results dir [%s] do not point to dir!" % resultsDir)
         return
     
     coverageMetadataFolderPath = os.path.join(resultsDir, self.config.getCoverageMetadataRelativeDir())
     if not os.path.isdir(coverageMetadataFolderPath):
         logger.error("In the results dir [%s] there is no folder with coverage metadata!" % resultsDir)
         return
     self.coverageMetadataFolder = coverageMetadataFolderPath
     
     if self.config.getCoverageMetadataFilename() not in os.listdir(coverageMetadataFolderPath):
         logger.error("Cannot find metadata filename in the coverage metadata folder: %s!" % self.coverageMetadataFolder)
         return 
     
     self.coverageMetadataFile = os.path.join(self.coverageMetadataFolder, self.config.getCoverageMetadataFilename())
     
     #by default trying to look for a file in the 
     if pathToInstrManifestFile:
         androidManifestPath = pathToInstrManifestFile
     else:
         androidManifestPath = os.path.join(resultsDir, "AndroidManifest.xml")
     
     if not os.path.isfile(androidManifestPath):
         logger.warning("Path [%s] is not pointing to a real file! Leaving pointer to AndroidManifest.xml empty!" % androidManifestPath)
         return
     
     self.instrumentedApk = pathToInstrApk
     self.apkResultsDir = resultsDir
     self.runtimeReportsRootDir = self._createDir(resultsDir, self.config.getRuntimeReportsRelativeDir(), False, False)
     self.androidManifestFile = androidManifestPath
     
     self.androidManifest = AndroidManifest(self.androidManifestFile)
     self.packageName = self.androidManifest.getInstrumentationTargetPackage()
     self.runnerName = self.androidManifest.getInstrumentationRunnerName()
     
     self._bboxStateMachine.start(STATE_VALID_SETTINGS_PROVIDED)
Beispiel #4
0
 def __init__(self, adbDevice, pathToAndroidManifest):
     self.adbDevice = adbDevice
     self.androidManifest = AndroidManifest(pathToAndroidManifest)
Beispiel #5
0
class IntentInvocationStrategy(RunningStrategy):
    def __init__(self, adbDevice, pathToAndroidManifest):
        self.adbDevice = adbDevice
        self.androidManifest = AndroidManifest(pathToAndroidManifest)

    def run(self, delay=10):
        print "Activities:"
        activities = self.androidManifest.getActivities()
        for activity in activities:
            self._invokeActivityExplicitely(activity)
            time.sleep(delay)

        print "Services:"
        services = self.androidManifest.getServices()
        for service in services:
            self._invokeServiceExplicitely(service)
            time.sleep(delay)

        print "Receivers:"
        receivers = self.androidManifest.getReceivers()
        for receiver in receivers:
            self._invokeReceiver(receiver)
            time.sleep(delay)

    def _invokeActivityExplicitely(self, activity):
        self.adbDevice.startActivityExplicitly(
            package_name=self.androidManifest.getPackageName(),
            activity_name=activity)

    def _invokeActivity(self, activity):
        intentFilters = self.androidManifest.getActivityIntentFilters(activity)
        if not intentFilters:
            self.adbDevice.startActivityExplicitly(
                package_name=self.androidManifest.getPackageName(),
                activity_name=activity)
            return
        for filt in intentFilters:
            action = self._getAction(filt)
            category = self._getCategory(filt)
            mimeType = self._getMiMeType(filt)
            self.adbDevice.startActivityImplicitely(action=action,
                                                    mimeType=mimeType,
                                                    category=category)

    def _invokeServiceExplicitely(self, service):
        self.adbDevice.startServiceExplicitly(
            package_name=self.androidManifest.getPackageName(),
            service_name=service)

    def _invokeService(self, service):
        intentFilters = self.androidManifest.getServiceIntentFilters(service)
        if not intentFilters:
            self.adbDevice.startServiceExplicitly(
                package_name=self.androidManifest.getPackageName(),
                service_name=service)
            return

        for filt in intentFilters:
            action = self._getAction(filt)
            category = self._getCategory(filt)
            mimeType = self._getMiMeType(filt)
            self.adbDevice.startServiceImplicitely(action=action,
                                                   mimeType=mimeType,
                                                   category=category)

    def _invokeReceiver(self, receiver):
        intentFilters = self.androidManifest.getReceiverIntentFilters(receiver)
        if not intentFilters:
            return

        for filt in intentFilters:
            action = self._getAction(filt)
            category = self._getCategory(filt)
            mimeType = self._getMiMeType(filt)
            self.adbDevice.sendBroadcast(action=action,
                                         mimeType=mimeType,
                                         category=category)

    def _getAction(self, filt):
        action = None
        actions = filt.get("action")
        if actions:
            action = actions[0]
        return action

    def _getCategory(self, filt):
        category = None
        categories = filt.get("category")
        if categories:
            category = categories[0]
        else:
            category = CATEGORY_DEFAULT
        return category

    def _getMiMeType(self, filt):
        mimeType = None
        mimeTypes = filt.get("mimeType")
        if mimeTypes:
            mimeType = mimeTypes[0]
        return mimeType
 def instrumentAndroidManifestFile(self, pathToUnmodifiedFile, pathToModifiedFile=None, addSdCardPermission=True):
     '''
     Adds instrumentation tag with predefined attributes corresponding to our
     instrumentation classes to the provided manifest file. If 
     instrumentation tag exists, this method substitutes it with appropriate
     one. Adds (if necessary) to the provided AndroidManifest file permission
     to write to the external storage.
     
     Args:
         :param pathToUnmodifiedFile: path to the unmodified 
             AndroidManifest.xml file
         :param pathToModifiedFile: path where to store modified
             AndroidManifest.xml file. If pathToModifiedFile==None, the 
             initial pathToUnmodifiedFile will be overridden.
     '''
     if not os.path.isfile(pathToUnmodifiedFile):
         raise IllegalArgumentException("File [%s] does not exist!" % pathToUnmodifiedFile) 
     androidManifest = AndroidManifest(pathAndroidManifest=pathToUnmodifiedFile)
     packageName = androidManifest.getPackageName()
     #TODO: think how to substitute these constants later
     try:
         androidManifest.addInstrumentation("com.zhauniarovich.bbtester.EmmaInstrumentation", packageName)
     except ManifestAlreadyInstrumentedException:
         #removing all existing instrumentation tags and creating our new
         androidManifest.removeExistingInstrumentation() #TODO: this can throw an exception
         androidManifest.addInstrumentation("com.zhauniarovich.bbtester.EmmaInstrumentation", packageName)
     
     if addSdCardPermission:
         androidManifest.addUsesPermission("android.permission.WRITE_EXTERNAL_STORAGE")
     
     if not pathToModifiedFile or (pathToUnmodifiedFile == pathToModifiedFile):
         androidManifest.exportManifest(path=None)
     else: 
         androidManifest.exportManifest(path=pathToModifiedFile)
class BBoxCoverage:
    PREFIX_ONSTOP = "onstop"
    PREFIX_ONERROR = "onerror"
    '''
    This class unites all separate classes needed for instrumentation and
    testing of the instrumented apk and provides a consistent workflow for a 
    developer.
    '''

    #     def __init__(self, resultsRootDir=None, pathToBBoxConfigFile="./config/bbox_config.ini"):
    #         '''
    #         Constructor.
    #         '''
    #         #getting resultsRootDir
    #         self.resultsRootDir = None
    #         if not resultsRootDir:
    #             self.resultsRootDir = os.path.join(os.getcwd(), )
    #         else:
    #             self.resultsRootDir = os.path.abspath(resultsRootDir)
    #
    #         self.config = BBoxConfig(pathToBBoxConfigFile)
    #         self._bboxStateMachine = StateMachine(states=STATES)

    def __init__(self, pathToBBoxConfigFile="./config/bbox_config.ini"):
        self.androidManifestFile = None
        self.instrumentedApk = None
        self.config = BBoxConfig(pathToBBoxConfigFile)
        self.bboxInstrumenter = BBoxInstrumenter(self.config)
        self.bboxExecutor = BBoxExecutor(self.config)
        self.bboxReporter = BBoxReporter(self.config)
        self._bboxStateMachine = StateMachine(states=STATES)

    def getInstrumentedApk(self):
        return self.instrumentedApk

    def getPackageName(self):
        return self.packageName

    def instrumentApkForCoverage(self,
                                 pathToOrigApk,
                                 resultsDir=None,
                                 tmpDir=None,
                                 removeApkTmpDirAfterInstr=True,
                                 copyApkToRes=True):
        '''
        Args:
            :param pathToOrigApk:
            :param resultsDir:
            :param tmpDir:
        
        Returns:
            :ret True - if instrumentation was successful, False otherwise
        '''
        self._bboxStateMachine.start(STATE_UNINITIALIZED)

        valid = self._checkProvidedApk(pathToOrigApk)
        if not valid:
            return False
        self._bboxStateMachine.transitToState(STATE_APK_VALID)

        resultsRootDir = None
        if not resultsDir:
            resultsRootDir = os.path.join(os.getcwd(), RESULTS_RELATIVE_DIR)
        else:
            resultsRootDir = os.path.abspath(resultsDir)

        tmpRootDir = None
        if not tmpDir:
            tmpRootDir = os.path.join(os.getcwd(), TMP_RELATIVE_DIR)
        else:
            tmpRootDir = os.path.abspath(tmpDir)

        apkFileName = os.path.splitext(os.path.basename(pathToOrigApk))[0]

        self.apkTmpDir = self._createDir(tmpDir, apkFileName, False, True)
        self.apkResultsDir = self._createDir(resultsRootDir, apkFileName,
                                             False, True)
        self.coverageMetadataFolder = self._createDir(
            self.apkResultsDir, self.config.getCoverageMetadataRelativeDir(),
            False, True)
        self.runtimeReportsRootDir = self._createDir(
            self.apkResultsDir, self.config.getRuntimeReportsRelativeDir(),
            False, True)
        self._bboxStateMachine.transitToState(STATE_FOLDERS_CREATED)

        #coping initial apk file if required
        if copyApkToRes:
            shutil.copy2(pathToOrigApk, self.apkResultsDir)

        #decompiling apk into a folder
        decompileDir = os.path.join(self.apkTmpDir,
                                    self.config.getDecompiledApkRelativeDir())
        success = self._decompileApk(self.bboxInstrumenter, pathToOrigApk,
                                     decompileDir)
        if not success:
            return False
        self._bboxStateMachine.transitToState(STATE_APK_DECOMPILED)

        #getting all available dex files
        dexFilesRelativePaths = self._getDexFilePathsRelativeToDir(
            decompileDir)
        if not dexFilesRelativePaths:
            logger.error("There is no dex files to convert!")
            return False
        if "classes.dex" not in dexFilesRelativePaths:
            # "classes.dex" must be in the decompile root directory
            logger.error(
                "There is no classes.dex file found in the list of dex files")
            return False

        #converting dex to jar files
        rawJarFilesRootDir = os.path.join(self.apkTmpDir,
                                          self.config.getTmpJarRelativeDir())
        jarFilesRelativePaths = self._convertDex2JarFiles(
            converter=self.bboxInstrumenter,
            dexFilesRootDir=decompileDir,
            dexFilesRelativePaths=dexFilesRelativePaths,
            jarFilesRootDir=rawJarFilesRootDir,
            proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_DEX_CONVERTED_TO_JAR)

        if "classes.jar" not in jarFilesRelativePaths:
            #main file is not converted
            logger.error(
                "Conversion from classes.dex to classes.jar was not successful!"
            )
            return False

        #instrumenting available jar files
        self.coverageMetadataFile = os.path.join(
            self.coverageMetadataFolder,
            self.config.getCoverageMetadataFilename())
        emmaInstrJarFilesRootDir = os.path.join(
            self.apkTmpDir, self.config.getInstrumentedFilesRelativeDir())
        emmaInstrJarFileRelativePaths = self._instrFilesWithEmma(
            instrumenter=self.bboxInstrumenter,
            jarFilesRootDir=rawJarFilesRootDir,
            jarFilesRelativePaths=jarFilesRelativePaths,
            instrJarsRootDir=emmaInstrJarFilesRootDir,
            coverageMetadataFile=self.coverageMetadataFile,
            proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_JARS_INSTRUMENTED)

        if "classes.jar" not in emmaInstrJarFileRelativePaths:
            #main file is not instrumented
            logger.error("Instrumentation of classes.jar was not successful!")
            return False

        #converting jar files back to dex files
        instrDexFilesRelativePaths = self._convertJar2DexWithInstr(
            converter=self.bboxInstrumenter,
            instrJarsRootDir=emmaInstrJarFilesRootDir,
            instrJarFilesRelativePaths=emmaInstrJarFileRelativePaths,
            finalDexFilesRootDir=decompileDir,
            proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_JAR_CONVERTED_TO_DEX)

        if "classes.dex" not in instrDexFilesRelativePaths:
            logger.error(
                "There is no classes.dex file found in the list of dex files")
            return False

        #checking what files have not been converted
        uninstrumentedFiles = self._getUnInstrFilesRelativePaths(
            dexFilesRelativePaths, instrDexFilesRelativePaths)
        if uninstrumentedFiles:
            logger.debug("The following files were not instrumented: %s" +
                         str(uninstrumentedFiles))

        #instrument AndroidManifest.xml
        decompiledAndroidManifestPath = os.path.join(decompileDir,
                                                     "AndroidManifest.xml")
        success = self._instrAndroidManifest(self.bboxInstrumenter,
                                             decompiledAndroidManifestPath)
        if not success:
            logger.error("Cannot instrument AndroidManifest.xml file!")
            return False
        #coping instrumented AndroidManifest.xml to result folder
        shutil.copy2(decompiledAndroidManifestPath, self.apkResultsDir)
        self.androidManifestFile = os.path.join(self.apkResultsDir,
                                                "AndroidManifest.xml")
        self._bboxStateMachine.transitToState(STATE_MANIFEST_INSTRUMENTED)

        compiledApkFilePath = os.path.join(
            self.apkResultsDir,
            "%s%s.apk" % (apkFileName, self.config.getInstrFileSuffix()))
        success = self._compileApk(self.bboxInstrumenter, decompileDir,
                                   compiledApkFilePath)
        if not success:
            logger.error("Cannot build apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_BUILD)

        #need to copy resources into file
        compiledApkFilePathWithEmmaRes = os.path.join(
            self.apkResultsDir,
            "%s%s.apk" % (apkFileName, self.config.getFinalInstrFileSuffix()))
        shutil.copy2(compiledApkFilePath, compiledApkFilePathWithEmmaRes)
        self._putAdditionalResources(
            apk=compiledApkFilePathWithEmmaRes,
            resources=self.config.getEmmaResourcesDir())
        self._bboxStateMachine.transitToState(
            STATE_FINAL_INSTRUMENTED_APK_BUILD)

        signedApkFilePath = os.path.join(
            self.apkResultsDir,
            "%s%s.apk" % (apkFileName, self.config.getSignedFileSuffix()))
        success = self._signApk(self.bboxInstrumenter,
                                compiledApkFilePathWithEmmaRes,
                                signedApkFilePath)
        if not success:
            logger.error("Cannot sign apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_SIGNED)

        alignedApkFilePath = os.path.join(
            self.apkResultsDir,
            "%s%s.apk" % (apkFileName, self.config.getAlignedFileSuffix()))
        success = self._alignApk(self.bboxInstrumenter, signedApkFilePath,
                                 alignedApkFilePath)
        if not success:
            logger.error("Cannot align apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_ALIGNED)

        #cleaning: if tmp dir needs to be removed after instrumentation
        if removeApkTmpDirAfterInstr:
            shutil.rmtree(self.apkTmpDir)

        self.instrumentedApk = alignedApkFilePath
        self.androidManifest = AndroidManifest(self.androidManifestFile)
        self.packageName = self.androidManifest.getInstrumentationTargetPackage(
        )
        self.runnerName = self.androidManifest.getInstrumentationRunnerName()

        #Final node transition
        self._bboxStateMachine.transitToState(STATE_APK_INSTRUMENTED)
        return True

    def _createDir(self, root, directory, createNew=True, overwrite=False):
        resDir = os.path.join(root, directory)
        if createNew:
            i = 0
            while os.path.exists(resDir):
                i += 1
                resDir = os.path.join(root, "%s_%d" % (directory, i))
        auxiliary_utils.mkdir(path=resDir, mode=0777, overwrite=overwrite)

        #         resDir = os.path.join(root, directory)
        #         if not overwrite:
        #             i = 0
        #             while os.path.exists(resDir):
        #                 i += 1
        #                 resDir = os.path.join(root, "%s_%d" % (directory, i))
        #
        #         auxiliary_utils.mkdir(path=resDir, mode=0777, overwrite=overwrite)
        return resDir

    def _createFolder(self, root, dirName, overwrite=False):
        resultDir = os.path.join(root, dirName)
        auxiliary_utils.mkdir(path=resultDir, mode=0777, overwrite=overwrite)
        return resultDir

    def _checkProvidedApk(self, pathToApk):
        '''
        This methods validates the provided apk file.
        
        Raises:
            ApkIsNotValidException: if the provided apk file is not correct
        '''
        (valid, error) = apk_utils.checkInputApkFile(pathToApk)
        if not valid:
            logger.error("Path %s points to the invalid apk file! ERROR:%s" %
                         (pathToApk, error))
            return valid

        return valid

    def _decompileApk(self, decompiler, apk, outputDir):
        try:
            decompiler.decompileApk(apk, outputDir)
        except ApkCannotBeDecompiledException as e:
            logger.error(e.msg)
            return False
        except:
            logger.error("Unknown error during decompile process! Exit")
            return False

        return True

    def _convertDex2JarFiles(self,
                             converter,
                             dexFilesRootDir,
                             dexFilesRelativePaths,
                             jarFilesRootDir,
                             proceedOnError=True):
        jarFilesRelativePaths = []
        for dexFileRelativePath in dexFilesRelativePaths:
            dexFilePath = os.path.join(dexFilesRootDir, dexFileRelativePath)
            jarFileRelativePath = os.path.splitext(
                dexFileRelativePath)[0] + ".jar"
            jarFilePath = os.path.join(jarFilesRootDir, jarFileRelativePath)
            try:
                converter.convertDex2Jar(dexFilePath,
                                         jarFilePath,
                                         overwrite=True)
            except Dex2JarConvertionError as e:
                if proceedOnError:
                    logger.warning("Cannot convert [%s] to [%s]. %s" (
                        dexFilePath, jarFilePath, e.msg))
                    continue
                else:
                    raise
            jarFilesRelativePaths.append(jarFileRelativePath)

        return jarFilesRelativePaths

    def _instrFilesWithEmma(self,
                            instrumenter,
                            jarFilesRootDir,
                            jarFilesRelativePaths,
                            instrJarsRootDir,
                            coverageMetadataFile,
                            proceedOnError=True):

        instrJarFilesRelativePaths = []
        for jarFileRelativePath in jarFilesRelativePaths:
            jarFileAbsPath = os.path.join(jarFilesRootDir, jarFileRelativePath)
            instrJarRelativeDir = jarFileRelativePath[:jarFileRelativePath.
                                                      rfind("/") + 1]
            instrJarFullDir = os.path.join(instrJarsRootDir,
                                           instrJarRelativeDir)

            try:
                instrumenter.instrumentJarWithEmma(
                    jarFile=jarFileAbsPath,
                    outputFolder=instrJarFullDir,
                    emmaMetadataFile=coverageMetadataFile)
            except EmmaCannotInstrumentException as e:
                if proceedOnError:
                    logger.warning("Cannot instrument [%s]. %s" (
                        jarFileAbsPath, e.msg))
                    continue
                else:
                    raise
            instrJarFilesRelativePaths.append(jarFileRelativePath)

        return instrJarFilesRelativePaths

    def _convertJar2DexWithInstr(self, converter, instrJarsRootDir,
                                 instrJarFilesRelativePaths,
                                 finalDexFilesRootDir, proceedOnError):
        instrDexFilesRelativePaths = []
        for jarFileRelativePath in instrJarFilesRelativePaths:
            jarFileAbsPath = os.path.join(instrJarsRootDir,
                                          jarFileRelativePath)
            dexFileRelativePath = os.path.splitext(
                jarFileRelativePath)[0] + ".dex"
            dexFileAbsPath = os.path.join(finalDexFilesRootDir,
                                          dexFileRelativePath)

            #we instrument main file with auxiliary classes
            print "jarFileRelativePath: " + jarFileRelativePath

            try:
                withFiles = []
                #we compile main file with additional instrumentation files
                #hack: emma copies instrumented jar files into lib folder
                if jarFileRelativePath == "classes.jar":
                    emmaDevicePath = os.path.join(
                        self.config.getEmmaDir(),
                        self.config.getEmmaDeviceJar())
                    withFiles.append(
                        self.config.
                        getAndroidSpecificInstrumentationClassesPath())
                    withFiles.append(emmaDevicePath)

                converter.convertJar2Dex(jarFile=jarFileAbsPath,
                                         dexFile=dexFileAbsPath,
                                         withFiles=withFiles,
                                         overwrite=True)
            except Jar2DexConvertionError as e:
                if proceedOnError:
                    logger.warning("Cannot instrument [%s]. %s" %
                                   (jarFileAbsPath, e.msg))
                    continue
                else:
                    raise
            instrDexFilesRelativePaths.append(dexFileRelativePath)

        return instrDexFilesRelativePaths

    def _getUnInstrFilesRelativePaths(self, dexFilesRelativePaths,
                                      instrDexFilesRelativePaths):
        uninstrumentedFiles = []
        for dexFileRelativePath in dexFilesRelativePaths:
            if dexFileRelativePath not in instrDexFilesRelativePaths:
                uninstrumentedFiles.append(dexFileRelativePath)
        return uninstrumentedFiles

    def _instrAndroidManifest(self,
                              instrumenter,
                              initAndroidManifest,
                              instrAndroidManifest=None,
                              addSdCardPermission=True):
        success = True
        try:
            instrumenter.instrumentAndroidManifestFile(initAndroidManifest,
                                                       instrAndroidManifest,
                                                       addSdCardPermission)
        except IllegalArgumentException as e:
            logger.error("Cannot instrument AndroidManifest file. %s" % e.msg)
            success = False
        except:
            logger.error("Cannot instrument AndroidManifest file!")
            success = False
        return success

    def _compileApk(self, compiler, fromDir, apkPath):
        success = True
        try:
            compiler.buildApk(fromDir, apkPath)
        except ApktoolBuildException as e:
            logger.error("Cannot build apk! %s" % e.msg)
            success = False
        except:
            logger.error("Cannot build apk!")
            success = False
        return success

    def _putAdditionalResources(self, apk, resources):
        zip_utils.zipdir(resources, apk)

    def _signApk(self, signer, unsignedApkFile, signedApkFile):
        success = True
        try:
            signer.signApk(unsignedApkFile, signedApkFile)
        except SignApkException as e:
            logger.error("Cannot sign apk! %s" % e.msg)
            success = False
        except:
            logger.error("Cannot sign apk!")
            success = False
        return success

    def _alignApk(self, aligner, unalignedApkFile, alignedApkFile):
        success = True
        try:
            aligner.alignApk(unalignedApkFile, alignedApkFile)
        except AlignApkException as e:
            logger.error("Cannot align apk! %s" % e.msg)
            success = False
        except:
            logger.error("Cannot align apk!")
            success = False
        return success

#     def _getMainDexFile(self, directory):
#         mainDexFile = os.path.join(directory, "classes.dex")
#         if not os.path.exists(mainDexFile):
#             logger.error("Cannot find classes.dex file!")
#             return (False, None)
#
#         return (True, mainDexFile)

    def _getDexFiles(self, directory):
        dexFileNames = auxiliary_utils.searchFiles(where=directory,
                                                   extension="dex")
        return dexFileNames

    def _getDexFilePathsRelativeToDir(self, target):
        dexFileRelativePaths = auxiliary_utils.searchFilesRelativeToDir(
            target=target, extension="dex")
        return dexFileRelativePaths

################################################################################

    def initAlreadyInstrApkEnv(self,
                               pathToInstrApk,
                               resultsDir,
                               pathToInstrManifestFile=None):
        if not apk_utils.checkInputApkFile(pathToInstrApk):
            logger.error("Provided file [%s] is not a valid apk file!" %
                         pathToInstrApk)
            return

        if not os.path.isdir(resultsDir):
            logger.error(
                "Provided path to results dir [%s] do not point to dir!" %
                resultsDir)
            return

        coverageMetadataFolderPath = os.path.join(
            resultsDir, self.config.getCoverageMetadataRelativeDir())
        if not os.path.isdir(coverageMetadataFolderPath):
            logger.error(
                "In the results dir [%s] there is no folder with coverage metadata!"
                % resultsDir)
            return
        self.coverageMetadataFolder = coverageMetadataFolderPath

        if self.config.getCoverageMetadataFilename() not in os.listdir(
                coverageMetadataFolderPath):
            logger.error(
                "Cannot find metadata filename in the coverage metadata folder: %s!"
                % self.coverageMetadataFolder)
            return

        self.coverageMetadataFile = os.path.join(
            self.coverageMetadataFolder,
            self.config.getCoverageMetadataFilename())

        #by default trying to look for a file in the
        if pathToInstrManifestFile:
            androidManifestPath = pathToInstrManifestFile
        else:
            androidManifestPath = os.path.join(resultsDir,
                                               "AndroidManifest.xml")

        if not os.path.isfile(androidManifestPath):
            logger.warning(
                "Path [%s] is not pointing to a real file! Leaving pointer to AndroidManifest.xml empty!"
                % androidManifestPath)
            return

        self.instrumentedApk = pathToInstrApk
        self.apkResultsDir = resultsDir
        self.runtimeReportsRootDir = self._createDir(
            resultsDir, self.config.getRuntimeReportsRelativeDir(), False,
            False)
        self.androidManifestFile = androidManifestPath

        self.androidManifest = AndroidManifest(self.androidManifestFile)
        self.packageName = self.androidManifest.getInstrumentationTargetPackage(
        )
        self.runnerName = self.androidManifest.getInstrumentationRunnerName()

        self._bboxStateMachine.start(STATE_VALID_SETTINGS_PROVIDED)

    def installApkOnDevice(self):
        if not self._bboxStateMachine.isTransitionPossible(
                STATE_APK_INSTALLED):
            logger.error(
                "Cannot install apk on device because the environment is not initialized!"
            )
            return

        #selecting device for execution
        self.bboxExecutor.selectExecutionDevice()
        try:
            self.bboxExecutor.installApkOnDevice(self.instrumentedApk)
        except ApkCannotBeInstalledException as e:
            logger.error("Cannot install instrumented apk. %s" % e.msg)
            return
        self._bboxStateMachine.transitToState(STATE_APK_INSTALLED)

    def startTesting(self):
        if not self._bboxStateMachine.isTransitionPossible(
                STATE_APK_TEST_STARTED):
            logger.error("Cannot start testing apk on a device!")
            return

        self.deviceReportFolder = os.path.join(DEVICE_REPORT_FOLDER_PATH,
                                               self.packageName)
        self.bboxExecutor.selectExecutionDevice()
        self.bboxExecutor.startOndeviceTesting(
            packageName=self.packageName,
            runnerName=self.runnerName,
            coverage=True,
            reportFolder=self.deviceReportFolder,
            proceedOnError=True,
            generateCoverageReportOnError=True)
        self._bboxStateMachine.transitToState(STATE_APK_TEST_STARTED)

    def stopTesting(self, localReportFolderName=None, paramsToWrite=None):
        if not self._bboxStateMachine.isTransitionPossible(
                STATE_APK_FINISHED_TESTING):
            logger.error("Cannot stop testing because it is not started!")
            return


#         currentDateTime = datetime.datetime.now()
#         reportTimePrefix = currentDateTime.strftime("%Y_%m_%d__%H_%M_%S")
#         coverageReportName = "%s___%s" % (reportTimePrefix, "coverage.ec")
#         reportFileOnDevice = "%s/%s" % (DEVICE_REPORT_FOLDER_PATH, coverageReportName)

#         reportLocally = os.path.join(self.runtimeReportsRootDir, coverageReportName)
        if not localReportFolderName:
            localReportFolderName = "test"

        localReportFolder = self._createDir(self.runtimeReportsRootDir,
                                            localReportFolderName, True, False)

        self.bboxExecutor.stopOndeviceTesting(cancelAnalysis=False)

        time.sleep(3)  #waiting for several seconds for report to be generated

        #         success = self.bboxExecutor.getFileFromDevice(reportFileOnDevice, reportLocally)
        success = self.bboxExecutor.getFileFromDevice(self.deviceReportFolder,
                                                      localReportFolder)

        if not success:
            self.bboxExecutor.removeFile(self.deviceReportFolder)
            return None

        if paramsToWrite:
            params_config = ConfigParser.ConfigParser()
            params_config.add_section(PARAMS_SECTION)
            for param in iteritems(paramsToWrite):
                params_config.set(PARAMS_SECTION, param[0], param[1])
            with open(os.path.join(localReportFolder, "parameters.txt"),
                      "w") as param_file:
                params_config.write(param_file)

        self.bboxExecutor.removeFile(self.deviceReportFolder)
        self._bboxStateMachine.transitToState(STATE_APK_FINISHED_TESTING)
        return localReportFolder

    def generateReport(self,
                       reportFiles=[],
                       reportName=None,
                       reportType=EMMA_REPORT.XML):
        if not reportFiles:
            logger.error("No report files are provided!")
            return

        self.bboxReporter.cleanMetaFiles()
        self.bboxReporter.cleanReportFiles()

        self.bboxReporter.addMetaFile(self.coverageMetadataFile)
        for rFile in reportFiles:
            self.bboxReporter.addReportFile(rFile)

        reportsRoot = os.path.join(self.apkResultsDir,
                                   self.config.getReportsRelativeDir())
        where = self._createReportResultsDir(reportsRoot,
                                             "report_%s" % reportType)
        self.bboxReporter.generateEmmaReport(where, reportName, reportType)

    def _createReportResultsDir(self, reportsRoot, reportDirName):
        i = 0
        resultsDir = os.path.join(reportsRoot, reportDirName)
        while os.path.exists(resultsDir):
            i += 1
            resultsDir = os.path.join(reportsRoot,
                                      "%s_%d" % (reportDirName, i))

        auxiliary_utils.mkdir(path=resultsDir, mode=0777, overwrite=False)
        return resultsDir

    def uninstallPackage(self):
        self.bboxExecutor.uninstallPackage(packageName=self.packageName,
                                           keepData=False)
        self._bboxStateMachine.stop()

    @staticmethod
    def getCoverageReportsFromFolderWithPrefix(folder, prefix):
        if not os.path.exists(folder):
            return None

        reports = []
        for file in os.listdir(folder):
            if file.endswith(".ec") and file.startswith(prefix):
                reports.append(os.path.join(folder, file))

        return reports
    def initAlreadyInstrApkEnv(self,
                               pathToInstrApk,
                               resultsDir,
                               pathToInstrManifestFile=None):
        if not apk_utils.checkInputApkFile(pathToInstrApk):
            logger.error("Provided file [%s] is not a valid apk file!" %
                         pathToInstrApk)
            return

        if not os.path.isdir(resultsDir):
            logger.error(
                "Provided path to results dir [%s] do not point to dir!" %
                resultsDir)
            return

        coverageMetadataFolderPath = os.path.join(
            resultsDir, self.config.getCoverageMetadataRelativeDir())
        if not os.path.isdir(coverageMetadataFolderPath):
            logger.error(
                "In the results dir [%s] there is no folder with coverage metadata!"
                % resultsDir)
            return
        self.coverageMetadataFolder = coverageMetadataFolderPath

        if self.config.getCoverageMetadataFilename() not in os.listdir(
                coverageMetadataFolderPath):
            logger.error(
                "Cannot find metadata filename in the coverage metadata folder: %s!"
                % self.coverageMetadataFolder)
            return

        self.coverageMetadataFile = os.path.join(
            self.coverageMetadataFolder,
            self.config.getCoverageMetadataFilename())

        #by default trying to look for a file in the
        if pathToInstrManifestFile:
            androidManifestPath = pathToInstrManifestFile
        else:
            androidManifestPath = os.path.join(resultsDir,
                                               "AndroidManifest.xml")

        if not os.path.isfile(androidManifestPath):
            logger.warning(
                "Path [%s] is not pointing to a real file! Leaving pointer to AndroidManifest.xml empty!"
                % androidManifestPath)
            return

        self.instrumentedApk = pathToInstrApk
        self.apkResultsDir = resultsDir
        self.runtimeReportsRootDir = self._createDir(
            resultsDir, self.config.getRuntimeReportsRelativeDir(), False,
            False)
        self.androidManifestFile = androidManifestPath

        self.androidManifest = AndroidManifest(self.androidManifestFile)
        self.packageName = self.androidManifest.getInstrumentationTargetPackage(
        )
        self.runnerName = self.androidManifest.getInstrumentationRunnerName()

        self._bboxStateMachine.start(STATE_VALID_SETTINGS_PROVIDED)
    def instrumentApkForCoverage(self,
                                 pathToOrigApk,
                                 resultsDir=None,
                                 tmpDir=None,
                                 removeApkTmpDirAfterInstr=True,
                                 copyApkToRes=True):
        '''
        Args:
            :param pathToOrigApk:
            :param resultsDir:
            :param tmpDir:
        
        Returns:
            :ret True - if instrumentation was successful, False otherwise
        '''
        self._bboxStateMachine.start(STATE_UNINITIALIZED)

        valid = self._checkProvidedApk(pathToOrigApk)
        if not valid:
            return False
        self._bboxStateMachine.transitToState(STATE_APK_VALID)

        resultsRootDir = None
        if not resultsDir:
            resultsRootDir = os.path.join(os.getcwd(), RESULTS_RELATIVE_DIR)
        else:
            resultsRootDir = os.path.abspath(resultsDir)

        tmpRootDir = None
        if not tmpDir:
            tmpRootDir = os.path.join(os.getcwd(), TMP_RELATIVE_DIR)
        else:
            tmpRootDir = os.path.abspath(tmpDir)

        apkFileName = os.path.splitext(os.path.basename(pathToOrigApk))[0]

        self.apkTmpDir = self._createDir(tmpDir, apkFileName, False, True)
        self.apkResultsDir = self._createDir(resultsRootDir, apkFileName,
                                             False, True)
        self.coverageMetadataFolder = self._createDir(
            self.apkResultsDir, self.config.getCoverageMetadataRelativeDir(),
            False, True)
        self.runtimeReportsRootDir = self._createDir(
            self.apkResultsDir, self.config.getRuntimeReportsRelativeDir(),
            False, True)
        self._bboxStateMachine.transitToState(STATE_FOLDERS_CREATED)

        #coping initial apk file if required
        if copyApkToRes:
            shutil.copy2(pathToOrigApk, self.apkResultsDir)

        #decompiling apk into a folder
        decompileDir = os.path.join(self.apkTmpDir,
                                    self.config.getDecompiledApkRelativeDir())
        success = self._decompileApk(self.bboxInstrumenter, pathToOrigApk,
                                     decompileDir)
        if not success:
            return False
        self._bboxStateMachine.transitToState(STATE_APK_DECOMPILED)

        #getting all available dex files
        dexFilesRelativePaths = self._getDexFilePathsRelativeToDir(
            decompileDir)
        if not dexFilesRelativePaths:
            logger.error("There is no dex files to convert!")
            return False
        if "classes.dex" not in dexFilesRelativePaths:
            # "classes.dex" must be in the decompile root directory
            logger.error(
                "There is no classes.dex file found in the list of dex files")
            return False

        #converting dex to jar files
        rawJarFilesRootDir = os.path.join(self.apkTmpDir,
                                          self.config.getTmpJarRelativeDir())
        jarFilesRelativePaths = self._convertDex2JarFiles(
            converter=self.bboxInstrumenter,
            dexFilesRootDir=decompileDir,
            dexFilesRelativePaths=dexFilesRelativePaths,
            jarFilesRootDir=rawJarFilesRootDir,
            proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_DEX_CONVERTED_TO_JAR)

        if "classes.jar" not in jarFilesRelativePaths:
            #main file is not converted
            logger.error(
                "Conversion from classes.dex to classes.jar was not successful!"
            )
            return False

        #instrumenting available jar files
        self.coverageMetadataFile = os.path.join(
            self.coverageMetadataFolder,
            self.config.getCoverageMetadataFilename())
        emmaInstrJarFilesRootDir = os.path.join(
            self.apkTmpDir, self.config.getInstrumentedFilesRelativeDir())
        emmaInstrJarFileRelativePaths = self._instrFilesWithEmma(
            instrumenter=self.bboxInstrumenter,
            jarFilesRootDir=rawJarFilesRootDir,
            jarFilesRelativePaths=jarFilesRelativePaths,
            instrJarsRootDir=emmaInstrJarFilesRootDir,
            coverageMetadataFile=self.coverageMetadataFile,
            proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_JARS_INSTRUMENTED)

        if "classes.jar" not in emmaInstrJarFileRelativePaths:
            #main file is not instrumented
            logger.error("Instrumentation of classes.jar was not successful!")
            return False

        #converting jar files back to dex files
        instrDexFilesRelativePaths = self._convertJar2DexWithInstr(
            converter=self.bboxInstrumenter,
            instrJarsRootDir=emmaInstrJarFilesRootDir,
            instrJarFilesRelativePaths=emmaInstrJarFileRelativePaths,
            finalDexFilesRootDir=decompileDir,
            proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_JAR_CONVERTED_TO_DEX)

        if "classes.dex" not in instrDexFilesRelativePaths:
            logger.error(
                "There is no classes.dex file found in the list of dex files")
            return False

        #checking what files have not been converted
        uninstrumentedFiles = self._getUnInstrFilesRelativePaths(
            dexFilesRelativePaths, instrDexFilesRelativePaths)
        if uninstrumentedFiles:
            logger.debug("The following files were not instrumented: %s" +
                         str(uninstrumentedFiles))

        #instrument AndroidManifest.xml
        decompiledAndroidManifestPath = os.path.join(decompileDir,
                                                     "AndroidManifest.xml")
        success = self._instrAndroidManifest(self.bboxInstrumenter,
                                             decompiledAndroidManifestPath)
        if not success:
            logger.error("Cannot instrument AndroidManifest.xml file!")
            return False
        #coping instrumented AndroidManifest.xml to result folder
        shutil.copy2(decompiledAndroidManifestPath, self.apkResultsDir)
        self.androidManifestFile = os.path.join(self.apkResultsDir,
                                                "AndroidManifest.xml")
        self._bboxStateMachine.transitToState(STATE_MANIFEST_INSTRUMENTED)

        compiledApkFilePath = os.path.join(
            self.apkResultsDir,
            "%s%s.apk" % (apkFileName, self.config.getInstrFileSuffix()))
        success = self._compileApk(self.bboxInstrumenter, decompileDir,
                                   compiledApkFilePath)
        if not success:
            logger.error("Cannot build apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_BUILD)

        #need to copy resources into file
        compiledApkFilePathWithEmmaRes = os.path.join(
            self.apkResultsDir,
            "%s%s.apk" % (apkFileName, self.config.getFinalInstrFileSuffix()))
        shutil.copy2(compiledApkFilePath, compiledApkFilePathWithEmmaRes)
        self._putAdditionalResources(
            apk=compiledApkFilePathWithEmmaRes,
            resources=self.config.getEmmaResourcesDir())
        self._bboxStateMachine.transitToState(
            STATE_FINAL_INSTRUMENTED_APK_BUILD)

        signedApkFilePath = os.path.join(
            self.apkResultsDir,
            "%s%s.apk" % (apkFileName, self.config.getSignedFileSuffix()))
        success = self._signApk(self.bboxInstrumenter,
                                compiledApkFilePathWithEmmaRes,
                                signedApkFilePath)
        if not success:
            logger.error("Cannot sign apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_SIGNED)

        alignedApkFilePath = os.path.join(
            self.apkResultsDir,
            "%s%s.apk" % (apkFileName, self.config.getAlignedFileSuffix()))
        success = self._alignApk(self.bboxInstrumenter, signedApkFilePath,
                                 alignedApkFilePath)
        if not success:
            logger.error("Cannot align apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_ALIGNED)

        #cleaning: if tmp dir needs to be removed after instrumentation
        if removeApkTmpDirAfterInstr:
            shutil.rmtree(self.apkTmpDir)

        self.instrumentedApk = alignedApkFilePath
        self.androidManifest = AndroidManifest(self.androidManifestFile)
        self.packageName = self.androidManifest.getInstrumentationTargetPackage(
        )
        self.runnerName = self.androidManifest.getInstrumentationRunnerName()

        #Final node transition
        self._bboxStateMachine.transitToState(STATE_APK_INSTRUMENTED)
        return True
 def __init__(self, adbDevice, pathToAndroidManifest):
     self.adbDevice = adbDevice
     self.androidManifest=AndroidManifest(pathToAndroidManifest)
class IntentInvocationStrategy(RunningStrategy):
    def __init__(self, adbDevice, pathToAndroidManifest):
        self.adbDevice = adbDevice
        self.androidManifest=AndroidManifest(pathToAndroidManifest)
    
    
    def run(self, delay=10):
        print "Activities:"
        activities = self.androidManifest.getActivities()
        for activity in activities:
            self._invokeActivityExplicitely(activity)
            time.sleep(delay)
        
        print "Services:"    
        services = self.androidManifest.getServices()
        for service in services:
            self._invokeServiceExplicitely(service)
            time.sleep(delay)
         
        print "Receivers:"        
        receivers = self.androidManifest.getReceivers()
        for receiver in receivers:
            self._invokeReceiver(receiver)
            time.sleep(delay)
             
    
    def _invokeActivityExplicitely(self, activity):
        self.adbDevice.startActivityExplicitly(package_name=self.androidManifest.getPackageName(),
                                                   activity_name=activity)
    
    def _invokeActivity(self, activity):
        intentFilters = self.androidManifest.getActivityIntentFilters(activity)
        if not intentFilters:
            self.adbDevice.startActivityExplicitly(package_name=self.androidManifest.getPackageName(),
                                                   activity_name=activity)
            return
        for filt in intentFilters:
            action = self._getAction(filt)
            category = self._getCategory(filt)
            mimeType = self._getMiMeType(filt)
            self.adbDevice.startActivityImplicitely(action=action, mimeType=mimeType, category=category)
    
    def _invokeServiceExplicitely(self, service):
        self.adbDevice.startServiceExplicitly(package_name=self.androidManifest.getPackageName(),
                                                   service_name=service)
            
    def _invokeService(self, service):
        intentFilters = self.androidManifest.getServiceIntentFilters(service)
        if not intentFilters:
            self.adbDevice.startServiceExplicitly(package_name=self.androidManifest.getPackageName(),
                                                   service_name=service)
            return
        
        for filt in intentFilters:
            action = self._getAction(filt)
            category = self._getCategory(filt)
            mimeType = self._getMiMeType(filt)
            self.adbDevice.startServiceImplicitely(action=action, mimeType=mimeType, category=category)
    
    def _invokeReceiver(self, receiver):
        intentFilters = self.androidManifest.getReceiverIntentFilters(receiver)
        if not intentFilters:
            return
        
        for filt in intentFilters:
            action = self._getAction(filt)
            category = self._getCategory(filt)
            mimeType = self._getMiMeType(filt)
            self.adbDevice.sendBroadcast(action=action, mimeType=mimeType, category=category)
    
    
    def _getAction(self, filt):
        action = None
        actions = filt.get("action")
        if actions:
            action = actions[0]
        return action    
        
    def _getCategory(self, filt):
        category = None
        categories = filt.get("category")
        if categories:
            category = categories[0]
        else:
            category = CATEGORY_DEFAULT
        return category
    
    def _getMiMeType(self, filt):
        mimeType = None
        mimeTypes = filt.get("mimeType")
        if mimeTypes:
            mimeType = mimeTypes[0]
        return mimeType
Beispiel #12
0
class BBoxCoverage:
    PREFIX_ONSTOP = "onstop";
    PREFIX_ONERROR = "onerror";
    
    '''
    This class unites all separate classes needed for instrumentation and
    testing of the instrumented apk and provides a consistent workflow for a 
    developer.
    '''
#     def __init__(self, resultsRootDir=None, pathToBBoxConfigFile="./config/bbox_config.ini"):
#         '''
#         Constructor.
#         '''
#         #getting resultsRootDir
#         self.resultsRootDir = None
#         if not resultsRootDir:
#             self.resultsRootDir = os.path.join(os.getcwd(), )
#         else:
#             self.resultsRootDir = os.path.abspath(resultsRootDir)
#         
#         self.config = BBoxConfig(pathToBBoxConfigFile)
#         self._bboxStateMachine = StateMachine(states=STATES)
    
    def __init__(self, pathToBBoxConfigFile="./config/bbox_config.ini"):
        self.androidManifestFile = None
        self.instrumentedApk = None
        self.config = BBoxConfig(pathToBBoxConfigFile)
        self.bboxInstrumenter = BBoxInstrumenter(self.config)
        self.bboxExecutor = BBoxExecutor(self.config)
        self.bboxReporter = BBoxReporter(self.config)
        self._bboxStateMachine = StateMachine(states=STATES)
    
    def getInstrumentedApk(self):
        return self.instrumentedApk

    def getPackageName(self):
        return self.packageName
    
    def instrumentApkForCoverage(self, pathToOrigApk, resultsDir=None, tmpDir=None, 
                                 removeApkTmpDirAfterInstr=True, 
                                 copyApkToRes = True):
        '''
        Args:
            :param pathToOrigApk:
            :param resultsDir:
            :param tmpDir:
        
        Returns:
            :ret True - if instrumentation was successful, False otherwise
        '''
        self._bboxStateMachine.start(STATE_UNINITIALIZED)
        
        valid = self._checkProvidedApk(pathToOrigApk)
        if not valid:
            return False
        self._bboxStateMachine.transitToState(STATE_APK_VALID)
        
        resultsRootDir = None
        if not resultsDir:
            resultsRootDir = os.path.join(os.getcwd(), RESULTS_RELATIVE_DIR)
        else:
            resultsRootDir = os.path.abspath(resultsDir)
        
        tmpRootDir = None
        if not tmpDir:
            tmpRootDir = os.path.join(os.getcwd(), TMP_RELATIVE_DIR)
        else:
            tmpRootDir = os.path.abspath(tmpDir)
        
        apkFileName = os.path.splitext(os.path.basename(pathToOrigApk))[0]
        
        self.apkTmpDir = self._createDir(tmpDir, apkFileName, False, True)
        self.apkResultsDir = self._createDir(resultsRootDir, apkFileName, False, True)
        self.coverageMetadataFolder = self._createDir(self.apkResultsDir, self.config.getCoverageMetadataRelativeDir(), False, True)
        self.runtimeReportsRootDir = self._createDir(self.apkResultsDir, self.config.getRuntimeReportsRelativeDir(), False, True)
        self._bboxStateMachine.transitToState(STATE_FOLDERS_CREATED)
        
        #coping initial apk file if required
        if copyApkToRes:
            shutil.copy2(pathToOrigApk, self.apkResultsDir)
        
        #decompiling apk into a folder
        decompileDir = os.path.join(self.apkTmpDir, self.config.getDecompiledApkRelativeDir())
        success = self._decompileApk(self.bboxInstrumenter, pathToOrigApk, decompileDir)
        if not success:
            return False
        self._bboxStateMachine.transitToState(STATE_APK_DECOMPILED)
        
        #getting all available dex files
        dexFilesRelativePaths = self._getDexFilePathsRelativeToDir(decompileDir)
        if not dexFilesRelativePaths:
            logger.error("There is no dex files to convert!")
            return False
        if "classes.dex" not in dexFilesRelativePaths:
            # "classes.dex" must be in the decompile root directory
            logger.error("There is no classes.dex file found in the list of dex files")
            return False
        
        
        #converting dex to jar files
        rawJarFilesRootDir = os.path.join(self.apkTmpDir, self.config.getTmpJarRelativeDir())
        jarFilesRelativePaths = self._convertDex2JarFiles(
                                    converter=self.bboxInstrumenter, 
                                    dexFilesRootDir=decompileDir, 
                                    dexFilesRelativePaths=dexFilesRelativePaths,
                                    jarFilesRootDir=rawJarFilesRootDir,
                                    proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_DEX_CONVERTED_TO_JAR)
        
        if "classes.jar" not in jarFilesRelativePaths:
            #main file is not converted
            logger.error("Conversion from classes.dex to classes.jar was not successful!")
            return False
        
        #instrumenting available jar files
        self.coverageMetadataFile = os.path.join(self.coverageMetadataFolder, self.config.getCoverageMetadataFilename())
        emmaInstrJarFilesRootDir = os.path.join(self.apkTmpDir, self.config.getInstrumentedFilesRelativeDir())
        emmaInstrJarFileRelativePaths = self._instrFilesWithEmma(
                            instrumenter=self.bboxInstrumenter, 
                            jarFilesRootDir=rawJarFilesRootDir, 
                            jarFilesRelativePaths=jarFilesRelativePaths, 
                            instrJarsRootDir=emmaInstrJarFilesRootDir,
                            coverageMetadataFile=self.coverageMetadataFile,
                            proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_JARS_INSTRUMENTED)
        
        if "classes.jar" not in emmaInstrJarFileRelativePaths:
            #main file is not instrumented
            logger.error("Instrumentation of classes.jar was not successful!")
            return False
        
        #converting jar files back to dex files
        instrDexFilesRelativePaths = self._convertJar2DexWithInstr(
                    converter=self.bboxInstrumenter,
                    instrJarsRootDir=emmaInstrJarFilesRootDir, 
                    instrJarFilesRelativePaths=emmaInstrJarFileRelativePaths,
                    finalDexFilesRootDir=decompileDir,
                    proceedOnError=True)
        self._bboxStateMachine.transitToState(STATE_JAR_CONVERTED_TO_DEX)
        
        if "classes.dex" not in instrDexFilesRelativePaths:
            logger.error("There is no classes.dex file found in the list of dex files")
            return False
        
        #checking what files have not been converted
        uninstrumentedFiles = self._getUnInstrFilesRelativePaths(dexFilesRelativePaths, instrDexFilesRelativePaths)
        if uninstrumentedFiles:
            logger.debug("The following files were not instrumented: %s" + str(uninstrumentedFiles))
        
        #instrument AndroidManifest.xml
        decompiledAndroidManifestPath = os.path.join(decompileDir, "AndroidManifest.xml")
        success = self._instrAndroidManifest(self.bboxInstrumenter, decompiledAndroidManifestPath)
        if not success:
            logger.error("Cannot instrument AndroidManifest.xml file!")
            return False
        #coping instrumented AndroidManifest.xml to result folder
        shutil.copy2(decompiledAndroidManifestPath, self.apkResultsDir)
        self.androidManifestFile = os.path.join(self.apkResultsDir, "AndroidManifest.xml")
        self._bboxStateMachine.transitToState(STATE_MANIFEST_INSTRUMENTED)
        
        compiledApkFilePath = os.path.join(self.apkResultsDir, "%s%s.apk" % (apkFileName, self.config.getInstrFileSuffix()))
        success = self._compileApk(self.bboxInstrumenter, decompileDir, compiledApkFilePath)
        if not success:
            logger.error("Cannot build apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_BUILD)
        
        #need to copy resources into file
        compiledApkFilePathWithEmmaRes = os.path.join(self.apkResultsDir, "%s%s.apk" % (apkFileName, self.config.getFinalInstrFileSuffix()))
        shutil.copy2(compiledApkFilePath, compiledApkFilePathWithEmmaRes)
        self._putAdditionalResources(apk=compiledApkFilePathWithEmmaRes, resources=self.config.getEmmaResourcesDir())
        self._bboxStateMachine.transitToState(STATE_FINAL_INSTRUMENTED_APK_BUILD)
        
        signedApkFilePath = os.path.join(self.apkResultsDir, "%s%s.apk" % (apkFileName, self.config.getSignedFileSuffix()))
        success = self._signApk(self.bboxInstrumenter, compiledApkFilePathWithEmmaRes, signedApkFilePath)
        if not success:
            logger.error("Cannot sign apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_SIGNED)
        
        alignedApkFilePath = os.path.join(self.apkResultsDir, "%s%s.apk" % (apkFileName, self.config.getAlignedFileSuffix()))
        success = self._alignApk(self.bboxInstrumenter, signedApkFilePath, alignedApkFilePath)
        if not success:
            logger.error("Cannot align apk!")
            return False
        self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_ALIGNED)
        
        #cleaning: if tmp dir needs to be removed after instrumentation
        if removeApkTmpDirAfterInstr:
            shutil.rmtree(self.apkTmpDir)
        
        self.instrumentedApk = alignedApkFilePath
        self.androidManifest = AndroidManifest(self.androidManifestFile)
        self.packageName = self.androidManifest.getInstrumentationTargetPackage()
        self.runnerName = self.androidManifest.getInstrumentationRunnerName()
        
        #Final node transition
        self._bboxStateMachine.transitToState(STATE_APK_INSTRUMENTED)
        return True
        
    
    def _createDir(self, root, directory, createNew=True, overwrite=False):
        resDir = os.path.join(root, directory)
        if createNew:
            i = 0
            while os.path.exists(resDir):
                i += 1
                resDir = os.path.join(root, "%s_%d" % (directory, i))
        auxiliary_utils.mkdir(path=resDir, mode=0777, overwrite=overwrite)
                
#         resDir = os.path.join(root, directory)
#         if not overwrite:
#             i = 0
#             while os.path.exists(resDir):
#                 i += 1
#                 resDir = os.path.join(root, "%s_%d" % (directory, i))
#         
#         auxiliary_utils.mkdir(path=resDir, mode=0777, overwrite=overwrite)
        return resDir
    
    def _createFolder(self, root, dirName, overwrite=False):
        resultDir = os.path.join(root, dirName)
        auxiliary_utils.mkdir(path=resultDir, mode=0777, overwrite=overwrite)
        return resultDir
    
    
    def _checkProvidedApk(self, pathToApk):
        '''
        This methods validates the provided apk file.
        
        Raises:
            ApkIsNotValidException: if the provided apk file is not correct
        '''
        (valid, error) = apk_utils.checkInputApkFile(pathToApk)
        if not valid:
            logger.error("Path %s points to the invalid apk file! ERROR:%s" % (pathToApk, error))
            return valid
        
        return valid

    
    def _decompileApk(self, decompiler, apk, outputDir):
        try:
            decompiler.decompileApk(apk, outputDir)
        except ApkCannotBeDecompiledException as e:
            logger.error(e.msg)
            return False
        except:
            logger.error("Unknown error during decompile process! Exit")
            return False
        
        return True
    
    
    def _convertDex2JarFiles(self, converter, dexFilesRootDir, dexFilesRelativePaths, jarFilesRootDir, proceedOnError=True):
        jarFilesRelativePaths = []
        for dexFileRelativePath in dexFilesRelativePaths:
            dexFilePath = os.path.join(dexFilesRootDir, dexFileRelativePath)
            jarFileRelativePath = os.path.splitext(dexFileRelativePath)[0] + ".jar"
            jarFilePath = os.path.join(jarFilesRootDir, jarFileRelativePath)
            try:
                converter.convertDex2Jar(dexFilePath, jarFilePath, overwrite=True)
            except Dex2JarConvertionError as e:
                if proceedOnError:
                    logger.warning("Cannot convert [%s] to [%s]. %s" (dexFilePath, jarFilePath, e.msg))
                    continue
                else:
                    raise
            jarFilesRelativePaths.append(jarFileRelativePath) 
            
        return jarFilesRelativePaths
        
    
    def _instrFilesWithEmma(self, instrumenter, jarFilesRootDir, jarFilesRelativePaths, 
            instrJarsRootDir, coverageMetadataFile, proceedOnError=True):
        
        instrJarFilesRelativePaths = []
        for jarFileRelativePath in jarFilesRelativePaths:
            jarFileAbsPath = os.path.join(jarFilesRootDir, jarFileRelativePath)
            instrJarRelativeDir = jarFileRelativePath[:jarFileRelativePath.rfind("/")+1]
            instrJarFullDir = os.path.join(instrJarsRootDir, instrJarRelativeDir)
            
            try:
                instrumenter.instrumentJarWithEmma(jarFile=jarFileAbsPath, outputFolder=instrJarFullDir, emmaMetadataFile=coverageMetadataFile)
            except EmmaCannotInstrumentException as e:
                if proceedOnError:
                    logger.warning("Cannot instrument [%s]. %s" (jarFileAbsPath, e.msg))
                    continue
                else:
                    raise
            instrJarFilesRelativePaths.append(jarFileRelativePath)
        
        return instrJarFilesRelativePaths
    
    
    def _convertJar2DexWithInstr(self, converter, instrJarsRootDir, 
                                 instrJarFilesRelativePaths, 
                                 finalDexFilesRootDir, proceedOnError):
        instrDexFilesRelativePaths = []
        for jarFileRelativePath in instrJarFilesRelativePaths:
            jarFileAbsPath = os.path.join(instrJarsRootDir, jarFileRelativePath)
            dexFileRelativePath = os.path.splitext(jarFileRelativePath)[0] + ".dex"
            dexFileAbsPath = os.path.join(finalDexFilesRootDir, dexFileRelativePath)
            
            #we instrument main file with auxiliary classes
            print "jarFileRelativePath: " + jarFileRelativePath
            
            try:
                withFiles = []
                #we compile main file with additional instrumentation files
                #hack: emma copies instrumented jar files into lib folder
                if jarFileRelativePath == "classes.jar":
                    emmaDevicePath = os.path.join(self.config.getEmmaDir(), self.config.getEmmaDeviceJar())
                    withFiles.append(self.config.getAndroidSpecificInstrumentationClassesPath())
                    withFiles.append(emmaDevicePath)
                
                converter.convertJar2Dex(jarFile=jarFileAbsPath, 
                                         dexFile=dexFileAbsPath, 
                                         withFiles=withFiles, 
                                         overwrite=True)
            except Jar2DexConvertionError as e:
                if proceedOnError:
                    logger.warning("Cannot instrument [%s]. %s" % (jarFileAbsPath, e.msg))
                    continue
                else:
                    raise
            instrDexFilesRelativePaths.append(dexFileRelativePath)
        
        return instrDexFilesRelativePaths
    
    
    def _getUnInstrFilesRelativePaths(self, dexFilesRelativePaths, instrDexFilesRelativePaths):
        uninstrumentedFiles = []
        for dexFileRelativePath in dexFilesRelativePaths:
            if dexFileRelativePath not in instrDexFilesRelativePaths:
                uninstrumentedFiles.append(dexFileRelativePath)
        return uninstrumentedFiles
    
    
    def _instrAndroidManifest(self, instrumenter, initAndroidManifest, instrAndroidManifest=None, addSdCardPermission=True):
        success = True
        try:
            instrumenter.instrumentAndroidManifestFile(initAndroidManifest, instrAndroidManifest, addSdCardPermission)
        except IllegalArgumentException as e:
            logger.error("Cannot instrument AndroidManifest file. %s" % e.msg)
            success = False
        except:
            logger.error("Cannot instrument AndroidManifest file!")
            success = False
        return success
    
    
    def _compileApk(self, compiler, fromDir, apkPath):
        success = True
        try:
            compiler.buildApk(fromDir, apkPath)
        except ApktoolBuildException as e:
            logger.error("Cannot build apk! %s" % e.msg)
            success = False
        except:
            logger.error("Cannot build apk!")
            success = False
        return success
    
    def _putAdditionalResources(self, apk, resources):
        zip_utils.zipdir(resources, apk)
    
    def _signApk(self, signer, unsignedApkFile, signedApkFile):
        success = True
        try:
            signer.signApk(unsignedApkFile, signedApkFile)
        except SignApkException as e:
            logger.error("Cannot sign apk! %s" % e.msg)
            success = False
        except:
            logger.error("Cannot sign apk!")
            success = False
        return success
            
    
    
    def _alignApk(self, aligner, unalignedApkFile, alignedApkFile):
        success = True
        try:
            aligner.alignApk(unalignedApkFile, alignedApkFile)
        except AlignApkException as e:
            logger.error("Cannot align apk! %s" % e.msg)
            success = False
        except:
            logger.error("Cannot align apk!")
            success = False
        return success
            
    
#     def _getMainDexFile(self, directory):
#         mainDexFile = os.path.join(directory, "classes.dex")
#         if not os.path.exists(mainDexFile):
#             logger.error("Cannot find classes.dex file!")
#             return (False, None)
#         
#         return (True, mainDexFile)
        
    def _getDexFiles(self, directory):
        dexFileNames = auxiliary_utils.searchFiles(where=directory, extension="dex")
        return dexFileNames
    
    def _getDexFilePathsRelativeToDir(self, target):
        dexFileRelativePaths = auxiliary_utils.searchFilesRelativeToDir(target=target, extension="dex")
        return dexFileRelativePaths
    
    
################################################################################
    
    def initAlreadyInstrApkEnv(self, pathToInstrApk, resultsDir, pathToInstrManifestFile=None):
        if not apk_utils.checkInputApkFile(pathToInstrApk):
            logger.error("Provided file [%s] is not a valid apk file!" % pathToInstrApk)
            return
        
        if not os.path.isdir(resultsDir):
            logger.error("Provided path to results dir [%s] do not point to dir!" % resultsDir)
            return
        
        coverageMetadataFolderPath = os.path.join(resultsDir, self.config.getCoverageMetadataRelativeDir())
        if not os.path.isdir(coverageMetadataFolderPath):
            logger.error("In the results dir [%s] there is no folder with coverage metadata!" % resultsDir)
            return
        self.coverageMetadataFolder = coverageMetadataFolderPath
        
        if self.config.getCoverageMetadataFilename() not in os.listdir(coverageMetadataFolderPath):
            logger.error("Cannot find metadata filename in the coverage metadata folder: %s!" % self.coverageMetadataFolder)
            return 
        
        self.coverageMetadataFile = os.path.join(self.coverageMetadataFolder, self.config.getCoverageMetadataFilename())
        
        #by default trying to look for a file in the 
        if pathToInstrManifestFile:
            androidManifestPath = pathToInstrManifestFile
        else:
            androidManifestPath = os.path.join(resultsDir, "AndroidManifest.xml")
        
        if not os.path.isfile(androidManifestPath):
            logger.warning("Path [%s] is not pointing to a real file! Leaving pointer to AndroidManifest.xml empty!" % androidManifestPath)
            return
        
        self.instrumentedApk = pathToInstrApk
        self.apkResultsDir = resultsDir
        self.runtimeReportsRootDir = self._createDir(resultsDir, self.config.getRuntimeReportsRelativeDir(), False, False)
        self.androidManifestFile = androidManifestPath
        
        self.androidManifest = AndroidManifest(self.androidManifestFile)
        self.packageName = self.androidManifest.getInstrumentationTargetPackage()
        self.runnerName = self.androidManifest.getInstrumentationRunnerName()
        
        self._bboxStateMachine.start(STATE_VALID_SETTINGS_PROVIDED)
        
    
    def installApkOnDevice(self):
        if not self._bboxStateMachine.isTransitionPossible(STATE_APK_INSTALLED):
            logger.error("Cannot install apk on device because the environment is not initialized!")
            return
        
        #selecting device for execution
        self.bboxExecutor.selectExecutionDevice()
        try:
            self.bboxExecutor.installApkOnDevice(self.instrumentedApk)
        except ApkCannotBeInstalledException as e:
            logger.error("Cannot install instrumented apk. %s" % e.msg)
            return
        self._bboxStateMachine.transitToState(STATE_APK_INSTALLED)
        
        
    
    def startTesting(self):
        if not self._bboxStateMachine.isTransitionPossible(STATE_APK_TEST_STARTED):
            logger.error("Cannot start testing apk on a device!")
            return
        
        self.deviceReportFolder = os.path.join(DEVICE_REPORT_FOLDER_PATH, self.packageName)
        self.bboxExecutor.selectExecutionDevice()
        self.bboxExecutor.startOndeviceTesting(packageName=self.packageName,
                                               runnerName=self.runnerName, 
                                               coverage=True,
                                               reportFolder=self.deviceReportFolder,
                                               proceedOnError=True,
                                               generateCoverageReportOnError=True)
        self._bboxStateMachine.transitToState(STATE_APK_TEST_STARTED)
        
        
    def stopTesting(self, localReportFolderName=None, paramsToWrite=None):
        if not self._bboxStateMachine.isTransitionPossible(STATE_APK_FINISHED_TESTING):
            logger.error("Cannot stop testing because it is not started!")
            return
        
#         currentDateTime = datetime.datetime.now()
#         reportTimePrefix = currentDateTime.strftime("%Y_%m_%d__%H_%M_%S")
#         coverageReportName = "%s___%s" % (reportTimePrefix, "coverage.ec")
#         reportFileOnDevice = "%s/%s" % (DEVICE_REPORT_FOLDER_PATH, coverageReportName)
        
#         reportLocally = os.path.join(self.runtimeReportsRootDir, coverageReportName)
        if not localReportFolderName:
            localReportFolderName="test"
        
        localReportFolder = self._createDir(self.runtimeReportsRootDir, localReportFolderName, True, False)
        
        self.bboxExecutor.stopOndeviceTesting(cancelAnalysis=False)
        
        time.sleep(3) #waiting for several seconds for report to be generated
        
#         success = self.bboxExecutor.getFileFromDevice(reportFileOnDevice, reportLocally)
        success = self.bboxExecutor.getFileFromDevice(self.deviceReportFolder, localReportFolder)

        if not success:
            self.bboxExecutor.removeFile(self.deviceReportFolder)
            return None
        
        if paramsToWrite:
            params_config = ConfigParser.ConfigParser()
            params_config.add_section(PARAMS_SECTION)
            for param in iteritems(paramsToWrite):
                params_config.set(PARAMS_SECTION, param[0], param[1])
            with open(os.path.join(localReportFolder, "parameters.txt"), "w") as param_file:
                params_config.write(param_file)
                
        self.bboxExecutor.removeFile(self.deviceReportFolder)
        self._bboxStateMachine.transitToState(STATE_APK_FINISHED_TESTING)
        return localReportFolder
    
    
    def generateReport(self, reportFiles=[], reportName=None, reportType=EMMA_REPORT.XML):
        if not reportFiles:
            logger.error("No report files are provided!")
            return
        
        self.bboxReporter.cleanMetaFiles()
        self.bboxReporter.cleanReportFiles()
       
        self.bboxReporter.addMetaFile(self.coverageMetadataFile)
        for rFile in reportFiles:
            self.bboxReporter.addReportFile(rFile)
        
        reportsRoot = os.path.join(self.apkResultsDir, self.config.getReportsRelativeDir())
        where = self._createReportResultsDir(reportsRoot, "report_%s" % reportType) 
        self.bboxReporter.generateEmmaReport(where, reportName, reportType)
    
    
    def _createReportResultsDir(self, reportsRoot, reportDirName):
        i = 0
        resultsDir = os.path.join(reportsRoot, reportDirName)
        while os.path.exists(resultsDir):
            i += 1
            resultsDir = os.path.join(reportsRoot, "%s_%d" % (reportDirName, i))
        
        auxiliary_utils.mkdir(path=resultsDir, mode=0777, overwrite=False)
        return resultsDir   

    def uninstallPackage(self):
        self.bboxExecutor.uninstallPackage(packageName=self.packageName, keepData=False)
        self._bboxStateMachine.stop()
    
    @staticmethod    
    def getCoverageReportsFromFolderWithPrefix(folder, prefix):
        if not os.path.exists(folder):
            return None
        
        reports = []
        for file in os.listdir(folder):
            if file.endswith(".ec") and file.startswith(prefix):
                reports.append(os.path.join(folder, file))
        
        return  reports
Beispiel #13
0
 def instrumentApkForCoverage(self, pathToOrigApk, resultsDir=None, tmpDir=None, 
                              removeApkTmpDirAfterInstr=True, 
                              copyApkToRes = True):
     '''
     Args:
         :param pathToOrigApk:
         :param resultsDir:
         :param tmpDir:
     
     Returns:
         :ret True - if instrumentation was successful, False otherwise
     '''
     self._bboxStateMachine.start(STATE_UNINITIALIZED)
     
     valid = self._checkProvidedApk(pathToOrigApk)
     if not valid:
         return False
     self._bboxStateMachine.transitToState(STATE_APK_VALID)
     
     resultsRootDir = None
     if not resultsDir:
         resultsRootDir = os.path.join(os.getcwd(), RESULTS_RELATIVE_DIR)
     else:
         resultsRootDir = os.path.abspath(resultsDir)
     
     tmpRootDir = None
     if not tmpDir:
         tmpRootDir = os.path.join(os.getcwd(), TMP_RELATIVE_DIR)
     else:
         tmpRootDir = os.path.abspath(tmpDir)
     
     apkFileName = os.path.splitext(os.path.basename(pathToOrigApk))[0]
     
     self.apkTmpDir = self._createDir(tmpDir, apkFileName, False, True)
     self.apkResultsDir = self._createDir(resultsRootDir, apkFileName, False, True)
     self.coverageMetadataFolder = self._createDir(self.apkResultsDir, self.config.getCoverageMetadataRelativeDir(), False, True)
     self.runtimeReportsRootDir = self._createDir(self.apkResultsDir, self.config.getRuntimeReportsRelativeDir(), False, True)
     self._bboxStateMachine.transitToState(STATE_FOLDERS_CREATED)
     
     #coping initial apk file if required
     if copyApkToRes:
         shutil.copy2(pathToOrigApk, self.apkResultsDir)
     
     #decompiling apk into a folder
     decompileDir = os.path.join(self.apkTmpDir, self.config.getDecompiledApkRelativeDir())
     success = self._decompileApk(self.bboxInstrumenter, pathToOrigApk, decompileDir)
     if not success:
         return False
     self._bboxStateMachine.transitToState(STATE_APK_DECOMPILED)
     
     #getting all available dex files
     dexFilesRelativePaths = self._getDexFilePathsRelativeToDir(decompileDir)
     if not dexFilesRelativePaths:
         logger.error("There is no dex files to convert!")
         return False
     if "classes.dex" not in dexFilesRelativePaths:
         # "classes.dex" must be in the decompile root directory
         logger.error("There is no classes.dex file found in the list of dex files")
         return False
     
     
     #converting dex to jar files
     rawJarFilesRootDir = os.path.join(self.apkTmpDir, self.config.getTmpJarRelativeDir())
     jarFilesRelativePaths = self._convertDex2JarFiles(
                                 converter=self.bboxInstrumenter, 
                                 dexFilesRootDir=decompileDir, 
                                 dexFilesRelativePaths=dexFilesRelativePaths,
                                 jarFilesRootDir=rawJarFilesRootDir,
                                 proceedOnError=True)
     self._bboxStateMachine.transitToState(STATE_DEX_CONVERTED_TO_JAR)
     
     if "classes.jar" not in jarFilesRelativePaths:
         #main file is not converted
         logger.error("Conversion from classes.dex to classes.jar was not successful!")
         return False
     
     #instrumenting available jar files
     self.coverageMetadataFile = os.path.join(self.coverageMetadataFolder, self.config.getCoverageMetadataFilename())
     emmaInstrJarFilesRootDir = os.path.join(self.apkTmpDir, self.config.getInstrumentedFilesRelativeDir())
     emmaInstrJarFileRelativePaths = self._instrFilesWithEmma(
                         instrumenter=self.bboxInstrumenter, 
                         jarFilesRootDir=rawJarFilesRootDir, 
                         jarFilesRelativePaths=jarFilesRelativePaths, 
                         instrJarsRootDir=emmaInstrJarFilesRootDir,
                         coverageMetadataFile=self.coverageMetadataFile,
                         proceedOnError=True)
     self._bboxStateMachine.transitToState(STATE_JARS_INSTRUMENTED)
     
     if "classes.jar" not in emmaInstrJarFileRelativePaths:
         #main file is not instrumented
         logger.error("Instrumentation of classes.jar was not successful!")
         return False
     
     #converting jar files back to dex files
     instrDexFilesRelativePaths = self._convertJar2DexWithInstr(
                 converter=self.bboxInstrumenter,
                 instrJarsRootDir=emmaInstrJarFilesRootDir, 
                 instrJarFilesRelativePaths=emmaInstrJarFileRelativePaths,
                 finalDexFilesRootDir=decompileDir,
                 proceedOnError=True)
     self._bboxStateMachine.transitToState(STATE_JAR_CONVERTED_TO_DEX)
     
     if "classes.dex" not in instrDexFilesRelativePaths:
         logger.error("There is no classes.dex file found in the list of dex files")
         return False
     
     #checking what files have not been converted
     uninstrumentedFiles = self._getUnInstrFilesRelativePaths(dexFilesRelativePaths, instrDexFilesRelativePaths)
     if uninstrumentedFiles:
         logger.debug("The following files were not instrumented: %s" + str(uninstrumentedFiles))
     
     #instrument AndroidManifest.xml
     decompiledAndroidManifestPath = os.path.join(decompileDir, "AndroidManifest.xml")
     success = self._instrAndroidManifest(self.bboxInstrumenter, decompiledAndroidManifestPath)
     if not success:
         logger.error("Cannot instrument AndroidManifest.xml file!")
         return False
     #coping instrumented AndroidManifest.xml to result folder
     shutil.copy2(decompiledAndroidManifestPath, self.apkResultsDir)
     self.androidManifestFile = os.path.join(self.apkResultsDir, "AndroidManifest.xml")
     self._bboxStateMachine.transitToState(STATE_MANIFEST_INSTRUMENTED)
     
     compiledApkFilePath = os.path.join(self.apkResultsDir, "%s%s.apk" % (apkFileName, self.config.getInstrFileSuffix()))
     success = self._compileApk(self.bboxInstrumenter, decompileDir, compiledApkFilePath)
     if not success:
         logger.error("Cannot build apk!")
         return False
     self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_BUILD)
     
     #need to copy resources into file
     compiledApkFilePathWithEmmaRes = os.path.join(self.apkResultsDir, "%s%s.apk" % (apkFileName, self.config.getFinalInstrFileSuffix()))
     shutil.copy2(compiledApkFilePath, compiledApkFilePathWithEmmaRes)
     self._putAdditionalResources(apk=compiledApkFilePathWithEmmaRes, resources=self.config.getEmmaResourcesDir())
     self._bboxStateMachine.transitToState(STATE_FINAL_INSTRUMENTED_APK_BUILD)
     
     signedApkFilePath = os.path.join(self.apkResultsDir, "%s%s.apk" % (apkFileName, self.config.getSignedFileSuffix()))
     success = self._signApk(self.bboxInstrumenter, compiledApkFilePathWithEmmaRes, signedApkFilePath)
     if not success:
         logger.error("Cannot sign apk!")
         return False
     self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_SIGNED)
     
     alignedApkFilePath = os.path.join(self.apkResultsDir, "%s%s.apk" % (apkFileName, self.config.getAlignedFileSuffix()))
     success = self._alignApk(self.bboxInstrumenter, signedApkFilePath, alignedApkFilePath)
     if not success:
         logger.error("Cannot align apk!")
         return False
     self._bboxStateMachine.transitToState(STATE_INSTRUMENTED_APK_ALIGNED)
     
     #cleaning: if tmp dir needs to be removed after instrumentation
     if removeApkTmpDirAfterInstr:
         shutil.rmtree(self.apkTmpDir)
     
     self.instrumentedApk = alignedApkFilePath
     self.androidManifest = AndroidManifest(self.androidManifestFile)
     self.packageName = self.androidManifest.getInstrumentationTargetPackage()
     self.runnerName = self.androidManifest.getInstrumentationRunnerName()
     
     #Final node transition
     self._bboxStateMachine.transitToState(STATE_APK_INSTRUMENTED)
     return True